This commit is contained in:
Austin Huang 2020-09-14 16:44:34 -04:00
commit 98420d0197
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
404 changed files with 20934 additions and 11655 deletions

View File

@ -45,7 +45,7 @@
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
</Profilers>
<option name="DEEP_LINK" value="" />
<option name="ACTIVITY_CLASS" value="awais.instagrabber.activities.Main" />
<option name="ACTIVITY_CLASS" value="awais.instagrabber.activities.MainActivity" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>

View File

@ -5,13 +5,13 @@ android {
compileSdkVersion 29
defaultConfig {
applicationId 'me.austinhuang.instagrabber'
applicationId 'me.austinhuang.instagrabbr'
minSdkVersion 16
targetSdkVersion 29
versionCode 49
versionName '18.3'
versionCode 50
versionName '19.0-a1'
multiDexEnabled true
@ -37,19 +37,30 @@ android {
}
dependencies {
implementation('androidx.appcompat:appcompat:1.3.0-alpha01@aar') { transitive true }
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation('com.google.android.material:material:1.3.0-alpha01@aar') { transitive true }
implementation('androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-rc01') { transitive true }
def appcompat_version = "1.2.0"
def nav_version = "2.3.0"
def preference_version = "1.1.1"
implementation 'com.google.android.material:material:1.2.1'
implementation 'com.google.android.exoplayer:exoplayer:2.11.1'
implementation "androidx.appcompat:appcompat:$appcompat_version"
implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
implementation "androidx.recyclerview:recyclerview:1.2.0-alpha05"
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation "androidx.viewpager2:viewpager2:1.0.0"
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"
implementation "androidx.constraintlayout:constraintlayout:2.0.1"
implementation "androidx.preference:preference:$preference_version"
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
implementation('org.jsoup:jsoup:1.13.1') { transitive true }
implementation('com.github.bumptech.glide:glide:4.11.0') { transitive true }
implementation('com.github.chrisbanes:PhotoView:v2.0.0@aar') { transitive true }
implementation('com.google.android.exoplayer:exoplayer:2.11.1@aar') { transitive true }
implementation 'org.jsoup:jsoup:1.13.1'
implementation 'com.facebook.fresco:fresco:2.3.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4'
}

View File

@ -8,18 +8,17 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:name=".InstaApp"
android:name=".InstaGrabberApplication"
android:allowBackup="true"
android:fullBackupContent="@xml/backup_descriptor"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:theme="@style/AppTheme.Light.White"
tools:ignore="UnusedAttribute">
<activity
android:name=".activities.Main"
android:name=".activities.MainActivity"
android:launchMode="singleTop"
android:taskAffinity=".Main"
android:windowSoftInputMode="adjustResize">
@ -29,18 +28,15 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SEARCH" />
<action android:name="android.intent.action.WEB_SEARCH" />
<!--<action android:name="android.intent.action.SEARCH" />-->
<!--<action android:name="android.intent.action.WEB_SEARCH" />-->
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -49,18 +45,15 @@
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="ig.me" />
<data android:host="www.ig.me" />
<data android:host="instagram.com" />
<data android:host="www.instagram.com" />
<data android:pathPrefix="/" />
<data android:pathPrefix="/p" />
<data android:pathPrefix="/explore/tags" />
</intent-filter>
</activity>
<activity
android:name="awaisomereport.ErrorReporterActivity"
android:allowEmbedded="false"
@ -77,7 +70,6 @@
android:screenOrientation="portrait"
android:taskAffinity="awais.instagrabber.errorreport"
android:theme="@android:style/Theme.DeviceDefault.Dialog" />
<activity
android:name=".directdownload.MultiDirectDialog"
android:allowEmbedded="false"
@ -93,7 +85,6 @@
android:noHistory="false"
android:taskAffinity="awais.instagrabber.multidialog"
android:theme="@style/FlyingGayDialog" />
<activity
android:name=".directdownload.DirectDownload"
android:allowEmbedded="false"
@ -110,7 +101,6 @@
android:lockTaskMode="never"
android:noHistory="false"
android:theme="@style/CompletelyTransparent">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SEARCH" />
@ -121,7 +111,6 @@
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
@ -131,96 +120,13 @@
<data android:scheme="https" />
</intent-filter>
</activity>
<activity
android:name=".activities.PostViewer"
android:parentActivityName=".activities.Main">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.Main" />
</activity>
<activity
android:name=".activities.CommentsViewer"
android:parentActivityName=".activities.PostViewer">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.PostViewer" />
</activity>
<activity
android:name=".activities.StoryViewer"
android:parentActivityName=".activities.Main">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.Main" />
</activity>
<activity
android:name=".activities.FollowViewer"
android:parentActivityName=".activities.Main">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.Main" />
</activity>
<activity
android:name=".activities.ProfilePicViewer"
android:parentActivityName=".activities.Main">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.Main" />
</activity>
<activity
android:name=".activities.ProfileViewer"
android:parentActivityName=".activities.PostViewer">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.PostViewer" />
</activity>
<activity
android:name=".activities.SavedViewer"
android:parentActivityName=".activities.Main">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.Main" />
</activity>
<activity
android:name=".activities.NotificationsViewer"
android:parentActivityName=".activities.Main">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.Main" />
</activity>
<activity
android:name=".activities.Login"
android:label="@string/login"
android:parentActivityName=".activities.Main">
android:parentActivityName=".activities.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.Main" />
</activity>
<activity
android:name=".activities.DirectMessagesActivity"
android:parentActivityName=".activities.Main">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.Main" />
android:value=".activities.MainActivity" />
</activity>
<provider
@ -232,5 +138,6 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<service android:name=".services.ActivityCheckerService" />
</application>
</manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 128 KiB

View File

@ -4,9 +4,11 @@ import android.content.ClipboardManager;
import android.content.Context;
import android.util.Log;
import androidx.core.app.NotificationManagerCompat;
import androidx.multidex.MultiDexApplication;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import java.net.CookieHandler;
import java.text.SimpleDateFormat;
import java.util.UUID;
@ -18,28 +20,33 @@ import awais.instagrabber.utils.SettingsHelper;
import awaisomereport.CrashReporter;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.NET_COOKIE_MANAGER;
import static awais.instagrabber.utils.CookieUtils.NET_COOKIE_MANAGER;
import static awais.instagrabber.utils.Utils.clipboardManager;
import static awais.instagrabber.utils.Utils.dataBox;
import static awais.instagrabber.utils.Utils.datetimeParser;
import static awais.instagrabber.utils.Utils.getInstalledTelegramPackage;
import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.notificationManager;
import static awais.instagrabber.utils.Utils.settingsHelper;
import static awais.instagrabber.utils.Utils.telegramPackage;
public final class InstaApp extends MultiDexApplication {
public final class InstaGrabberApplication extends MultiDexApplication {
private static final String TAG = "InstaGrabberApplication";
@Override
public void onCreate() {
super.onCreate();
final ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig
.newBuilder(this)
// .setMainDiskCacheConfig(diskCacheConfig)
.setDownsampleEnabled(true)
.build();
Fresco.initialize(this, imagePipelineConfig);
if (BuildConfig.DEBUG) {
try {
Class.forName("dalvik.system.CloseGuard")
.getMethod("setEnabled", boolean.class)
.invoke(null, true);
.getMethod("setEnabled", boolean.class)
.invoke(null, true);
} catch (Exception e) {
Log.e("InstaApp", "Error", e);
Log.e(TAG, "Error", e);
}
}
@ -50,8 +57,6 @@ public final class InstaApp extends MultiDexApplication {
final Context appContext = getApplicationContext();
telegramPackage = getInstalledTelegramPackage(appContext);
if (dataBox == null)
dataBox = DataBox.getInstance(appContext);
@ -60,17 +65,14 @@ public final class InstaApp extends MultiDexApplication {
LocaleUtils.setLocale(getBaseContext());
if (notificationManager == null)
notificationManager = NotificationManagerCompat.from(appContext);
if (clipboardManager == null)
clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
if (datetimeParser == null)
datetimeParser = new SimpleDateFormat(
settingsHelper.getBoolean(Constants.CUSTOM_DATE_TIME_FORMAT_ENABLED) ?
settingsHelper.getString(Constants.CUSTOM_DATE_TIME_FORMAT) :
settingsHelper.getString(Constants.DATE_TIME_FORMAT), LocaleUtils.getCurrentLocale());
settingsHelper.getString(Constants.CUSTOM_DATE_TIME_FORMAT) :
settingsHelper.getString(Constants.DATE_TIME_FORMAT), LocaleUtils.getCurrentLocale());
settingsHelper.putString(Constants.DEVICE_UUID, UUID.randomUUID().toString());
}

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.ThemeUtils;
public abstract class BaseLanguageActivity extends AppCompatActivity {
protected BaseLanguageActivity() {
@ -15,7 +15,7 @@ public abstract class BaseLanguageActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
Utils.changeTheme(this);
ThemeUtils.changeTheme(this);
super.onCreate(savedInstanceState);
}
}

View File

@ -1,302 +0,0 @@
package awais.instagrabber.activities;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.RelativeSizeSpan;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import awais.instagrabber.R;
import awais.instagrabber.adapters.CommentsAdapter;
import awais.instagrabber.asyncs.CommentsFetcher;
import awais.instagrabber.databinding.ActivityCommentsBinding;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
public final class CommentsViewer extends BaseLanguageActivity implements SwipeRefreshLayout.OnRefreshListener {
private CommentsAdapter commentsAdapter;
private CommentModel commentModel;
private ActivityCommentsBinding commentsBinding;
private ArrayAdapter<String> commmentDialogAdapter;
private String shortCode, postId, userId;
private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
private Resources resources;
private InputMethodManager imm;
private View focus;
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
commentsBinding = ActivityCommentsBinding.inflate(getLayoutInflater());
setContentView(commentsBinding.getRoot());
commentsBinding.swipeRefreshLayout.setOnRefreshListener(this);
final Intent intent = getIntent();
if (intent == null || !intent.hasExtra(Constants.EXTRAS_SHORTCODE)
|| Utils.isEmpty((shortCode = intent.getStringExtra(Constants.EXTRAS_SHORTCODE)))
|| !intent.hasExtra(Constants.EXTRAS_POST)
|| Utils.isEmpty((postId = intent.getStringExtra(Constants.EXTRAS_POST)))
|| !intent.hasExtra(Constants.EXTRAS_USER)
|| Utils.isEmpty((userId = intent.getStringExtra(Constants.EXTRAS_USER)))) {
Utils.errorFinish(this);
return;
}
commentsBinding.swipeRefreshLayout.setRefreshing(true);
setSupportActionBar(commentsBinding.toolbar.toolbar);
commentsBinding.toolbar.toolbar.setTitle(R.string.title_comments);
commentsBinding.toolbar.toolbar.setSubtitle(shortCode);
resources = getResources();
if (!Utils.isEmpty(cookie)) {
commentsBinding.commentText.setVisibility(View.VISIBLE);
commentsBinding.commentSend.setVisibility(View.VISIBLE);
commentsBinding.commentSend.setOnClickListener(newCommentListener);
commentsBinding.commentCancelParent.setOnClickListener(newCommentListener);
}
new CommentsFetcher(shortCode, new FetchListener<CommentModel[]>() {
@Override
public void onResult(final CommentModel[] commentModels) {
commentsAdapter = new CommentsAdapter(commentModels, true, clickListener, mentionClickListener);
commentsBinding.rvComments.setAdapter(commentsAdapter);
commentsBinding.swipeRefreshLayout.setRefreshing(false);
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@Override
public void onRefresh() {
commentsBinding.swipeRefreshLayout.setRefreshing(true);
new CommentsFetcher(shortCode, new FetchListener<CommentModel[]>() {
@Override
public void onResult(final CommentModel[] commentModels) {
commentsBinding.swipeRefreshLayout.setRefreshing(false);
commentsAdapter = new CommentsAdapter(commentModels, true, clickListener, mentionClickListener);
commentsBinding.rvComments.setAdapter(commentsAdapter);
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
final ProfileModel profileModel = commentModel.getProfileModel();
if (which == 0) {
searchUsername(profileModel.getUsername());
} else if (which == 1) {
startActivity(new Intent(this, ProfilePicViewer.class).putExtra(Constants.EXTRAS_PROFILE, profileModel));
} else if (which == 2) {
Utils.copyText(this, profileModel.getUsername());
} else if (which == 3) {
Utils.copyText(this, commentModel.getText().toString());
} else if (which == 4) {
if (commentModel == null) {
Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
else {
focus = commentsBinding.rvComments.findViewWithTag(commentModel);
focus.setBackgroundColor(0x80888888);
commentsBinding.commentCancelParent.setVisibility(View.VISIBLE);
String mention = "@" + profileModel.getUsername() + " ";
commentsBinding.commentText.setText(mention);
commentsBinding.commentText.requestFocus();
commentsBinding.commentText.setSelection(mention.length());
commentsBinding.commentText.postDelayed(new Runnable() {
@Override
public void run() {
imm = (InputMethodManager) getSystemService(getApplicationContext().INPUT_METHOD_SERVICE);
imm.showSoftInput(commentsBinding.commentText, 0);
}
}, 200);
}
} else if (which == 5) {
new CommentAction().execute((commentModel.getLiked() ? "unlike/" : "like/")+commentModel.getId());
} else if (which == 6) {
new CommentAction().execute("delete/"+commentModel.getId());
}
};
private final View.OnClickListener clickListener = v -> {
final Object tag = v.getTag();
if (tag instanceof CommentModel) {
commentModel = (CommentModel) tag;
final String username = commentModel.getProfileModel().getUsername();
final SpannableString title = new SpannableString(username + ":\n" + commentModel.getText());
title.setSpan(new RelativeSizeSpan(1.23f), 0, username.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
String[] commentDialogList;
if (!Utils.isEmpty(cookie) &&
(Utils.getUserIdFromCookie(cookie).equals(commentModel.getProfileModel().getId()) ||
Utils.getUserIdFromCookie(cookie).equals(userId))) commentDialogList = new String[]{
resources.getString(R.string.open_profile),
resources.getString(R.string.view_pfp),
resources.getString(R.string.comment_viewer_copy_user),
resources.getString(R.string.comment_viewer_copy_comment),
resources.getString(R.string.comment_viewer_reply_comment),
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment) : resources.getString(R.string.comment_viewer_like_comment),
resources.getString(R.string.comment_viewer_delete_comment)
};
else if (!Utils.isEmpty(cookie)) commentDialogList = new String[]{
resources.getString(R.string.open_profile),
resources.getString(R.string.view_pfp),
resources.getString(R.string.comment_viewer_copy_user),
resources.getString(R.string.comment_viewer_copy_comment),
resources.getString(R.string.comment_viewer_reply_comment),
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment) : resources.getString(R.string.comment_viewer_like_comment),
};
else commentDialogList = new String[]{
resources.getString(R.string.open_profile),
resources.getString(R.string.view_pfp),
resources.getString(R.string.comment_viewer_copy_user),
resources.getString(R.string.comment_viewer_copy_comment)
};
commmentDialogAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, commentDialogList);
new AlertDialog.Builder(this).setTitle(title)
.setAdapter(commmentDialogAdapter, profileDialogListener)
.setNeutralButton(R.string.cancel, null)
.show();
}
};
private final MentionClickListener mentionClickListener = (view, text, isHashtag) ->
new AlertDialog.Builder(this).setTitle(text)
.setMessage(isHashtag ? R.string.comment_view_mention_hash_search : R.string.comment_view_mention_user_search)
.setNegativeButton(R.string.cancel, null).setPositiveButton(R.string.ok,
(dialog, which) -> searchUsername(text)).show();
private final View.OnClickListener newCommentListener = v -> {
if (Utils.isEmpty(commentsBinding.commentText.getText().toString()) && v == commentsBinding.commentSend)
Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show();
else if (v == commentsBinding.commentSend) new CommentAction().execute("add");
else if (v == commentsBinding.commentCancelParent) {
focus.setBackgroundColor(commentModel.getLiked() ? 0x40FF69B4 : 0x00000000);
commentsBinding.commentCancelParent.setVisibility(View.GONE);
commentsBinding.commentText.setText("");
commentModel = null;
focus = null;
}
};
private void searchUsername(final String text) {
startActivity(
new Intent(getApplicationContext(), ProfileViewer.class)
.putExtra(Constants.EXTRAS_USERNAME, text)
);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.follow, menu);
final MenuItem menuSearch = menu.findItem(R.id.action_search);
final SearchView searchView = (SearchView) menuSearch.getActionView();
searchView.setQueryHint(getResources().getString(R.string.action_search));
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(final String query) {
return false;
}
@Override
public boolean onQueryTextChange(final String query) {
if (commentsAdapter != null) commentsAdapter.getFilter().filter(query);
return true;
}
});
menu.findItem(R.id.action_compare).setVisible(false);
return true;
}
class CommentAction extends AsyncTask<String, Void, Void> {
boolean ok = false;
protected Void doInBackground(String... rawAction) {
final String action = rawAction[0];
final String url = "https://www.instagram.com/web/comments/"+postId+"/"+action+"/";
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 == "add") {
// https://stackoverflow.com/questions/14321873/java-url-encoding-urlencoder-vs-uri
final String commentText = URLEncoder.encode(commentsBinding.commentText.getText().toString(), "UTF-8")
.replaceAll("\\+", "%20").replaceAll("\\%21", "!").replaceAll("\\%27", "'")
.replaceAll("\\%28", "(").replaceAll("\\%29", ")").replaceAll("\\%7E", "~");
final String urlParameters = "comment_text="+commentText+"&replied_to_comment_id="+
(commentModel == null ? "" : commentModel.getId());
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", "" +
urlParameters.getBytes().length);
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
}
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = true;
if (action == "add") {
commentsBinding.commentText.setText("");
commentsBinding.commentText.clearFocus();
}
}
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", action+": " + ex);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if (ok == true) {
if (focus != null) {
focus.setBackgroundColor(commentModel.getLiked() ? 0x40FF69B4 : 0x00000000);
commentsBinding.commentCancelParent.setVisibility(View.GONE);
commentModel = null;
focus = null;
}
onRefresh();
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -1,93 +0,0 @@
package awais.instagrabber.activities;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.navigation.NavController;
import androidx.navigation.NavDestination;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import awais.instagrabber.R;
import awais.instagrabber.databinding.ActivityDirectMessagesBinding;
import awais.instagrabber.fragments.directmessages.DirectMessageThreadFragmentArgs;
import awais.instagrabber.utils.Constants;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class DirectMessagesActivity extends BaseLanguageActivity implements NavController.OnDestinationChangedListener {
private TextView toolbarTitle;
private AppCompatImageView dmInfo, dmSeen;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActivityDirectMessagesBinding binding = ActivityDirectMessagesBinding.inflate(getLayoutInflater());
final CoordinatorLayout root = binding.getRoot();
setContentView(root);
toolbarTitle = binding.toolbarTitle;
final Toolbar toolbar = binding.toolbar;
setSupportActionBar(toolbar);
dmInfo = binding.dmInfo;
dmSeen = binding.dmSeen;
final NavController navController = Navigation.findNavController(this, R.id.direct_messages_nav_host_fragment);
navController.addOnDestinationChangedListener(this);
final AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration);
}
@Override
public void onDestinationChanged(@NonNull final NavController controller,
@NonNull final NavDestination destination,
@Nullable final Bundle arguments) {
switch (destination.getId()) {
case R.id.directMessagesInboxFragment:
setToolbarTitle(R.string.action_dms);
dmInfo.setVisibility(View.GONE);
dmSeen.setVisibility(View.GONE);
return;
case R.id.directMessagesThreadFragment:
if (arguments == null) {
return;
}
final String title = DirectMessageThreadFragmentArgs.fromBundle(arguments).getTitle();
setToolbarTitle(title);
dmInfo.setVisibility(View.VISIBLE);
dmSeen.setVisibility(settingsHelper.getBoolean(Constants.DM_MARK_AS_SEEN) ? View.GONE : View.VISIBLE);
return;
case R.id.directMessagesSettingsFragment:
if (arguments == null) {
return;
}
setToolbarTitle(R.string.action_settings);
dmInfo.setVisibility(View.GONE);
dmSeen.setVisibility(View.GONE);
return;
}
}
private void setToolbarTitle(final String text) {
if (toolbarTitle == null) {
return;
}
toolbarTitle.setText(text);
}
private void setToolbarTitle(final int resourceId) {
if (toolbarTitle == null) {
return;
}
toolbarTitle.setText(resourceId);
}
}

View File

@ -1,333 +0,0 @@
package awais.instagrabber.activities;
import android.content.Intent;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.SearchView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.util.ArrayList;
import java.util.Arrays;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.adapters.FollowAdapter;
import awais.instagrabber.asyncs.FollowFetcher;
import awais.instagrabber.databinding.ActivityFollowBinding;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.FollowModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
import thoughtbot.expandableadapter.ExpandableGroup;
import static awais.instagrabber.utils.Utils.logCollector;
public final class FollowViewer extends BaseLanguageActivity implements SwipeRefreshLayout.OnRefreshListener {
private final ArrayList<FollowModel> followModels = new ArrayList<>();
private final ArrayList<FollowModel> followingModels = new ArrayList<>();
private final ArrayList<FollowModel> followersModels = new ArrayList<>();
private final ArrayList<FollowModel> allFollowing = new ArrayList<>();
private boolean followers, isCompare = false;
private String id, name, namePost, type;
private Resources resources;
private FollowModel model;
private FollowAdapter adapter;
private View.OnClickListener clickListener;
private ActivityFollowBinding followBinding;
private AsyncTask<Void, Void, FollowModel[]> currentlyExecuting;
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
followBinding = ActivityFollowBinding.inflate(getLayoutInflater());
setContentView(followBinding.getRoot());
final Intent intent = getIntent();
if (intent == null || Utils.isEmpty(id = intent.getStringExtra(Constants.EXTRAS_ID))) {
Utils.errorFinish(this);
return;
}
setSupportActionBar(followBinding.toolbar.toolbar);
followers = intent.getBooleanExtra(Constants.EXTRAS_FOLLOWERS, false);
name = intent.getStringExtra(Constants.EXTRAS_NAME);
namePost = name;
if (Utils.isEmpty(name)) {
// this usually should not occur
name = "You";
namePost = "You're";
}
followBinding.toolbar.toolbar.setTitle(name);
resources = getResources();
clickListener = v -> {
final Object tag = v.getTag();
if (tag instanceof FollowModel) {
model = (FollowModel) tag;
startActivity(
new Intent(getApplicationContext(), ProfileViewer.class)
.putExtra(Constants.EXTRAS_USERNAME, model.getUsername())
);
}
};
followBinding.swipeRefreshLayout.setOnRefreshListener(this);
onRefresh();
}
@Override
public void onRefresh() {
if (isCompare) listCompare();
else listFollows();
}
private void listFollows() {
stopCurrentExecutor();
type = resources.getString(followers ? R.string.followers_type_followers : R.string.followers_type_following);
followBinding.toolbar.toolbar.setSubtitle(type);
followModels.clear();
final FetchListener<FollowModel[]> fetchListener = new FetchListener<FollowModel[]>() {
@Override
public void doBefore() {
followBinding.swipeRefreshLayout.setRefreshing(true);
}
@Override
public void onResult(final FollowModel[] result) {
if (result == null) followBinding.swipeRefreshLayout.setRefreshing(false);
else {
followModels.addAll(Arrays.asList(result));
final FollowModel model = result[result.length - 1];
if (model != null && model.hasNextPage()) {
stopCurrentExecutor();
currentlyExecuting = new FollowFetcher(id, followers, model.getEndCursor(), this)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
model.setPageCursor(false, null);
} else {
followBinding.swipeRefreshLayout.setRefreshing(false);
refreshAdapter(followModels, null, null, null);
}
}
}
};
currentlyExecuting = new FollowFetcher(id, followers, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void listCompare() {
stopCurrentExecutor();
followBinding.toolbar.toolbar.setSubtitle(R.string.followers_compare);
allFollowing.clear();
followersModels.clear();
followingModels.clear();
final FetchListener<FollowModel[]> followingFetchListener = new FetchListener<FollowModel[]>() {
@Override
public void onResult(final FollowModel[] result) {
if (result != null) {
followingModels.addAll(Arrays.asList(result));
final FollowModel model = result[result.length - 1];
if (model != null && model.hasNextPage()) {
stopCurrentExecutor();
currentlyExecuting = new FollowFetcher(id, false, model.getEndCursor(), this)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
model.setPageCursor(false, null);
} else {
allFollowing.addAll(followersModels);
allFollowing.retainAll(followingModels);
for (final FollowModel followModel : allFollowing) {
followersModels.remove(followModel);
followingModels.remove(followModel);
}
allFollowing.trimToSize();
followersModels.trimToSize();
followingModels.trimToSize();
followBinding.swipeRefreshLayout.setRefreshing(false);
refreshAdapter(null, followingModels, followersModels, allFollowing);
}
} else followBinding.swipeRefreshLayout.setRefreshing(false);
}
};
final FetchListener<FollowModel[]> followersFetchListener = new FetchListener<FollowModel[]>() {
@Override
public void doBefore() {
followBinding.swipeRefreshLayout.setRefreshing(true);
}
@Override
public void onResult(final FollowModel[] result) {
if (result != null) {
followersModels.addAll(Arrays.asList(result));
final FollowModel model = result[result.length - 1];
if (model == null || !model.hasNextPage()) {
stopCurrentExecutor();
currentlyExecuting = new FollowFetcher(id, false, followingFetchListener)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
stopCurrentExecutor();
currentlyExecuting = new FollowFetcher(id, true, model.getEndCursor(), this)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
model.setPageCursor(false, null);
}
}
}
};
currentlyExecuting = new FollowFetcher(id, true, followersFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.follow, menu);
final MenuItem menuSearch = menu.findItem(R.id.action_search);
final SearchView searchView = (SearchView) menuSearch.getActionView();
searchView.setQueryHint(getResources().getString(R.string.action_search));
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
// private final Filter filter = new Filter() {
// private final ArrayList<FollowModel> searchFollowModels = new ArrayList<>(followModels.size() / 2);
// private final ArrayList<FollowModel> searchFollowingModels = new ArrayList<>(followingModels.size() / 2);
// private final ArrayList<FollowModel> searchFollowersModels = new ArrayList<>(followersModels.size() / 2);
// private final ArrayList<FollowModel> searchAllFollowing = new ArrayList<>(allFollowing.size() / 2);
//
// @Nullable
// @Override
// protected FilterResults performFiltering(@NonNull final CharSequence constraint) {
// searchFollowModels.clear();
// searchFollowingModels.clear();
// searchFollowersModels.clear();
// searchAllFollowing.clear();
//
// final int followModelsSize = followModels.size();
// final int followingModelsSize = followingModels.size();
// final int followersModelsSize = followersModels.size();
// final int allFollowingSize = allFollowing.size();
//
// int maxSize = followModelsSize;
// if (maxSize < followingModelsSize) maxSize = followingModelsSize;
// if (maxSize < followersModelsSize) maxSize = followersModelsSize;
// if (maxSize < allFollowingSize) maxSize = allFollowingSize;
//
// final String query = constraint.toString().toLowerCase();
// FollowModel followModel;
// while (maxSize != -1) {
// if (maxSize < followModelsSize) {
// followModel = followModels.get(maxSize);
// if (Utils.hasKey(query, followModel.getUsername(), followModel.getFullName()))
// searchFollowModels.add(followModel);
// }
//
// if (maxSize < followingModelsSize) {
// followModel = followingModels.get(maxSize);
// if (Utils.hasKey(query, followModel.getUsername(), followModel.getFullName()))
// searchFollowingModels.add(followModel);
// }
//
// if (maxSize < followersModelsSize) {
// followModel = followersModels.get(maxSize);
// if (Utils.hasKey(query, followModel.getUsername(), followModel.getFullName()))
// searchFollowersModels.add(followModel);
// }
//
// if (maxSize < allFollowingSize) {
// followModel = allFollowing.get(maxSize);
// if (Utils.hasKey(query, followModel.getUsername(), followModel.getFullName()))
// searchAllFollowing.add(followModel);
// }
//
// --maxSize;
// }
//
// return null;
// }
//
// @Override
// protected void publishResults(final CharSequence query, final FilterResults results) {
// refreshAdapter(searchFollowModels, searchFollowingModels, searchFollowersModels, searchAllFollowing);
// }
// };
@Override
public boolean onQueryTextSubmit(final String query) {
return false;
}
@Override
public boolean onQueryTextChange(final String query) {
// if (Utils.isEmpty(query)) refreshAdapter(followModels, followingModels, followersModels, allFollowing);
// else filter.filter(query.toLowerCase());
if (adapter != null) adapter.getFilter().filter(query);
return true;
}
});
final MenuItem menuCompare = menu.findItem(R.id.action_compare);
menuCompare.setOnMenuItemClickListener(item -> {
followBinding.rvFollow.setAdapter(null);
if (isCompare) listFollows();
else listCompare();
isCompare = !isCompare;
return true;
});
return true;
}
private void refreshAdapter(final ArrayList<FollowModel> followModels, final ArrayList<FollowModel> followingModels,
final ArrayList<FollowModel> followersModels, final ArrayList<FollowModel> allFollowing) {
final ArrayList<ExpandableGroup> groups = new ArrayList<>(1);
if (isCompare) {
if (followingModels != null && followingModels.size() > 0)
groups.add(new ExpandableGroup(resources.getString(R.string.followers_not_following, name), followingModels));
if (followersModels != null && followersModels.size() > 0)
groups.add(new ExpandableGroup(resources.getString(R.string.followers_not_follower, namePost), followersModels));
if (allFollowing != null && allFollowing.size() > 0)
groups.add(new ExpandableGroup(resources.getString(R.string.followers_both_following), allFollowing));
} else {
final ExpandableGroup group = new ExpandableGroup(type, followModels);
groups.add(group);
}
adapter = new FollowAdapter(this, clickListener, groups);
adapter.toggleGroup(0);
followBinding.rvFollow.setAdapter(adapter);
}
public void stopCurrentExecutor() {
if (currentlyExecuting != null) {
try {
currentlyExecuting.cancel(true);
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.MAIN_HELPER, "stopCurrentExecutor");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
}
}
}
}

View File

@ -1,6 +1,7 @@
package awais.instagrabber.activities;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
@ -19,9 +20,8 @@ import androidx.annotation.Nullable;
import awais.instagrabber.R;
import awais.instagrabber.databinding.ActivityLoginBinding;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.TextUtils;
public final class Login extends BaseLanguageActivity implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {
private final WebViewClient webViewClient = new WebViewClient() {
@ -33,16 +33,24 @@ public final class Login extends BaseLanguageActivity implements View.OnClickLis
@Override
public void onPageFinished(final WebView view, final String url) {
webViewUrl = url;
final String mainCookie = Utils.getCookie(url);
if (Utils.isEmpty(mainCookie) || !mainCookie.contains("; ds_user_id=")) ready = true;
else if (mainCookie.contains("; ds_user_id=") && ready) {
Utils.setupCookies(mainCookie);
settingsHelper.putString(Constants.COOKIE, mainCookie);
Toast.makeText(getApplicationContext(), R.string.login_success_loading_cookies, Toast.LENGTH_SHORT).show();
finish();
final String mainCookie = CookieUtils.getCookie(url);
if (TextUtils.isEmpty(mainCookie) || !mainCookie.contains("; ds_user_id=")) {
ready = true;
return;
}
if (mainCookie.contains("; ds_user_id=") && ready) {
returnCookieResult(mainCookie);
}
}
};
private void returnCookieResult(final String mainCookie) {
final Intent intent = new Intent();
intent.putExtra("cookie", mainCookie);
setResult(Constants.LOGIN_RESULT_CODE, intent);
finish();
}
private final WebChromeClient webChromeClient = new WebChromeClient();
private String webViewUrl, defaultUserAgent;
private boolean ready = false;
@ -65,16 +73,15 @@ public final class Login extends BaseLanguageActivity implements View.OnClickLis
public void onClick(final View v) {
if (v == loginBinding.refresh) {
loginBinding.webView.loadUrl("https://instagram.com/");
} else if (v == loginBinding.cookies) {
final String mainCookie = Utils.getCookie(webViewUrl);
if (Utils.isEmpty(mainCookie) || !mainCookie.contains("; ds_user_id="))
return;
}
if (v == loginBinding.cookies) {
final String mainCookie = CookieUtils.getCookie(webViewUrl);
if (TextUtils.isEmpty(mainCookie) || !mainCookie.contains("; ds_user_id=")) {
Toast.makeText(this, R.string.login_error_loading_cookies, Toast.LENGTH_SHORT).show();
else {
Utils.setupCookies(mainCookie);
settingsHelper.putString(Constants.COOKIE, mainCookie);
Toast.makeText(this, R.string.login_success_loading_cookies, Toast.LENGTH_SHORT).show();
finish();
return;
}
returnCookieResult(mainCookie);
}
}
@ -82,8 +89,9 @@ public final class Login extends BaseLanguageActivity implements View.OnClickLis
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
final WebSettings webSettings = loginBinding.webView.getSettings();
final String newUserAgent = isChecked ? "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36"
: defaultUserAgent;
final String newUserAgent = isChecked
? "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36"
: defaultUserAgent;
webSettings.setUserAgentString(newUserAgent);
webSettings.setUseWideViewPort(isChecked);
@ -95,7 +103,6 @@ public final class Login extends BaseLanguageActivity implements View.OnClickLis
}
@SuppressLint("SetJavaScriptEnabled")
@SuppressWarnings("deprecation")
private void initWebView() {
if (loginBinding != null) {
loginBinding.webView.setWebChromeClient(webChromeClient);

View File

@ -1,602 +0,0 @@
package awais.instagrabber.activities;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.MatrixCursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.PersistableBundle;
import android.provider.BaseColumns;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
import androidx.core.app.NotificationCompat;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.GridLayoutManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.MainHelper;
import awais.instagrabber.R;
import awais.instagrabber.adapters.HighlightsAdapter;
import awais.instagrabber.adapters.SuggestionsAdapter;
import awais.instagrabber.asyncs.GetActivityAsyncTask;
import awais.instagrabber.asyncs.SuggestionsFetcher;
import awais.instagrabber.asyncs.UsernameFetcher;
import awais.instagrabber.asyncs.i.iStoryStatusFetcher;
import awais.instagrabber.customviews.MouseDrawer;
import awais.instagrabber.databinding.ActivityMainBinding;
import awais.instagrabber.dialogs.AboutDialog;
import awais.instagrabber.dialogs.QuickAccessDialog;
import awais.instagrabber.dialogs.SettingsDialog;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.interfaces.ItemGetter;
import awais.instagrabber.models.DiscoverItemModel;
import awais.instagrabber.models.FeedModel;
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.SuggestionModel;
import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.ItemGetType;
import awais.instagrabber.models.enums.SuggestionType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DataBox;
import awais.instagrabber.utils.FlavorTown;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.CHANNEL_ID;
import static awais.instagrabber.utils.Utils.notificationManager;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class Main extends BaseLanguageActivity {
private static final int INITIAL_DELAY_MILLIS = 200;
private static final int DELAY_MILLIS = 60000;
public static FetchListener<String> scanHack;
public static ItemGetter itemGetter;
// -------- items --------
public final ArrayList<PostModel> allItems = new ArrayList<>();
public final ArrayList<FeedModel> feedItems = new ArrayList<>();
public final ArrayList<DiscoverItemModel> discoverItems = new ArrayList<>();
// -------- items --------
public final ArrayList<PostModel> selectedItems = new ArrayList<>();
public final ArrayList<DiscoverItemModel> selectedDiscoverItems = new ArrayList<>();
// -------- items --------
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,
(!mainHelper.isLoggedIn && Utils.settingsHelper.getBoolean(Constants.STORIESIG)), true, result -> {
if (result != null && result.length > 0)
startActivity(new Intent(Main.this, StoryViewer.class)
.putExtra(Constants.EXTRAS_USERNAME, userQuery.replace("@", ""))
.putExtra(Constants.EXTRAS_HIGHLIGHT, highlightModel.getTitle())
.putExtra(Constants.EXTRAS_STORIES, result)
);
else
Toast.makeText(Main.this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
});
private SuggestionsAdapter suggestionAdapter;
private MenuItem searchAction;
public ActivityMainBinding mainBinding;
public SearchView searchView;
public MenuItem downloadAction, settingsAction, dmsAction, notifAction;
public StoryModel[] storyModels;
public String userQuery = null, cookie, uid = null;
public MainHelper mainHelper;
public ProfileModel profileModel;
public HashtagModel hashtagModel;
public LocationModel locationModel;
private AutoCompleteTextView searchAutoComplete;
private ArrayAdapter<String> profileDialogAdapter;
private DialogInterface.OnClickListener profileDialogListener;
private Stack<String> queriesStack;
private DataBox.CookieModel cookieModel;
private Runnable runnable;
private Handler handler;
@Override
protected void onCreate(@Nullable final Bundle bundle) {
super.onCreate(bundle);
mainBinding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(mainBinding.getRoot());
if (settingsHelper.getBoolean(Constants.CHECK_UPDATES)) FlavorTown.updateCheck(this);
FlavorTown.changelogCheck(this);
cookie = settingsHelper.getString(Constants.COOKIE);
uid = Utils.getUserIdFromCookie(cookie);
Utils.setupCookies(cookie);
MainHelper.stopCurrentExecutor();
mainHelper = new MainHelper(this);
if (bundle == null) {
queriesStack = new Stack<>();
userQuery = null;
} else {
setStack(bundle);
userQuery = bundle.getString("query");
}
mainHelper.isLoggedIn = !Utils.isEmpty(Utils.settingsHelper.getString(Constants.COOKIE));
itemGetter = itemGetType -> {
if (itemGetType == ItemGetType.MAIN_ITEMS) return allItems;
if (itemGetType == ItemGetType.DISCOVER_ITEMS) return discoverItems;
if (itemGetType == ItemGetType.FEED_ITEMS) return feedItems;
return null;
};
scanHack = result -> {
if (mainHelper != null && !Utils.isEmpty(result)) {
closeAnyOpenDrawer();
addToStack();
userQuery = (result.contains("/") || result.startsWith("#") || result.startsWith("@")) ? result : ("@" + result);
mainHelper.onRefresh();
}
};
// searches for your userid and returns username
if (uid != null) {
final FetchListener<String> fetchListener = username -> {
if (!Utils.isEmpty(username)) {
if (!BuildConfig.DEBUG) {
userQuery = username;
if (mainHelper != null && !mainBinding.profileView.swipeRefreshLayout.isRefreshing())
mainHelper.onRefresh();
}
// adds cookies to database for quick access
cookieModel = Utils.dataBox.getCookie(uid);
if (Utils.dataBox.getCookieCount() == 0 || cookieModel == null || Utils.isEmpty(cookieModel.getUsername()))
Utils.dataBox.addUserCookie(new DataBox.CookieModel(uid, username, cookie));
}
};
boolean found = false;
cookieModel = Utils.dataBox.getCookie(uid);
if (cookieModel != null) {
final String username = cookieModel.getUsername();
if (username != null) {
found = true;
fetchListener.onResult("@" + username);
}
}
if (!found) // if not in database, fetch info from instagram
new UsernameFetcher(uid, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
suggestionAdapter = new SuggestionsAdapter(this, v -> {
final Object tag = v.getTag();
if (tag instanceof CharSequence) {
addToStack();
userQuery = tag.toString();
mainHelper.onRefresh();
}
if (searchView != null && !searchView.isIconified()) {
if (searchAction != null) searchAction.collapseActionView();
searchView.setIconified(true);
searchView.setIconified(true);
}
});
final Resources resources = getResources();
profileDialogAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
new String[]{resources.getString(R.string.view_pfp), resources.getString(R.string.show_stories)});
profileDialogListener = (dialog, which) -> {
final Intent intent;
if (which == 0 || storyModels == null || storyModels.length < 1) {
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)));
} else
intent = new Intent(this, StoryViewer.class).putExtra(Constants.EXTRAS_USERNAME, userQuery.replace("@", ""))
.putExtra(Constants.EXTRAS_STORIES, storyModels)
.putExtra(Constants.EXTRAS_HASHTAG, (hashtagModel != null));
startActivity(intent);
};
final View.OnClickListener onClickListener = v -> {
if (v == mainBinding.profileView.mainBiography) {
Utils.copyText(this, mainBinding.profileView.mainBiography.getText().toString());
} else if (v == mainBinding.profileView.locationBiography) {
Utils.copyText(this, mainBinding.profileView.locationBiography.getText().toString());
} else if (v == mainBinding.profileView.mainProfileImage || v == mainBinding.profileView.mainHashtagImage || v == mainBinding.profileView.mainLocationImage) {
if (storyModels == null || storyModels.length <= 0) {
profileDialogListener.onClick(null, 0);
} else {
// because sometimes configuration changes made this crash on some phones
new AlertDialog.Builder(this).setAdapter(profileDialogAdapter, profileDialogListener)
.setNeutralButton(R.string.cancel, null).show();
}
}
};
mainBinding.profileView.mainBiography.setOnClickListener(onClickListener);
mainBinding.profileView.locationBiography.setOnClickListener(onClickListener);
mainBinding.profileView.mainProfileImage.setOnClickListener(onClickListener);
mainBinding.profileView.mainHashtagImage.setOnClickListener(onClickListener);
mainBinding.profileView.mainLocationImage.setOnClickListener(onClickListener);
mainBinding.profileView.mainBiography.setEnabled(false);
mainBinding.profileView.mainProfileImage.setEnabled(false);
mainBinding.profileView.mainHashtagImage.setEnabled(false);
mainBinding.profileView.mainLocationImage.setEnabled(false);
final boolean isQueryNull = userQuery == null;
if (isQueryNull) {
allItems.clear();
mainBinding.profileView.privatePage1.setImageResource(R.drawable.ic_info);
mainBinding.profileView.privatePage2.setTextSize(20);
mainBinding.profileView.privatePage2.setText(mainHelper.isLoggedIn ? R.string.no_acc_logged_in : R.string.no_acc);
mainBinding.profileView.privatePage.setVisibility(View.VISIBLE);
}
if (!mainBinding.profileView.swipeRefreshLayout.isRefreshing() && userQuery != null)
mainHelper.onRefresh();
mainHelper.onIntent(getIntent());
handler = new Handler();
runnable = () -> {
final GetActivityAsyncTask activityAsyncTask = new GetActivityAsyncTask(uid, cookie, result -> {
if (result == null || notificationManager == null) {
return;
}
final List<String> list = new ArrayList<>();
if (result.getRelationshipsCount() != 0) {
list.add(getString(R.string.activity_count_relationship, result.getRelationshipsCount()));
}
if (result.getUserTagsCount() != 0) {
list.add(getString(R.string.activity_count_usertags, result.getUserTagsCount()));
}
if (result.getCommentsCount() != 0) {
list.add(getString(R.string.activity_count_comments, result.getCommentsCount()));
}
if (result.getCommentLikesCount() != 0) {
list.add(getString(R.string.activity_count_commentlikes, result.getCommentLikesCount()));
}
if (result.getLikesCount() != 0) {
list.add(getString(R.string.activity_count_likes, result.getLikesCount()));
}
if (list.isEmpty()) {
return;
}
final String join = TextUtils.join(", ", list);
final String notificationString = getString(R.string.activity_count_prefix) + " " + join + ".";
final Intent intent = new Intent(getApplicationContext(), NotificationsViewer.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
final Notification notification = new NotificationCompat.Builder(Main.this, CHANNEL_ID)
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setSmallIcon(R.drawable.ic_notif)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setContentText(notificationString)
.setContentIntent(PendingIntent.getActivity(getApplicationContext(), 1738, intent, PendingIntent.FLAG_UPDATE_CURRENT))
.build();
notificationManager.cancel(1800000000);
notificationManager.notify(1800000000, notification);
});
if (!Utils.isEmpty(cookie) && Utils.settingsHelper.getBoolean(Constants.CHECK_ACTIVITY)) activityAsyncTask.execute();
handler.postDelayed(runnable, DELAY_MILLIS);
};
handler.postDelayed(runnable, INITIAL_DELAY_MILLIS);
}
private void downloadSelectedItems() {
if (selectedItems.size() > 0) {
Utils.batchDownload(this, userQuery, DownloadMethod.DOWNLOAD_MAIN, selectedItems);
} else if (selectedDiscoverItems.size() > 0) {
Utils.batchDownload(this, null, DownloadMethod.DOWNLOAD_DISCOVER, selectedDiscoverItems);
}
}
@Override
protected void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
mainHelper.onIntent(intent);
}
@Override
public void onSaveInstanceState(@NonNull final Bundle outState, @NonNull final PersistableBundle outPersistentState) {
outState.putString("query", userQuery);
outState.putSerializable("stack", queriesStack);
super.onSaveInstanceState(outState, outPersistentState);
}
@Override
public void onRestoreInstanceState(@Nullable final Bundle savedInstanceState, @Nullable final PersistableBundle persistentState) {
super.onRestoreInstanceState(savedInstanceState, persistentState);
if (savedInstanceState != null) {
userQuery = savedInstanceState.getString("query");
setStack(savedInstanceState);
}
}
@Override
protected void onSaveInstanceState(@NonNull final Bundle outState) {
outState.putString("query", userQuery);
outState.putSerializable("stack", queriesStack);
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
userQuery = savedInstanceState.getString("query");
setStack(savedInstanceState);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
final FragmentManager fragmentManager = getSupportFragmentManager();
final MenuItem quickAccessAction = menu.findItem(R.id.action_quickaccess).setVisible(true);
final MenuItem.OnMenuItemClickListener clickListener = item -> {
if (item == downloadAction)
downloadSelectedItems();
else if (item == dmsAction)
startActivity(new Intent(this, DirectMessagesActivity.class));
else if (item == notifAction)
startActivity(new Intent(this, NotificationsViewer.class));
else if (item == settingsAction)
new SettingsDialog().show(fragmentManager, "settings");
else if (item == quickAccessAction)
new QuickAccessDialog()
.setQuery(userQuery, locationModel != null ? locationModel.getName() : userQuery)
.show(fragmentManager, "quickAccess");
else
new AboutDialog().show(fragmentManager, "about");
return true;
};
quickAccessAction.setOnMenuItemClickListener(clickListener);
menu.findItem(R.id.action_about).setVisible(true).setOnMenuItemClickListener(clickListener);
dmsAction = menu.findItem(R.id.action_dms).setOnMenuItemClickListener(clickListener);
notifAction = menu.findItem(R.id.action_notif).setOnMenuItemClickListener(clickListener);
settingsAction = menu.findItem(R.id.action_settings).setVisible(true).setOnMenuItemClickListener(clickListener);
downloadAction = menu.findItem(R.id.action_download).setOnMenuItemClickListener(clickListener);
if (!Utils.isEmpty(Utils.settingsHelper.getString(Constants.COOKIE))) {
notifAction.setVisible(true);
dmsAction.setVisible(true).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
searchAction = menu.findItem(R.id.action_search);
searchView = (SearchView) searchAction.getActionView();
final View searchText = searchView.findViewById(R.id.search_src_text);
if (searchText instanceof AutoCompleteTextView)
searchAutoComplete = (AutoCompleteTextView) searchText;
searchView.setQueryHint(getResources().getString(R.string.action_search));
searchView.setSuggestionsAdapter(suggestionAdapter);
searchView.setOnSearchClickListener(v -> {
searchView.setQuery((cookieModel != null && userQuery != null && userQuery.equals("@" + cookieModel.getUsername())) ? "" : userQuery, false);
menu.findItem(R.id.action_about).setVisible(false);
menu.findItem(R.id.action_settings).setVisible(false);
menu.findItem(R.id.action_dms).setVisible(false);
menu.findItem(R.id.action_quickaccess).setVisible(false);
menu.findItem(R.id.action_notif).setVisible(false);
});
searchAction.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
menu.findItem(R.id.action_about).setVisible(true);
menu.findItem(R.id.action_settings).setVisible(true);
menu.findItem(R.id.action_dms).setVisible(!Utils.isEmpty(Utils.settingsHelper.getString(Constants.COOKIE)));
menu.findItem(R.id.action_quickaccess).setVisible(true);
menu.findItem(R.id.action_notif).setVisible(true);
return true;
}
});
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
private boolean searchUser, searchHash;
private AsyncTask<?, ?, ?> prevSuggestionAsync;
private final String[] COLUMNS = {BaseColumns._ID, Constants.EXTRAS_USERNAME, Constants.EXTRAS_NAME,
Constants.EXTRAS_TYPE, "pfp", "verified"};
private final FetchListener<SuggestionModel[]> fetchListener = new FetchListener<SuggestionModel[]>() {
@Override
public void doBefore() {
suggestionAdapter.changeCursor(null);
}
@Override
public void onResult(final SuggestionModel[] result) {
final MatrixCursor cursor;
if (result == null) cursor = null;
else {
cursor = new MatrixCursor(COLUMNS, 0);
for (int i = 0; i < result.length; i++) {
final SuggestionModel suggestionModel = result[i];
if (suggestionModel != null) {
final SuggestionType suggestionType = suggestionModel.getSuggestionType();
final Object[] objects = {i,
(suggestionType == SuggestionType.TYPE_LOCATION) ? suggestionModel.getName() : suggestionModel.getUsername(),
(suggestionType == SuggestionType.TYPE_LOCATION) ? suggestionModel.getUsername() : suggestionModel.getName(),
suggestionType, suggestionModel.getProfilePic(), suggestionModel.isVerified()};
if (!searchHash && !searchUser) cursor.addRow(objects);
else {
final boolean isCurrHash = suggestionType == SuggestionType.TYPE_HASHTAG;
if (searchHash && isCurrHash || !searchHash && !isCurrHash)
cursor.addRow(objects);
}
}
}
}
suggestionAdapter.changeCursor(cursor);
}
};
private void cancelSuggestionsAsync() {
if (prevSuggestionAsync != null)
try {
prevSuggestionAsync.cancel(true);
} catch (final Exception ignored) {
}
}
@Override
public boolean onQueryTextSubmit(final String query) {
cancelSuggestionsAsync();
menu.findItem(R.id.action_about).setVisible(true);
menu.findItem(R.id.action_settings).setVisible(true);
closeAnyOpenDrawer();
addToStack();
userQuery = (query.contains("@") || query.contains("#")) ? query : ("@" + query);
searchAction.collapseActionView();
searchView.setIconified(true);
searchView.setIconified(true);
mainHelper.onRefresh();
return false;
}
@Override
public boolean onQueryTextChange(final String newText) {
cancelSuggestionsAsync();
if (!Utils.isEmpty(newText)) {
searchUser = newText.charAt(0) == '@';
searchHash = newText.charAt(0) == '#';
if (newText.length() == 1 && (searchHash || searchUser)) {
if (searchAutoComplete != null) searchAutoComplete.setThreshold(2);
} else {
if (searchAutoComplete != null) searchAutoComplete.setThreshold(1);
prevSuggestionAsync = new SuggestionsFetcher(fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
searchUser || searchHash ? newText.substring(1) : newText);
}
}
return true;
}
});
return true;
}
@Override
public void onBackPressed() {
if (closeAnyOpenDrawer()) return;
if (searchView != null && !searchView.isIconified()) {
if (searchAction != null) searchAction.collapseActionView();
searchView.setIconified(true);
searchView.setIconified(true);
return;
}
if (!mainHelper.isSelectionCleared()) return;
final GridLayoutManager layoutManager = (GridLayoutManager) mainBinding.profileView.mainPosts.getLayoutManager();
if (layoutManager != null && layoutManager.findFirstCompletelyVisibleItemPosition() >= layoutManager.getSpanCount()) {
mainBinding.profileView.mainPosts.smoothScrollToPosition(0);
mainBinding.profileView.appBarLayout.setExpanded(true, true);
return;
}
if (queriesStack != null && queriesStack.size() > 0) {
userQuery = queriesStack.pop();
if (userQuery != null) {
mainHelper.onRefresh();
return;
}
} else {
finish();
}
}
@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 8020 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
downloadSelectedItems();
}
@Override
protected void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 9629 && (resultCode == 1692 || resultCode == RESULT_CANCELED))
finish();
else if (requestCode == 6007)
Utils.showImportExportDialog(this);
else if (requestCode == 6969 && mainHelper.currentFeedPlayer != null)
mainHelper.currentFeedPlayer.setPlayWhenReady(true);
}
@Override
protected void onPause() {
if (mainHelper != null) mainHelper.onPause();
if (handler != null && runnable != null) {
handler.removeCallbacks(runnable);
}
super.onPause();
}
@Override
protected void onResume() {
if (mainHelper != null) mainHelper.onResume();
if (handler != null && runnable != null) {
handler.postDelayed(runnable, INITIAL_DELAY_MILLIS);
}
super.onResume();
}
private void setStack(final Bundle bundle) {
final Object stack = bundle != null ? bundle.get("stack") : null;
if (stack instanceof Stack) //noinspection unchecked
queriesStack = (Stack<String>) stack;
}
public void addToStack() {
if (userQuery != null) {
if (queriesStack == null) queriesStack = new Stack<>();
queriesStack.add(userQuery);
}
}
private boolean closeAnyOpenDrawer() {
final int childCount = mainBinding.drawerLayout.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = mainBinding.drawerLayout.getChildAt(i);
final MouseDrawer.LayoutParams childLp = (MouseDrawer.LayoutParams) child.getLayoutParams();
if ((childLp.openState & MouseDrawer.LayoutParams.FLAG_IS_OPENED) == 1 ||
(childLp.openState & MouseDrawer.LayoutParams.FLAG_IS_OPENING) == 2 ||
childLp.onScreen >= 0.6 || childLp.isPeeking) {
mainBinding.drawerLayout.closeDrawer(child);
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,585 @@
package awais.instagrabber.activities;
import android.annotation.SuppressLint;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.TypedArray;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.provider.BaseColumns;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AutoCompleteTextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.app.NotificationManagerCompat;
import androidx.lifecycle.LiveData;
import androidx.navigation.NavBackStackEntry;
import androidx.navigation.NavController;
import androidx.navigation.NavDestination;
import androidx.navigation.NavDirections;
import androidx.navigation.ui.NavigationUI;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import awais.instagrabber.R;
import awais.instagrabber.adapters.SuggestionsAdapter;
import awais.instagrabber.asyncs.SuggestionsFetcher;
import awais.instagrabber.customviews.helpers.CustomHideBottomViewOnScrollBehavior;
import awais.instagrabber.databinding.ActivityMainBinding;
import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.IntentModel;
import awais.instagrabber.models.SuggestionModel;
import awais.instagrabber.models.enums.SuggestionType;
import awais.instagrabber.services.ActivityCheckerService;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.FlavorTown;
import awais.instagrabber.utils.IntentUtils;
import awais.instagrabber.utils.TextUtils;
import static awais.instagrabber.utils.NavigationExtensions.setupWithNavController;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class MainActivity extends BaseLanguageActivity {
private static final String TAG = "MainActivity";
private static final List<Integer> SHOW_BOTTOM_VIEW_DESTINATIONS = Arrays.asList(
R.id.directMessagesInboxFragment,
R.id.feedFragment,
R.id.profileFragment,
R.id.discoverFragment,
R.id.morePreferencesFragment);
private static final List<Integer> KEEP_SCROLL_BEHAVIOUR_DESTINATIONS = Arrays.asList(
R.id.directMessagesInboxFragment,
R.id.feedFragment,
R.id.profileFragment,
R.id.discoverFragment,
R.id.morePreferencesFragment,
R.id.settingsPreferencesFragment,
R.id.aboutFragment,
R.id.hashTagFragment,
R.id.locationFragment,
R.id.savedViewerFragment,
R.id.commentsViewerFragment,
R.id.followViewerFragment,
R.id.directMessagesSettingsFragment,
R.id.notificationsViewer,
R.id.themePreferencesFragment);
private static final Map<Integer, Integer> NAV_TO_MENU_ID_MAP = new HashMap<>();
private static final List<Integer> REMOVE_COLLAPSING_TOOLBAR_SCROLL_DESTINATIONS = Collections.singletonList(R.id.commentsViewerFragment);
private static final String FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex";
private ActivityMainBinding binding;
private LiveData<NavController> currentNavControllerLiveData;
private MenuItem searchMenuItem;
private SuggestionsAdapter suggestionAdapter;
private AutoCompleteTextView searchAutoComplete;
private SearchView searchView;
private boolean showSearch = true;
private Handler suggestionsFetchHandler;
private int firstFragmentGraphIndex;
private boolean isActivityCheckerServiceBound = false;
private final ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(final ComponentName name, final IBinder service) {
// final ActivityCheckerService.LocalBinder binder = (ActivityCheckerService.LocalBinder) service;
// final ActivityCheckerService activityCheckerService = binder.getService();
isActivityCheckerServiceBound = true;
}
@Override
public void onServiceDisconnected(final ComponentName name) {
isActivityCheckerServiceBound = false;
}
};
static {
NAV_TO_MENU_ID_MAP.put(R.navigation.direct_messages_nav_graph, R.id.direct_messages_nav_graph);
NAV_TO_MENU_ID_MAP.put(R.navigation.feed_nav_graph, R.id.feed_nav_graph);
NAV_TO_MENU_ID_MAP.put(R.navigation.profile_nav_graph, R.id.profile_nav_graph);
NAV_TO_MENU_ID_MAP.put(R.navigation.discover_nav_graph, R.id.discover_nav_graph);
NAV_TO_MENU_ID_MAP.put(R.navigation.more_nav_graph, R.id.more_nav_graph);
}
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
final String cookie = settingsHelper.getString(Constants.COOKIE);
CookieUtils.setupCookies(cookie);
setContentView(binding.getRoot());
final Toolbar toolbar = binding.toolbar;
setSupportActionBar(toolbar);
createNotificationChannels();
if (savedInstanceState == null) {
setupBottomNavigationBar(true);
}
setupScrollingListener();
setupSuggestions();
final boolean checkUpdates = settingsHelper.getBoolean(Constants.CHECK_UPDATES);
if (checkUpdates) FlavorTown.updateCheck(this);
FlavorTown.changelogCheck(this);
final Intent intent = getIntent();
handleIntent(intent);
if (!TextUtils.isEmpty(cookie) && settingsHelper.getBoolean(Constants.CHECK_ACTIVITY)) {
bindActivityCheckerService();
}
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
searchMenuItem = menu.findItem(R.id.search);
if (showSearch && currentNavControllerLiveData != null && currentNavControllerLiveData.getValue() != null) {
final NavController navController = currentNavControllerLiveData.getValue();
final NavDestination currentDestination = navController.getCurrentDestination();
if (currentDestination != null) {
final int destinationId = currentDestination.getId();
showSearch = destinationId == R.id.profileFragment;
}
}
if (!showSearch) {
searchMenuItem.setVisible(false);
return true;
}
return setupSearchView();
}
@Override
protected void onSaveInstanceState(@NonNull final Bundle outState) {
outState.putString(FIRST_FRAGMENT_GRAPH_INDEX_KEY, String.valueOf(firstFragmentGraphIndex));
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
final String key = (String) savedInstanceState.get(FIRST_FRAGMENT_GRAPH_INDEX_KEY);
if (key != null) {
try {
firstFragmentGraphIndex = Integer.parseInt(key);
} catch (NumberFormatException ignored) { }
}
setupBottomNavigationBar(false);
}
@Override
public boolean onSupportNavigateUp() {
if (currentNavControllerLiveData != null && currentNavControllerLiveData.getValue() != null) {
return currentNavControllerLiveData.getValue().navigateUp();
}
return false;
}
@Override
protected void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
handleIntent(intent);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindActivityCheckerService();
}
private void createNotificationChannels() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getApplicationContext());
notificationManager.createNotificationChannel(new NotificationChannel(Constants.DOWNLOAD_CHANNEL_ID,
Constants.DOWNLOAD_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT));
notificationManager.createNotificationChannel(new NotificationChannel(Constants.ACTIVITY_CHANNEL_ID,
Constants.ACTIVITY_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT));
}
}
private void setupSuggestions() {
suggestionsFetchHandler = new Handler();
suggestionAdapter = new SuggestionsAdapter(this, (type, query) -> {
if (searchMenuItem != null) searchMenuItem.collapseActionView();
if (searchView != null && !searchView.isIconified()) searchView.setIconified(true);
if (currentNavControllerLiveData != null && currentNavControllerLiveData.getValue() != null) {
final NavController navController = currentNavControllerLiveData.getValue();
final Bundle bundle = new Bundle();
switch (type) {
case TYPE_LOCATION:
bundle.putString("locationId", query);
navController.navigate(R.id.action_global_locationFragment, bundle);
break;
case TYPE_HASHTAG:
bundle.putString("hashtag", query);
navController.navigate(R.id.action_global_hashTagFragment, bundle);
break;
case TYPE_USER:
bundle.putString("username", query);
navController.navigate(R.id.action_global_profileFragment, bundle);
break;
}
}
});
}
private void setupScrollingListener() {
final CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) binding.bottomNavView.getLayoutParams();
layoutParams.setBehavior(new CustomHideBottomViewOnScrollBehavior());
binding.bottomNavView.requestLayout();
}
private boolean setupSearchView() {
final View actionView = searchMenuItem.getActionView();
if (!(actionView instanceof SearchView)) return false;
searchView = (SearchView) actionView;
searchView.setSuggestionsAdapter(suggestionAdapter);
searchView.setMaxWidth(Integer.MAX_VALUE);
final View searchText = searchView.findViewById(R.id.search_src_text);
if (searchText instanceof AutoCompleteTextView) {
searchAutoComplete = (AutoCompleteTextView) searchText;
}
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
private boolean searchUser;
private boolean searchHash;
private AsyncTask<?, ?, ?> prevSuggestionAsync;
private final String[] COLUMNS = {
BaseColumns._ID,
Constants.EXTRAS_USERNAME,
Constants.EXTRAS_NAME,
Constants.EXTRAS_TYPE,
"pfp",
"verified"
};
private String currentSearchQuery;
private final FetchListener<SuggestionModel[]> fetchListener = new FetchListener<SuggestionModel[]>() {
@Override
public void doBefore() {
suggestionAdapter.changeCursor(null);
}
@Override
public void onResult(final SuggestionModel[] result) {
final MatrixCursor cursor;
if (result == null) cursor = null;
else {
cursor = new MatrixCursor(COLUMNS, 0);
for (int i = 0; i < result.length; i++) {
final SuggestionModel suggestionModel = result[i];
if (suggestionModel != null) {
final SuggestionType suggestionType = suggestionModel.getSuggestionType();
final Object[] objects = {
i,
suggestionType == SuggestionType.TYPE_LOCATION ? suggestionModel.getName() : suggestionModel.getUsername(),
suggestionType == SuggestionType.TYPE_LOCATION ? suggestionModel.getUsername() : suggestionModel.getName(),
suggestionType,
suggestionModel.getProfilePic(),
suggestionModel.isVerified()};
if (!searchHash && !searchUser) cursor.addRow(objects);
else {
final boolean isCurrHash = suggestionType == SuggestionType.TYPE_HASHTAG;
if (searchHash && isCurrHash || !searchHash && !isCurrHash)
cursor.addRow(objects);
}
}
}
}
suggestionAdapter.changeCursor(cursor);
}
};
private final Runnable runnable = () -> {
cancelSuggestionsAsync();
if (TextUtils.isEmpty(currentSearchQuery)) {
suggestionAdapter.changeCursor(null);
return;
}
searchUser = currentSearchQuery.charAt(0) == '@';
searchHash = currentSearchQuery.charAt(0) == '#';
if (currentSearchQuery.length() == 1 && (searchHash || searchUser)) {
if (searchAutoComplete != null) {
searchAutoComplete.setThreshold(2);
}
} else {
if (searchAutoComplete != null) {
searchAutoComplete.setThreshold(1);
}
prevSuggestionAsync = new SuggestionsFetcher(fetchListener).executeOnExecutor(
AsyncTask.THREAD_POOL_EXECUTOR,
searchUser || searchHash ? currentSearchQuery.substring(1)
: currentSearchQuery);
}
};
private void cancelSuggestionsAsync() {
if (prevSuggestionAsync != null)
try {
prevSuggestionAsync.cancel(true);
} catch (final Exception ignored) {}
}
@Override
public boolean onQueryTextSubmit(final String query) {
return onQueryTextChange(query);
}
@Override
public boolean onQueryTextChange(final String query) {
suggestionsFetchHandler.removeCallbacks(runnable);
currentSearchQuery = query;
suggestionsFetchHandler.postDelayed(runnable, 800);
return true;
}
});
return true;
}
private void setupBottomNavigationBar(final boolean setDefaultFromSettings) {
int main_nav_ids = R.array.main_nav_ids;
final String cookie = settingsHelper.getString(Constants.COOKIE);
final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
if (!isLoggedIn) {
main_nav_ids = R.array.logged_out_main_nav_ids;
binding.bottomNavView.getMenu().clear();
binding.bottomNavView.inflateMenu(R.menu.logged_out_bottom_navigation_menu);
}
final TypedArray navIds = getResources().obtainTypedArray(main_nav_ids);
final List<Integer> mainNavList = new ArrayList<>(navIds.length());
final int length = navIds.length();
for (int i = 0; i < length; i++) {
final int resourceId = navIds.getResourceId(i, -1);
if (resourceId < 0) continue;
mainNavList.add(resourceId);
}
navIds.recycle();
if (setDefaultFromSettings || !isLoggedIn) {
final String defaultTabIdString = settingsHelper.getString(Constants.DEFAULT_TAB);
try {
final int defaultNavId = TextUtils.isEmpty(defaultTabIdString) || !isLoggedIn
? R.navigation.profile_nav_graph
: Integer.parseInt(defaultTabIdString);
final int index = mainNavList.indexOf(defaultNavId);
if (index >= 0) {
firstFragmentGraphIndex = index;
final Integer menuId = NAV_TO_MENU_ID_MAP.get(defaultNavId);
if (menuId != null) {
binding.bottomNavView.setSelectedItemId(menuId);
}
}
} catch (NumberFormatException e) {
Log.e(TAG, "Error parsing id", e);
}
}
final LiveData<NavController> navControllerLiveData = setupWithNavController(
binding.bottomNavView,
mainNavList,
getSupportFragmentManager(),
R.id.main_nav_host,
getIntent(),
firstFragmentGraphIndex);
navControllerLiveData.observe(this, this::setupNavigation);
currentNavControllerLiveData = navControllerLiveData;
}
private void setupNavigation(final NavController navController) {
NavigationUI.setupWithNavController(binding.toolbar, navController);
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
// below is a hack to check if we are at the end of the current stack, to setup the search view
binding.appBarLayout.setExpanded(true, true);
final int destinationId = destination.getId();
@SuppressLint("RestrictedApi") final Deque<NavBackStackEntry> backStack = navController.getBackStack();
setupMenu(backStack.size(), destinationId);
binding.bottomNavView.setVisibility(SHOW_BOTTOM_VIEW_DESTINATIONS.contains(destinationId) ? View.VISIBLE : View.GONE);
if (KEEP_SCROLL_BEHAVIOUR_DESTINATIONS.contains(destinationId)) {
setScrollingBehaviour();
} else {
removeScrollingBehaviour();
}
if (REMOVE_COLLAPSING_TOOLBAR_SCROLL_DESTINATIONS.contains(destinationId)) {
removeCollapsingToolbarScrollFlags();
} else {
setCollapsingToolbarScrollFlags();
}
});
}
private void setupMenu(final int backStackSize, final int destinationId) {
if (searchMenuItem == null) return;
if (backStackSize >= 2 && destinationId == R.id.profileFragment) {
showSearch = true;
searchMenuItem.setVisible(true);
return;
}
showSearch = false;
searchMenuItem.setVisible(false);
}
private void setScrollingBehaviour() {
final CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) binding.mainNavHost.getLayoutParams();
layoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
binding.mainNavHost.requestLayout();
}
private void removeScrollingBehaviour() {
final CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) binding.mainNavHost.getLayoutParams();
layoutParams.setBehavior(null);
binding.mainNavHost.requestLayout();
}
private void setCollapsingToolbarScrollFlags() {
final CollapsingToolbarLayout collapsingToolbarLayout = binding.collapsingToolbarLayout;
final AppBarLayout.LayoutParams toolbarLayoutLayoutParams = (AppBarLayout.LayoutParams) collapsingToolbarLayout.getLayoutParams();
toolbarLayoutLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
| AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP
| AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);
binding.collapsingToolbarLayout.requestLayout();
}
private void removeCollapsingToolbarScrollFlags() {
final CollapsingToolbarLayout collapsingToolbarLayout = binding.collapsingToolbarLayout;
final AppBarLayout.LayoutParams toolbarLayoutLayoutParams = (AppBarLayout.LayoutParams) collapsingToolbarLayout.getLayoutParams();
toolbarLayoutLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL);
binding.collapsingToolbarLayout.requestLayout();
}
private void handleIntent(final Intent intent) {
if (intent == null) return;
final String action = intent.getAction();
final String type = intent.getType();
// Log.d(TAG, action + " " + type);
if (Intent.ACTION_MAIN.equals(action)) return;
if (Constants.ACTION_SHOW_ACTIVITY.equals(action)) {
showActivityView();
return;
}
if (Intent.ACTION_SEND.equals(action) && type != null) {
if (type.equals("text/plain")) {
handleUrl(intent.getStringExtra(Intent.EXTRA_TEXT));
}
return;
}
if (Intent.ACTION_VIEW.equals(action)) {
final Uri data = intent.getData();
if (data == null) return;
handleUrl(data.toString());
}
}
private void handleUrl(final String url) {
if (url == null) return;
// Log.d(TAG, url);
final IntentModel intentModel = IntentUtils.parseUrl(url);
if (intentModel == null) return;
showView(intentModel);
}
private void showView(final IntentModel intentModel) {
switch (intentModel.getType()) {
case USERNAME:
showProfileView(intentModel);
break;
case POST:
showPostView(intentModel);
break;
case LOCATION:
showLocationView(intentModel);
break;
case HASHTAG:
showHashtagView(intentModel);
break;
case UNKNOWN:
default:
Log.w(TAG, "Unknown model type received!");
}
}
private void showProfileView(@NonNull final IntentModel intentModel) {
final String username = intentModel.getText();
// Log.d(TAG, "username: " + username);
final NavController navController = currentNavControllerLiveData.getValue();
if (currentNavControllerLiveData == null || navController == null) return;
final Bundle bundle = new Bundle();
bundle.putString("username", "@" + username);
navController.navigate(R.id.action_global_profileFragment, bundle);
}
private void showPostView(@NonNull final IntentModel intentModel) {
final String shortCode = intentModel.getText();
// Log.d(TAG, "shortCode: " + shortCode);
final NavController navController = currentNavControllerLiveData.getValue();
if (currentNavControllerLiveData == null || navController == null) return;
final Bundle bundle = new Bundle();
bundle.putStringArray("idOrCodeArray", new String[]{shortCode});
bundle.putInt("index", 0);
bundle.putBoolean("isId", false);
navController.navigate(R.id.action_global_postViewFragment, bundle);
}
private void showLocationView(@NonNull final IntentModel intentModel) {
final String locationId = intentModel.getText();
// Log.d(TAG, "locationId: " + locationId);
final NavController navController = currentNavControllerLiveData.getValue();
if (currentNavControllerLiveData == null || navController == null) return;
final Bundle bundle = new Bundle();
bundle.putString("locationId", locationId);
navController.navigate(R.id.action_global_locationFragment, bundle);
}
private void showHashtagView(@NonNull final IntentModel intentModel) {
final String hashtag = intentModel.getText();
// Log.d(TAG, "hashtag: " + hashtag);
final NavController navController = currentNavControllerLiveData.getValue();
if (currentNavControllerLiveData == null || navController == null) return;
final Bundle bundle = new Bundle();
bundle.putString("hashtag", "#" + hashtag);
navController.navigate(R.id.action_global_hashTagFragment, bundle);
}
private void showActivityView() {
binding.bottomNavView.setSelectedItemId(R.id.more_nav_graph);
binding.bottomNavView.post(() -> {
final NavController navController = currentNavControllerLiveData.getValue();
if (currentNavControllerLiveData == null || navController == null) return;
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToNotificationsViewer();
navController.navigate(navDirections);
});
}
private void bindActivityCheckerService() {
bindService(new Intent(this, ActivityCheckerService.class), serviceConnection, Context.BIND_AUTO_CREATE);
isActivityCheckerServiceBound = true;
}
private void unbindActivityCheckerService() {
if (!isActivityCheckerServiceBound) return;
unbindService(serviceConnection);
isActivityCheckerServiceBound = false;
}
}

View File

@ -1,189 +0,0 @@
package awais.instagrabber.activities;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.RelativeSizeSpan;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import awais.instagrabber.R;
import awais.instagrabber.adapters.NotificationsAdapter;
import awais.instagrabber.asyncs.NotificationsFetcher;
import awais.instagrabber.databinding.ActivityNotificationBinding;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.NotificationModel;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.notificationManager;
public final class NotificationsViewer extends BaseLanguageActivity implements SwipeRefreshLayout.OnRefreshListener {
private NotificationModel notificationModel;
private ActivityNotificationBinding notificationsBinding;
private ArrayAdapter<String> commmentDialogAdapter;
private String shortCode, postId, userId;
private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
private Resources resources;
String[] commentDialogList;
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
notificationManager.cancel(1800000000);
if (Utils.isEmpty(cookie)) {
Toast.makeText(this, R.string.activity_notloggedin, Toast.LENGTH_SHORT).show();
}
super.onCreate(savedInstanceState);
notificationsBinding = ActivityNotificationBinding.inflate(getLayoutInflater());
setContentView(notificationsBinding.getRoot());
notificationsBinding.swipeRefreshLayout.setOnRefreshListener(this);
resources = getResources();
setSupportActionBar(notificationsBinding.toolbar.toolbar);
notificationsBinding.toolbar.toolbar.setTitle(R.string.action_notif);
onRefresh();
}
@Override
public void onRefresh() {
notificationsBinding.swipeRefreshLayout.setRefreshing(true);
new NotificationsFetcher(new FetchListener<NotificationModel[]>() {
@Override
public void onResult(final NotificationModel[] notificationModels) {
notificationsBinding.rvComments.setAdapter(new NotificationsAdapter(notificationModels, clickListener, mentionClickListener));
notificationsBinding.swipeRefreshLayout.setRefreshing(false);
new SeenAction().execute();
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
if (which == 0)
searchUsername(notificationModel.getUsername());
else if (which == 1 && commentDialogList.length == 2)
startActivity(new Intent(getApplicationContext(), PostViewer.class)
.putExtra(Constants.EXTRAS_POST, new PostModel(notificationModel.getShortcode(), false)));
else if (which == 1) new ProfileAction().execute("/approve/");
else if (which == 2) new ProfileAction().execute("/ignore/");
};
private final View.OnClickListener clickListener = v -> {
final Object tag = v.getTag();
if (tag instanceof NotificationModel) {
notificationModel = (NotificationModel) tag;
final String username = notificationModel.getUsername();
final SpannableString title = new SpannableString(username + ":\n" + notificationModel.getText());
title.setSpan(new RelativeSizeSpan(1.23f), 0, username.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
if (notificationModel.getShortcode() != null) commentDialogList = new String[]{
resources.getString(R.string.open_profile),
resources.getString(R.string.view_post)
};
else if (notificationModel.getType() == NotificationType.REQUEST)
commentDialogList = new String[]{
resources.getString(R.string.open_profile),
resources.getString(R.string.request_approve),
resources.getString(R.string.request_reject)
};
else commentDialogList = new String[]{
resources.getString(R.string.open_profile)
};
commmentDialogAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, commentDialogList);
new AlertDialog.Builder(this).setTitle(title)
.setAdapter(commmentDialogAdapter, profileDialogListener)
.setNeutralButton(R.string.cancel, null)
.show();
}
};
private final MentionClickListener mentionClickListener = (view, text, isHashtag) ->
new AlertDialog.Builder(this).setTitle(text)
.setMessage(isHashtag ? R.string.comment_view_mention_hash_search : R.string.comment_view_mention_user_search)
.setNegativeButton(R.string.cancel, null).setPositiveButton(R.string.ok,
(dialog, which) -> searchUsername(text)).show();
private void searchUsername(final String text) {
startActivity(new Intent(getApplicationContext(), ProfileViewer.class).putExtra(Constants.EXTRAS_USERNAME, text));
}
class ProfileAction extends AsyncTask<String, Void, Void> {
boolean ok = false;
String action;
protected Void doInBackground(String... rawAction) {
action = rawAction[0];
final String url = "https://www.instagram.com/web/friendships/"+notificationModel.getId()+action;
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",
Utils.settingsHelper.getString(Constants.COOKIE).split("csrftoken=")[1].split(";")[0]);
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = true;
}
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", action+": " + ex);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if (ok == true) {
onRefresh();
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
class SeenAction extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... lmao) {
try {
final HttpURLConnection urlConnection =
(HttpURLConnection) new URL("https://www.instagram.com/web/activity/mark_checked/").openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);
urlConnection.setRequestProperty("x-csrftoken",
Utils.settingsHelper.getString(Constants.COOKIE).split("csrftoken=")[1].split(";")[0]);
final String urlParameters = "timestamp="+(System.currentTimeMillis()/1000);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", "" +
urlParameters.getBytes().length);
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
urlConnection.connect();
} catch (Throwable ex) {
Log.e("austin_debug", "seen: " + ex);
}
return null;
}
}
}

View File

@ -1,767 +0,0 @@
package awais.instagrabber.activities;
import android.annotation.SuppressLint;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.view.GestureDetectorCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
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 com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import org.json.JSONObject;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.PostsMediaAdapter;
import awais.instagrabber.asyncs.PostFetcher;
import awais.instagrabber.asyncs.ProfileFetcher;
import awais.instagrabber.asyncs.i.iPostFetcher;
import awais.instagrabber.customviews.CommentMentionClickSpan;
import awais.instagrabber.customviews.helpers.SwipeGestureListener;
import awais.instagrabber.databinding.ActivityViewerBinding;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.interfaces.SwipeEvent;
import awais.instagrabber.models.BasePostModel;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.ViewerPostModel;
import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.ItemGetType;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class PostViewer extends BaseLanguageActivity {
private ActivityViewerBinding viewerBinding;
private String url, prevUsername, commentsEndCursor;
private ProfileModel profileModel;
private BasePostModel postModel;
private ViewerPostModel viewerPostModel;
private SimpleExoPlayer player;
private ArrayAdapter<String> profileDialogAdapter;
private View viewsContainer, viewerCaptionParent;
private GestureDetectorCompat gestureDetector;
private SwipeEvent swipeEvent;
private CharSequence postCaption = null, postUserId;
private Resources resources;
private boolean session = false, isFromShare, liked, saved, ok = false;
private int slidePos = 0, lastSlidePos = 0;
private ItemGetType itemGetType;
@SuppressLint("ClickableViewAccessibility")
final View.OnTouchListener gestureTouchListener = new View.OnTouchListener() {
private float startX;
private float startY;
@Override
public boolean onTouch(final View v, final MotionEvent event) {
if (v == viewerCaptionParent) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX();
startY = event.getY();
break;
case MotionEvent.ACTION_UP:
if (!(Utils.isEmpty(postCaption) ||
Math.abs(startX - event.getX()) > 50 || Math.abs(startY - event.getY()) > 50)) {
Utils.copyText(PostViewer.this, postCaption);
return false;
}
}
}
return gestureDetector.onTouchEvent(event);
}
};
private final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
final String username = viewerPostModel.getUsername();
if (which == 0) {
searchUsername(username);
} else if (profileModel != null && which == 1) {
startActivity(new Intent(this, ProfilePicViewer.class)
.putExtra(Constants.EXTRAS_PROFILE, profileModel));
}
};
private final View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(final View v) {
if (v == viewerBinding.topPanel.ivProfilePic) {
new AlertDialog.Builder(PostViewer.this).setAdapter(profileDialogAdapter, profileDialogListener)
.setNeutralButton(R.string.cancel, null).setTitle(viewerPostModel.getUsername()).show();
} else if (v == viewerBinding.ivToggleFullScreen) {
toggleFullscreen();
final LinearLayout topPanelRoot = viewerBinding.topPanel.getRoot();
final int iconRes;
if (containerLayoutParams.weight != 3.3f) {
containerLayoutParams.weight = 3.3f;
iconRes = R.drawable.ic_fullscreen_exit;
topPanelRoot.setVisibility(View.GONE);
viewerBinding.btnDownload.setVisibility(View.VISIBLE);
viewerBinding.bottomPanel.tvPostDate.setVisibility(View.GONE);
} else {
containerLayoutParams.weight = (viewerBinding.mediaList.getVisibility() == View.VISIBLE) ? 1.35f : 1.9f;
containerLayoutParams.weight += (Utils.isEmpty(settingsHelper.getString(Constants.COOKIE))) ? 0.3f : 0;
iconRes = R.drawable.ic_fullscreen;
topPanelRoot.setVisibility(View.VISIBLE);
viewerBinding.btnDownload.setVisibility(View.GONE);
viewerBinding.bottomPanel.tvPostDate.setVisibility(View.VISIBLE);
}
viewerBinding.ivToggleFullScreen.setImageResource(iconRes);
viewerBinding.container.setLayoutParams(containerLayoutParams);
} else if (v == viewerBinding.bottomPanel.btnMute) {
if (player != null) {
final float intVol = player.getVolume() == 0f ? 1f : 0f;
player.setVolume(intVol);
viewerBinding.bottomPanel.btnMute.setImageResource(intVol == 0f ? R.drawable.mute : R.drawable.vol);
Utils.sessionVolumeFull = intVol == 1f;
}
} else if (v == viewerBinding.btnLike) {
new PostAction().execute("likes");
} else if (v == viewerBinding.btnBookmark) {
new PostAction().execute("save");
} else {
final Object tag = v.getTag();
if (tag instanceof ViewerPostModel) {
viewerPostModel = (ViewerPostModel) tag;
slidePos = Math.max(0, viewerPostModel.getPosition());
refreshPost();
}
}
}
};
private final View.OnClickListener downloadClickListener = v -> {
if (ContextCompat.checkSelfPermission(this, Utils.PERMS[0]) == PackageManager.PERMISSION_GRANTED)
showDownloadDialog();
else
ActivityCompat.requestPermissions(this, Utils.PERMS, 8020);
};
private final PostsMediaAdapter mediaAdapter = new PostsMediaAdapter(null, onClickListener);
private RequestManager glideRequestManager;
private LinearLayout.LayoutParams containerLayoutParams;
private final FetchListener<ViewerPostModel[]> pfl = result -> {
if (result == null || result.length < 1) {
Toast.makeText(this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
viewerPostModel = result[0];
mediaAdapter.setData(result);
if (result.length > 1) {
viewerBinding.mediaList.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, 0, 0.55f
));
containerLayoutParams.weight = 1.35f;
containerLayoutParams.weight += (Utils.isEmpty(settingsHelper.getString(Constants.COOKIE))) ? 0.3f : 0;
viewerBinding.container.setLayoutParams(containerLayoutParams);
viewerBinding.mediaList.setVisibility(View.VISIBLE);
}
viewerCaptionParent.setOnTouchListener(gestureTouchListener);
viewerBinding.playerView.setOnTouchListener(gestureTouchListener);
viewerBinding.imageViewer.setOnSingleFlingListener((e1, e2, velocityX, velocityY) -> {
final float diffX = e2.getX() - e1.getX();
if (Math.abs(diffX) > Math.abs(e2.getY() - e1.getY()) && Math.abs(diffX) > SwipeGestureListener.SWIPE_THRESHOLD
&& Math.abs(velocityX) > SwipeGestureListener.SWIPE_VELOCITY_THRESHOLD) {
swipeEvent.onSwipe(diffX > 0);
return true;
}
return false;
});
final long commentsCount = viewerPostModel.getCommentsCount();
viewerBinding.bottomPanel.commentsCount.setText(String.valueOf(commentsCount));
viewerBinding.bottomPanel.btnComments.setVisibility(View.VISIBLE);
viewerBinding.bottomPanel.btnComments.setOnClickListener(v ->
startActivityForResult(new Intent(this, CommentsViewer.class)
.putExtra(Constants.EXTRAS_SHORTCODE, postModel.getShortCode())
.putExtra(Constants.EXTRAS_POST, viewerPostModel.getPostId())
.putExtra(Constants.EXTRAS_USER, postUserId), 6969));
viewerBinding.bottomPanel.btnComments.setClickable(true);
viewerBinding.bottomPanel.btnComments.setEnabled(true);
if (postModel instanceof PostModel) {
final PostModel postModel = (PostModel) this.postModel;
postModel.setPostId(viewerPostModel.getPostId());
postModel.setTimestamp(viewerPostModel.getTimestamp());
postModel.setPostCaption(viewerPostModel.getPostCaption());
if (!ok) {
liked = viewerPostModel.getLike();
saved = viewerPostModel.getBookmark();
}
}
setupPostInfoBar("@"+viewerPostModel.getUsername(), viewerPostModel.getItemType(),
viewerPostModel.getLocationName(), viewerPostModel.getLocation());
postCaption = postModel.getPostCaption();
viewerCaptionParent.setVisibility(View.VISIBLE);
viewerBinding.bottomPanel.btnDownload.setOnClickListener(downloadClickListener);
refreshPost();
};
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewerBinding = ActivityViewerBinding.inflate(getLayoutInflater());
setContentView(viewerBinding.getRoot());
glideRequestManager = Glide.with(this);
final Intent intent = getIntent();
if (intent == null || !intent.hasExtra(Constants.EXTRAS_POST)
|| (postModel = (PostModel) intent.getSerializableExtra(Constants.EXTRAS_POST)) == null) {
Utils.errorFinish(this);
return;
}
containerLayoutParams = (LinearLayout.LayoutParams) viewerBinding.container.getLayoutParams();
if (intent.hasExtra(Constants.EXTRAS_TYPE))
itemGetType = (ItemGetType) intent.getSerializableExtra(Constants.EXTRAS_TYPE);
resources = getResources();
viewerBinding.topPanel.title.setMovementMethod(new LinkMovementMethod());
viewerBinding.topPanel.title.setMentionClickListener((view, text, isHashtag) -> searchUsername(text));
viewerBinding.topPanel.ivProfilePic.setOnClickListener(onClickListener);
viewerBinding.ivToggleFullScreen.setOnClickListener(onClickListener);
if (Utils.isEmpty(settingsHelper.getString(Constants.COOKIE))) {
viewerBinding.btnLike.setVisibility(View.GONE);
viewerBinding.btnBookmark.setVisibility(View.GONE);
viewerBinding.postActions.setVisibility(View.GONE);
viewerBinding.postActions.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, 0, 0
));
containerLayoutParams.weight = (containerLayoutParams.weight == 3.3f) ? 3.3f : 2.2f;
viewerBinding.container.setLayoutParams(containerLayoutParams);
}
else {
viewerBinding.btnLike.setOnClickListener(onClickListener);
viewerBinding.btnBookmark.setOnClickListener(onClickListener);
}
viewerBinding.btnDownload.setOnClickListener(downloadClickListener);
profileDialogAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
new String[]{resources.getString(R.string.open_profile), resources.getString(R.string.view_pfp)});
postModel.setPosition(intent.getIntExtra(Constants.EXTRAS_INDEX, -1));
final boolean postIdNull = postModel.getPostId() == null;
if (!postIdNull)
setupPostInfoBar(intent.getStringExtra(Constants.EXTRAS_USER), postModel.getItemType(), null, null);
isFromShare = postModel.getPosition() == -1 || postIdNull;
viewerCaptionParent = (View) viewerBinding.bottomPanel.viewerCaption.getParent();
viewsContainer = (View) viewerBinding.bottomPanel.tvVideoViews.getParent();
viewerBinding.mediaList.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false));
viewerBinding.mediaList.setAdapter(mediaAdapter);
viewerBinding.mediaList.setVisibility(View.GONE);
swipeEvent = isRight -> {
final List<? extends BasePostModel> itemGetterItems;
final boolean isMainSwipe;
if (itemGetType == ItemGetType.SAVED_ITEMS && SavedViewer.itemGetter != null) {
itemGetterItems = SavedViewer.itemGetter.get(itemGetType);
isMainSwipe = !(itemGetterItems.size() < 1 || itemGetType == ItemGetType.SAVED_ITEMS && isFromShare);
}
else if (itemGetType != null && Main.itemGetter != null) {
itemGetterItems = Main.itemGetter.get(itemGetType);
isMainSwipe = !(itemGetterItems.size() < 1 || itemGetType == ItemGetType.MAIN_ITEMS && isFromShare);
} else {
itemGetterItems = null;
isMainSwipe = false;
}
final BasePostModel[] basePostModels = mediaAdapter.getPostModels();
final int slides = basePostModels.length;
int position = postModel.getPosition();
if (isRight) {
--slidePos;
if (!isMainSwipe && slidePos < 0) slidePos = 0;
if (slides > 0 && slidePos >= 0) {
if (basePostModels[slidePos] instanceof ViewerPostModel) {
viewerPostModel = (ViewerPostModel) basePostModels[slidePos];
}
refreshPost();
return;
}
if (isMainSwipe && --position < 0) position = itemGetterItems.size() - 1;
} else {
++slidePos;
if (!isMainSwipe && slidePos >= slides) slidePos = slides - 1;
if (slides > 0 && slidePos < slides) {
if (basePostModels[slidePos] instanceof ViewerPostModel) {
viewerPostModel = (ViewerPostModel) basePostModels[slidePos];
}
refreshPost();
return;
}
if (isMainSwipe && ++position >= itemGetterItems.size()) position = 0;
}
if (isMainSwipe) {
slidePos = 0;
ok = false;
Log.d("AWAISKING_APP", "swipe left <<< post[" + position + "]: " + postModel + " -- " + slides);
postModel = itemGetterItems.get(position);
postModel.setPosition(position);
viewPost();
}
};
gestureDetector = new GestureDetectorCompat(this, new SwipeGestureListener(swipeEvent));
viewPost();
}
private void viewPost() {
lastSlidePos = 0;
mediaAdapter.setData(null);
viewsContainer.setVisibility(View.GONE);
viewerCaptionParent.setVisibility(View.GONE);
viewerBinding.mediaList.setVisibility(View.GONE);
viewerBinding.btnDownload.setVisibility(View.GONE);
viewerBinding.bottomPanel.btnMute.setVisibility(View.GONE);
viewerBinding.bottomPanel.tvPostDate.setVisibility(View.GONE);
viewerBinding.bottomPanel.btnComments.setVisibility(View.GONE);
viewerBinding.bottomPanel.btnDownload.setVisibility(View.INVISIBLE);
viewerBinding.bottomPanel.viewerCaption.setText(null);
viewerBinding.bottomPanel.viewerCaption.setMentionClickListener(null);
viewerBinding.playerView.setVisibility(View.GONE);
viewerBinding.playerView.setPlayer(null);
viewerBinding.imageViewer.setImageResource(0);
viewerBinding.imageViewer.setImageDrawable(null);
if (postModel.getShortCode() != null)
new PostFetcher(postModel.getShortCode(), pfl).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else if (postModel.getPostId() != null)
new iPostFetcher(postModel.getPostId(), pfl).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void searchUsername(final String text) {
startActivity(
new Intent(getApplicationContext(), ProfileViewer.class)
.putExtra(Constants.EXTRAS_USERNAME, text)
);
}
private void setupVideo() {
viewerBinding.playerView.setVisibility(View.VISIBLE);
viewerBinding.bottomPanel.btnDownload.setVisibility(View.VISIBLE);
viewerBinding.bottomPanel.btnMute.setVisibility(View.VISIBLE);
viewerBinding.progressView.setVisibility(View.GONE);
viewerBinding.imageViewer.setVisibility(View.GONE);
viewerBinding.imageViewer.setImageDrawable(null);
if (viewerPostModel.getVideoViews() > -1) {
viewsContainer.setVisibility(View.VISIBLE);
viewerBinding.bottomPanel.tvVideoViews.setText(String.valueOf(viewerPostModel.getVideoViews()));
}
player = new SimpleExoPlayer.Builder(this).build();
viewerBinding.playerView.setPlayer(player);
float vol = Utils.settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;
player.setVolume(vol);
player.setPlayWhenReady(Utils.settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));
final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(new DefaultDataSourceFactory(this, "instagram"))
.createMediaSource(Uri.parse(url));
mediaSource.addEventListener(new Handler(), new MediaSourceEventListener() {
@Override
public void onLoadCompleted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
viewerBinding.progressView.setVisibility(View.GONE);
}
@Override
public void onLoadStarted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
viewerBinding.progressView.setVisibility(View.VISIBLE);
}
@Override
public void onLoadCanceled(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
viewerBinding.progressView.setVisibility(View.GONE);
}
@Override
public void onLoadError(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData, final IOException error, final boolean wasCanceled) {
viewerBinding.progressView.setVisibility(View.GONE);
}
});
player.prepare(mediaSource);
player.setVolume(vol);
viewerBinding.bottomPanel.btnMute.setImageResource(vol == 0f ? R.drawable.vol : R.drawable.mute);
viewerBinding.bottomPanel.btnMute.setOnClickListener(onClickListener);
}
private void setupImage() {
viewsContainer.setVisibility(View.GONE);
viewerBinding.playerView.setVisibility(View.GONE);
viewerBinding.progressView.setVisibility(View.VISIBLE);
viewerBinding.bottomPanel.btnMute.setVisibility(View.GONE);
viewerBinding.bottomPanel.btnDownload.setVisibility(View.VISIBLE);
viewerBinding.imageViewer.setImageDrawable(null);
viewerBinding.imageViewer.setVisibility(View.VISIBLE);
viewerBinding.imageViewer.setZoomable(true);
viewerBinding.imageViewer.setZoomTransitionDuration(420);
viewerBinding.imageViewer.setMaximumScale(7.2f);
glideRequestManager.load(url).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable final GlideException e, final Object model, final Target<Drawable> target, final boolean isFirstResource) {
viewerBinding.progressView.setVisibility(View.GONE);
return false;
}
@Override
public boolean onResourceReady(final Drawable resource, final Object model, final Target<Drawable> target, final DataSource dataSource, final boolean isFirstResource) {
viewerBinding.progressView.setVisibility(View.GONE);
return false;
}
}).into(viewerBinding.imageViewer);
}
private void showDownloadDialog() {
final ArrayList<BasePostModel> postModels = new ArrayList<>();
if (!session && viewerBinding.mediaList.getVisibility() == View.VISIBLE) {
final DialogInterface.OnClickListener clickListener = (dialog, which) -> {
postModels.clear();
if (which == DialogInterface.BUTTON_NEGATIVE) {
final BasePostModel[] adapterPostModels = mediaAdapter.getPostModels();
for (int i = 0, size = mediaAdapter.getItemCount(); i < size; ++i) {
if (adapterPostModels[i] instanceof ViewerPostModel)
postModels.add(adapterPostModels[i]);
}
} else if (which == DialogInterface.BUTTON_POSITIVE) {
postModels.add(viewerPostModel);
} else {
session = true;
postModels.add(viewerPostModel);
}
if (postModels.size() > 0)
Utils.batchDownload(this, viewerPostModel.getUsername(), DownloadMethod.DOWNLOAD_POST_VIEWER, postModels);
};
new AlertDialog.Builder(this).setTitle(R.string.post_viewer_download_dialog_title)
.setMessage(R.string.post_viewer_download_message)
.setNeutralButton(R.string.post_viewer_download_session, clickListener).setPositiveButton(R.string.post_viewer_download_current, clickListener)
.setNegativeButton(R.string.post_viewer_download_album, clickListener).show();
} else {
Utils.batchDownload(this, viewerPostModel.getUsername(), DownloadMethod.DOWNLOAD_POST_VIEWER, Collections.singletonList(viewerPostModel));
}
}
@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 8020 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
showDownloadDialog();
}
@Override
protected void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == 6969) {
setResult(RESULT_OK);
finish();
}
}
@Override
public void onPause() {
super.onPause();
if (Build.VERSION.SDK_INT < 24) releasePlayer();
}
@Override
public void onStop() {
super.onStop();
if (Build.VERSION.SDK_INT >= 24) releasePlayer();
}
@Override
protected void onResume() {
super.onResume();
if (player == null && viewerPostModel != null && viewerPostModel.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO)
setupVideo();
else if (player != null) {
player.setPlayWhenReady(true);
player.getPlaybackState();
}
}
private void refreshPost() {
if (containerLayoutParams.weight != 3.3f) {
containerLayoutParams.weight = (viewerBinding.mediaList.getVisibility() == View.VISIBLE) ? 1.35f : 1.9f;
viewerBinding.container.setLayoutParams(containerLayoutParams);
}
if (viewerBinding.mediaList.getVisibility() == View.VISIBLE) {
ViewerPostModel item = mediaAdapter.getItemAt(lastSlidePos);
if (item != null) {
item.setCurrentSlide(false);
mediaAdapter.notifyItemChanged(lastSlidePos, item);
}
item = mediaAdapter.getItemAt(slidePos);
if (item != null) {
item.setCurrentSlide(true);
mediaAdapter.notifyItemChanged(slidePos, item);
}
}
lastSlidePos = slidePos;
postCaption = viewerPostModel.getPostCaption();
if (Utils.hasMentions(postCaption)) {
viewerBinding.bottomPanel.viewerCaption.setText(Utils.getMentionText(postCaption), TextView.BufferType.SPANNABLE);
viewerBinding.bottomPanel.viewerCaption.setMentionClickListener((view, text, isHashtag) -> searchUsername(text));
} else {
viewerBinding.bottomPanel.viewerCaption.setMentionClickListener(null);
viewerBinding.bottomPanel.viewerCaption.setText(postCaption);
}
setupPostInfoBar("@"+viewerPostModel.getUsername(), viewerPostModel.getItemType(),
viewerPostModel.getLocationName(), viewerPostModel.getLocation());
if (postModel instanceof PostModel) {
final PostModel postModel = (PostModel) this.postModel;
postModel.setPostId(viewerPostModel.getPostId());
postModel.setTimestamp(viewerPostModel.getTimestamp());
postModel.setPostCaption(viewerPostModel.getPostCaption());
if (liked == true) {
viewerBinding.btnLike.setText(resources.getString(R.string.unlike, viewerPostModel.getLikes()
+ ((ok && viewerPostModel.getLike() != liked) ? (liked ? 1L : -1L) : 0L)));
viewerBinding.btnLike.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(
getApplicationContext(), R.color.btn_pink_background)));
}
else {
viewerBinding.btnLike.setText(resources.getString(R.string.like, viewerPostModel.getLikes()
+ ((ok && viewerPostModel.getLike() != liked) ? (liked ? 1L : -1L) : 0L)));
viewerBinding.btnLike.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(
getApplicationContext(), R.color.btn_lightpink_background)));
}
if (saved == true) {
viewerBinding.btnBookmark.setText(R.string.unbookmark);
viewerBinding.btnBookmark.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(
getApplicationContext(), R.color.btn_orange_background)));
}
else {
viewerBinding.btnBookmark.setText(R.string.bookmark);
viewerBinding.btnBookmark.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(
getApplicationContext(), R.color.btn_lightorange_background)));
}
}
viewerBinding.bottomPanel.tvPostDate.setText(viewerPostModel.getPostDate());
viewerBinding.bottomPanel.tvPostDate.setVisibility(containerLayoutParams.weight != 3.3f ? View.VISIBLE : View.GONE);
viewerBinding.bottomPanel.tvPostDate.setSelected(true);
url = viewerPostModel.getDisplayUrl();
releasePlayer();
viewerBinding.btnDownload.setVisibility(containerLayoutParams.weight == 3.3f ? View.VISIBLE : View.GONE);
if (viewerPostModel.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) setupVideo();
else setupImage();
}
private void releasePlayer() {
if (player != null) {
player.release();
player = null;
}
}
private void setupPostInfoBar(final String from, final MediaItemType mediaItemType, final String locationName, final String location) {
if (prevUsername == null || !prevUsername.equals(from)) {
viewerBinding.topPanel.ivProfilePic.setImageBitmap(null);
viewerBinding.topPanel.ivProfilePic.setImageDrawable(null);
viewerBinding.topPanel.ivProfilePic.setImageResource(0);
if (!Utils.isEmpty(from) && from.charAt(0) == '@')
new ProfileFetcher(from.substring(1), result -> {
profileModel = result;
if (result != null) {
final String hdProfilePic = result.getHdProfilePic();
final String sdProfilePic = result.getSdProfilePic();
postUserId = result.getId();
final boolean hdPicEmpty = Utils.isEmpty(hdProfilePic);
RequestListener<Drawable> profilePicListener = new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable final GlideException e, final Object model, final Target<Drawable> target, final boolean isFirstResource) {
viewerBinding.topPanel.ivProfilePic.setEnabled(false);
viewerBinding.topPanel.ivProfilePic.setOnClickListener(null);
return true;
}
@Override
public boolean onResourceReady(final Drawable resource, final Object model, final Target<Drawable> target, final DataSource dataSource, final boolean isFirstResource) {
viewerBinding.topPanel.ivProfilePic.setEnabled(true);
viewerBinding.topPanel.ivProfilePic.setOnClickListener(onClickListener);
return false;
}
};
glideRequestManager.load(hdPicEmpty ? sdProfilePic : hdProfilePic).listener(profilePicListener)
.error(glideRequestManager.load(sdProfilePic).listener(profilePicListener)).into(viewerBinding.topPanel.ivProfilePic);
final View viewStoryPost = findViewById(R.id.viewStoryPost);
if (viewStoryPost != null) {
viewStoryPost.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (result.isPrivate())
Toast.makeText(getApplicationContext(), R.string.share_private_post, Toast.LENGTH_LONG).show();
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, "https://instagram.com/p/"+postModel.getShortCode());
startActivity(Intent.createChooser(sharingIntent,
(result.isPrivate()) ? getString(R.string.share_private_post) : getString(R.string.share_public_post)));
}
});
}
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
prevUsername = from;
}
final String titlePrefix = resources.getString(mediaItemType == MediaItemType.MEDIA_TYPE_VIDEO ?
R.string.post_viewer_video_post : R.string.post_viewer_image_post);
if (Utils.isEmpty(from)) viewerBinding.topPanel.title.setText(titlePrefix);
else {
final int titleLen = from.length();
final SpannableString spannableString = new SpannableString(from);
spannableString.setSpan(new CommentMentionClickSpan(), 0, titleLen, 0);
viewerBinding.topPanel.title.setText(spannableString);
}
if (location == null) {
viewerBinding.topPanel.location.setVisibility(View.GONE);
viewerBinding.topPanel.title.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT
));
}
else {
viewerBinding.topPanel.location.setVisibility(View.VISIBLE);
viewerBinding.topPanel.location.setText(locationName);
viewerBinding.topPanel.location.setOnClickListener(v -> searchUsername(location));
viewerBinding.topPanel.title.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT
));
}
}
private void toggleFullscreen() {
final View decorView = getWindow().getDecorView();
int newUiOptions = decorView.getSystemUiVisibility();
newUiOptions ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
newUiOptions ^= View.SYSTEM_UI_FLAG_FULLSCREEN;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
newUiOptions ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(newUiOptions);
}
class PostAction extends AsyncTask<String, Void, Void> {
String action;
protected Void doInBackground(String... rawAction) {
action = rawAction[0];
final String url = "https://www.instagram.com/web/"+action+"/"+postModel.getPostId()+"/"+ (action == "save" ?
(saved ? "unsave/" : "save/") :
(liked ? "unlike/" : "like/"));
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",
settingsHelper.getString(Constants.COOKIE).split("csrftoken=")[1].split(";")[0]);
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = true;
}
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", action+": " + ex);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if (ok == true && action == "likes") {
liked = !liked;
refreshPost();
}
else if (ok == true && action == "save") {
saved = !saved;
refreshPost();
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -1,211 +0,0 @@
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<String> 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<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable final GlideException e, final Object model, final Target<Drawable> 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);
}
profileBinding.progressView.setVisibility(View.GONE);
return false;
}
@Override
public boolean onResourceReady(final Drawable resource, final Object model, final Target<Drawable> 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);
}
}
}).error(glideRequestManager.load(profilePicUrl)).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;
}
}

View File

@ -1,930 +0,0 @@
package awais.instagrabber.activities;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
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.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
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.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.RamboTextView;
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
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.DownloadMethod;
import awais.instagrabber.models.enums.ItemGetType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DataBox;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Constants.AUTOLOAD_POSTS;
import static awais.instagrabber.utils.Utils.logCollector;
public final class ProfileViewer extends BaseLanguageActivity implements SwipeRefreshLayout.OnRefreshListener {
private final ArrayList<PostModel> 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 StoryModel[] storyModels;
private MenuItem downloadAction, favouriteAction;
private final FetchListener<PostModel[]> postsFetchListener = new FetchListener<PostModel[]>() {
@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) {
startActivity(new Intent(getApplicationContext(), ProfileViewer.class).putExtra(Constants.EXTRAS_USERNAME, text));
}
};
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;
private ArrayAdapter<String> profileDialogAdapter;
private DialogInterface.OnClickListener profileDialogListener;
protected void onCreate(@Nullable final Bundle savedInstanceState) {
stopCurrentExecutor();
super.onCreate(savedInstanceState);
final Intent intent = getIntent();
if (intent == null || !intent.hasExtra(Constants.EXTRAS_USERNAME)
|| Utils.isEmpty((userQuery = intent.getStringExtra(Constants.EXTRAS_USERNAME)))) {
Utils.errorFinish(this);
return;
}
userQuery = (userQuery.contains("/") || userQuery.startsWith("#") || userQuery.startsWith("@")) ? userQuery : ("@"+userQuery);
profileBinding = ActivityProfileBinding.inflate(getLayoutInflater());
setContentView(profileBinding.getRoot());
resources = getResources();
profileDialogAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
new String[]{resources.getString(R.string.view_pfp), resources.getString(R.string.show_stories)});
profileDialogListener = (dialog, which) -> {
final Intent newintent;
if (which == 0 || storyModels == null || storyModels.length < 1) {
newintent = 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)));
}
else newintent = new Intent(this, StoryViewer.class).putExtra(Constants.EXTRAS_USERNAME, userQuery.replace("@", ""))
.putExtra(Constants.EXTRAS_STORIES, storyModels)
.putExtra(Constants.EXTRAS_HASHTAG, (hashtagModel != null));
startActivity(newintent);
};
profileBinding.profileView.swipeRefreshLayout.setOnRefreshListener(this);
profileBinding.profileView.mainUrl.setMovementMethod(new LinkMovementMethod());
isLoggedIn = !Utils.isEmpty(cookie);
collapsingToolbar = profileBinding.profileView.appBarLayout.getChildAt(0);
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));
}
}, v -> { // long click listener
final Object tag = v.getTag();
if (tag instanceof PostModel) {
postsAdapter.isSelecting = true;
toggleSelection((PostModel) tag);
}
return true;
}));
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);
final View.OnClickListener onClickListener = v -> {
if (v == profileBinding.profileView.mainBiography) {
Utils.copyText(this, profileBinding.profileView.mainBiography.getText().toString());
} else if (v == profileBinding.profileView.locationBiography) {
Utils.copyText(this, profileBinding.profileView.locationBiography.getText().toString());
} else if (v == profileBinding.profileView.mainProfileImage || v == profileBinding.profileView.mainHashtagImage || v == profileBinding.profileView.mainLocationImage) {
if (storyModels == null || storyModels.length <= 0) {
profileDialogListener.onClick(null, 0);
} else {
// because sometimes configuration changes made this crash on some phones
new AlertDialog.Builder(this).setAdapter(profileDialogAdapter, profileDialogListener)
.setNeutralButton(R.string.cancel, null).show();
}
}
};
profileBinding.profileView.mainBiography.setOnClickListener(onClickListener);
profileBinding.profileView.locationBiography.setOnClickListener(onClickListener);
profileBinding.profileView.mainProfileImage.setOnClickListener(onClickListener);
profileBinding.profileView.mainHashtagImage.setOnClickListener(onClickListener);
profileBinding.profileView.mainLocationImage.setOnClickListener(onClickListener);
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);
}
}
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.saved, menu);
downloadAction = menu.findItem(R.id.downloadAction);
downloadAction.setVisible(false);
favouriteAction = menu.findItem(R.id.favouriteAction);
favouriteAction.setVisible(!Utils.isEmpty(cookie));
favouriteAction.setIcon(Utils.dataBox.getFavorite(userQuery) == null ? R.drawable.ic_not_liked : R.drawable.ic_like);
downloadAction.setOnMenuItemClickListener(item -> {
if (selectedItems.size() > 0) {
Utils.batchDownload(this, userQuery, DownloadMethod.DOWNLOAD_MAIN, selectedItems);
}
return true;
});
favouriteAction.setOnMenuItemClickListener(item -> {
if (Utils.dataBox.getFavorite(userQuery) == null) {
Utils.dataBox.addFavorite(new DataBox.FavoriteModel(userQuery, System.currentTimeMillis(),
locationModel != null ? locationModel.getName() : userQuery.replaceAll("^@", "")));
favouriteAction.setIcon(R.drawable.ic_like);
}
else {
Utils.dataBox.delFavorite(new DataBox.FavoriteModel(userQuery,
Long.parseLong(Utils.dataBox.getFavorite(userQuery).split("/")[1]),
locationModel != null ? locationModel.getName() : userQuery.replaceAll("^@", "")));
favouriteAction.setIcon(R.drawable.ic_not_liked);
}
return true;
});
return true;
}
private void toggleSelection(final PostModel postModel) {
if (postModel != null && postsAdapter != null) {
if (postModel.isSelected()) selectedItems.remove(postModel);
else if (selectedItems.size() >= 100) {
Toast.makeText(ProfileViewer.this, R.string.downloader_too_many, Toast.LENGTH_SHORT);
return;
}
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);
}
@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 8020 && grantResults[0] == PackageManager.PERMISSION_GRANTED && selectedItems.size() > 0)
Utils.batchDownload(this, userQuery, DownloadMethod.DOWNLOAD_MAIN, selectedItems);
}
public void deselectSelection(final BasePostModel postModel) {
if (postModel instanceof PostModel) {
selectedItems.remove(postModel);
postModel.setSelected(false);
if (postsAdapter != null) notifyAdapter((PostModel) postModel);
}
}
class MyTask extends AsyncTask<Void, Bitmap, Void> {
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());
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<String, Void, Void> {
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", "" +
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();
}
}
}
}

View File

@ -1,250 +0,0 @@
package awais.instagrabber.activities;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.util.ArrayList;
import java.util.Arrays;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.adapters.PostsAdapter;
import awais.instagrabber.asyncs.PostsFetcher;
import awais.instagrabber.asyncs.i.iLikedFetcher;
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.ActivitySavedBinding;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.interfaces.ItemGetter;
import awais.instagrabber.models.BasePostModel;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.ItemGetType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Constants.AUTOLOAD_POSTS;
import static awais.instagrabber.utils.Utils.logCollector;
public final class SavedViewer extends BaseLanguageActivity implements SwipeRefreshLayout.OnRefreshListener {
private static AsyncTask<?, ?, ?> currentlyExecuting;
public static ItemGetter itemGetter;
private PostsAdapter postsAdapter;
private boolean hasNextPage, autoloadPosts;
//private CommentModel commentModel;
private ActivitySavedBinding savedBinding;
private String action, username, endCursor;
private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
private RecyclerLazyLoader lazyLoader;
private Resources resources;
private ArrayList<PostModel> selectedItems = new ArrayList<>();
private final ArrayList<PostModel> allItems = new ArrayList<>();
private MenuItem downloadAction;
private final FetchListener<PostModel[]> postsFetchListener = new FetchListener<PostModel[]>() {
@Override
public void onResult(final PostModel[] result) {
final int oldSize = allItems.size();
if (result != null && result.length > 0) {
allItems.addAll(Arrays.asList(result));
postsAdapter.notifyItemRangeInserted(oldSize, result.length);
savedBinding.mainPosts.post(() -> {
savedBinding.mainPosts.setNestedScrollingEnabled(true);
savedBinding.mainPosts.setVisibility(View.VISIBLE);
});
final PostModel model = result[result.length - 1];
if (model != null) {
endCursor = model.getEndCursor();
hasNextPage = model.hasNextPage();
if (autoloadPosts && hasNextPage && action.charAt(0) == '^')
currentlyExecuting = new iLikedFetcher(endCursor, postsFetchListener)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else if (autoloadPosts && hasNextPage)
currentlyExecuting = new PostsFetcher(action, endCursor, this)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else {
savedBinding.swipeRefreshLayout.setRefreshing(false);
}
model.setPageCursor(false, null);
}
}
else {
savedBinding.swipeRefreshLayout.setRefreshing(false);
if (oldSize == 0) {
Toast.makeText(getApplicationContext(), R.string.empty_list, Toast.LENGTH_SHORT).show();
finish();
}
}
}
};
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
savedBinding = ActivitySavedBinding.inflate(getLayoutInflater());
setContentView(savedBinding.getRoot());
savedBinding.swipeRefreshLayout.setOnRefreshListener(this);
autoloadPosts = Utils.settingsHelper.getBoolean(AUTOLOAD_POSTS);
savedBinding.mainPosts.setNestedScrollingEnabled(false);
final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(this, Utils.convertDpToPx(110));
savedBinding.mainPosts.setLayoutManager(layoutManager);
savedBinding.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
final Intent intent = getIntent();
if (intent == null || !intent.hasExtra(Constants.EXTRAS_INDEX)
|| Utils.isEmpty((action = intent.getStringExtra(Constants.EXTRAS_INDEX)))
|| !intent.hasExtra(Constants.EXTRAS_USER)
|| Utils.isEmpty((username = intent.getStringExtra(Constants.EXTRAS_USER)))) {
Utils.errorFinish(this);
return;
}
savedBinding.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(this, PostViewer.class)
.putExtra(Constants.EXTRAS_INDEX, postModel.getPosition())
.putExtra(Constants.EXTRAS_POST, postModel)
.putExtra(Constants.EXTRAS_USER, username)
.putExtra(Constants.EXTRAS_TYPE, ItemGetType.SAVED_ITEMS));
}
}, v -> {
final Object tag = v.getTag();
if (tag instanceof PostModel) {
postsAdapter.isSelecting = true;
toggleSelection((PostModel) tag);
}
return true;
}));
savedBinding.swipeRefreshLayout.setRefreshing(true);
setSupportActionBar(savedBinding.toolbar.toolbar);
savedBinding.toolbar.toolbar.setTitle((action.charAt(0) == '$' ? R.string.saved :
(action.charAt(0) == '%' ? R.string.tagged : R.string.liked)));
savedBinding.toolbar.toolbar.setSubtitle(username);
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
if (!autoloadPosts && hasNextPage) {
savedBinding.swipeRefreshLayout.setRefreshing(true);
stopCurrentExecutor();
currentlyExecuting = action.charAt(0) == '^'
? new iLikedFetcher(endCursor, postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
: new PostsFetcher(action, endCursor, postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
endCursor = null;
}
});
savedBinding.mainPosts.addOnScrollListener(lazyLoader);
itemGetter = itemGetType -> {
if (itemGetType == ItemGetType.SAVED_ITEMS) return allItems;
return null;
};
if (action.charAt(0) == '^') new iLikedFetcher(postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else new PostsFetcher(action, postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.saved, menu);
downloadAction = menu.findItem(R.id.downloadAction);
downloadAction.setVisible(false);
menu.findItem(R.id.favouriteAction).setVisible(false);
downloadAction.setOnMenuItemClickListener(item -> {
if (selectedItems.size() > 0) {
Utils.batchDownload(this, null, DownloadMethod.DOWNLOAD_SAVED, selectedItems);
}
return true;
});
return true;
}
public void deselectSelection(final BasePostModel postModel) {
if (postModel instanceof PostModel) {
selectedItems.remove(postModel);
postModel.setSelected(false);
if (postsAdapter != null) notifyAdapter((PostModel) postModel);
}
}
@Override
public void onRefresh() {
if (lazyLoader != null) lazyLoader.resetState();
stopCurrentExecutor();
allItems.clear();
selectedItems.clear();
if (postsAdapter != null) {
postsAdapter.isSelecting = false;
postsAdapter.notifyDataSetChanged();
}
savedBinding.swipeRefreshLayout.setRefreshing(true);
if (action.charAt(0) == '^') new iLikedFetcher(postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else new PostsFetcher(action, postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 8020 && grantResults[0] == PackageManager.PERMISSION_GRANTED && selectedItems.size() > 0)
Utils.batchDownload(this, null, DownloadMethod.DOWNLOAD_SAVED, selectedItems);
}
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 if (selectedItems.size() >= 100) {
Toast.makeText(SavedViewer.this, R.string.downloader_too_many, Toast.LENGTH_SHORT);
return;
}
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);
}
}
}

View File

@ -1,781 +0,0 @@
package awais.instagrabber.activities;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.util.Pair;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.view.GestureDetectorCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.bumptech.glide.Glide;
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 com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import org.json.JSONObject;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;
import java.util.UUID;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.adapters.StoriesAdapter;
import awais.instagrabber.asyncs.DownloadAsync;
import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster;
import awais.instagrabber.asyncs.i.iStoryStatusFetcher;
import awais.instagrabber.customviews.helpers.SwipeGestureListener;
import awais.instagrabber.databinding.ActivityStoryViewerBinding;
import awais.instagrabber.interfaces.SwipeEvent;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.stickers.PollModel;
import awais.instagrabber.models.stickers.QuestionModel;
import awais.instagrabber.models.stickers.QuizModel;
import awais.instagrabber.models.stickers.SwipeUpModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_THRESHOLD;
import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_VELOCITY_THRESHOLD;
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
import static awais.instagrabber.utils.Constants.MARK_AS_SEEN;
import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class StoryViewer extends BaseLanguageActivity {
private final StoriesAdapter storiesAdapter = new StoriesAdapter(null, new View.OnClickListener() {
@Override
public void onClick(final View v) {
final Object tag = v.getTag();
if (tag instanceof StoryModel) {
currentStory = (StoryModel) tag;
slidePos = currentStory.getPosition();
refreshStory();
}
}
});
private ActivityStoryViewerBinding storyViewerBinding;
private StoryModel[] storyModels;
private GestureDetectorCompat gestureDetector;
private SimpleExoPlayer player;
private SwipeEvent swipeEvent;
private MenuItem menuDownload, menuDm;
private PollModel poll;
private QuestionModel question;
private String[] mentions;
private QuizModel quiz;
private SwipeUpModel swipeUp;
private StoryModel currentStory;
private String url, username;
private int slidePos = 0, lastSlidePos = 0;
private final String cookie = settingsHelper.getString(Constants.COOKIE);
private boolean fetching = false;
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
storyViewerBinding = ActivityStoryViewerBinding.inflate(getLayoutInflater());
setContentView(storyViewerBinding.getRoot());
setSupportActionBar(storyViewerBinding.toolbar.toolbar);
final Intent intent = getIntent();
if (intent == null || !intent.hasExtra(Constants.EXTRAS_STORIES)
|| (storyModels = (StoryModel[]) intent.getSerializableExtra(Constants.EXTRAS_STORIES)) == null) {
Utils.errorFinish(this);
return;
}
username = intent.getStringExtra(Constants.EXTRAS_USERNAME);
final String highlight = intent.getStringExtra(Constants.EXTRAS_HIGHLIGHT);
final boolean hasUsername = !Utils.isEmpty(username);
final boolean hasHighlight = !Utils.isEmpty(highlight);
if (hasUsername) {
username = username.replace("@", "");
storyViewerBinding.toolbar.toolbar.setTitle(username);
storyViewerBinding.toolbar.toolbar.setOnClickListener(v -> {
searchUsername(username);
});
if (hasHighlight) storyViewerBinding.toolbar.toolbar.setSubtitle(getString(R.string.title_highlight, highlight));
else storyViewerBinding.toolbar.toolbar.setSubtitle(R.string.title_user_story);
}
storyViewerBinding.storiesList.setVisibility(View.GONE);
storyViewerBinding.storiesList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
storyViewerBinding.storiesList.setAdapter(storiesAdapter);
swipeEvent = new SwipeEvent() {
private final int storiesLen = storyModels != null ? storyModels.length : 0;
@Override
public void onSwipe(final boolean isRightSwipe) {
if (storyModels != null && storiesLen > 0) {
if (((slidePos + 1 >= storiesLen && isRightSwipe == false) || (slidePos == 0 && isRightSwipe == true))
&& intent.hasExtra(Constants.FEED)) {
final FeedStoryModel[] storyFeed = (FeedStoryModel[]) intent.getSerializableExtra(Constants.FEED);
final int index = intent.getIntExtra(Constants.FEED_ORDER, 1738);
if (settingsHelper.getBoolean(MARK_AS_SEEN)) new SeenAction().execute();
if ((isRightSwipe == true && index == 0) || (isRightSwipe == false && index == storyFeed.length - 1))
Toast.makeText(getApplicationContext(), R.string.no_more_stories, Toast.LENGTH_SHORT).show();
else {
final FeedStoryModel feedStoryModel = isRightSwipe ?
(index == 0 ? null : storyFeed[index - 1]) :
(storyFeed.length == index + 1 ? null : storyFeed[index + 1]);
if (feedStoryModel != null) {
if (fetching) {
Toast.makeText(getApplicationContext(), R.string.be_patient, Toast.LENGTH_SHORT).show();
} else {
fetching = true;
new iStoryStatusFetcher(feedStoryModel.getStoryMediaId(), null, false, false, false, false, result -> {
if (result != null && result.length > 0) {
final Intent newIntent = new Intent(getApplicationContext(), StoryViewer.class)
.putExtra(Constants.EXTRAS_STORIES, result)
.putExtra(Constants.EXTRAS_USERNAME, feedStoryModel.getProfileModel().getUsername())
.putExtra(Constants.FEED, storyFeed)
.putExtra(Constants.FEED_ORDER, isRightSwipe ? (index - 1) : (index + 1));
newIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(newIntent);
} else
Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
}
}
else {
if (isRightSwipe) {
if (--slidePos <= 0) slidePos = 0;
} else if (++slidePos >= storiesLen) slidePos = storiesLen - 1;
currentStory = storyModels[slidePos];
refreshStory();
}
}
}
};
gestureDetector = new GestureDetectorCompat(this, new SwipeGestureListener(swipeEvent));
viewPost();
}
@SuppressLint("ClickableViewAccessibility")
private void viewPost() {
lastSlidePos = 0;
storyViewerBinding.storiesList.setVisibility(View.GONE);
storiesAdapter.setData(null);
if (menuDownload != null) menuDownload.setVisible(false);
if (menuDm != null) menuDm.setVisible(false);
storyViewerBinding.playerView.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event));
storyViewerBinding.imageViewer.setOnSingleFlingListener((e1, e2, velocityX, velocityY) -> {
final float diffX = e2.getX() - e1.getX();
try {
if (Math.abs(diffX) > Math.abs(e2.getY() - e1.getY()) && Math.abs(diffX) > SWIPE_THRESHOLD
&& Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
swipeEvent.onSwipe(diffX > 0);
return true;
}
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ACTIVITY_STORY_VIEWER, "viewPost",
new Pair<>("swipeEvent", swipeEvent),
new Pair<>("diffX", diffX));
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
}
return false;
});
final View.OnClickListener linkActionListener = v -> {
final Object tag = v.getTag();
if (tag instanceof CharSequence) {
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(tag.toString()));
startActivity(intent);
}
};
storyViewerBinding.spotify.setOnClickListener(linkActionListener);
storyViewerBinding.swipeUp.setOnClickListener(linkActionListener);
storyViewerBinding.viewStoryPost.setOnClickListener(v -> {
final Object tag = v.getTag();
if (tag instanceof CharSequence) startActivity(new Intent(this, PostViewer.class)
.putExtra(Constants.EXTRAS_POST, new PostModel(tag.toString(), tag.toString().matches("^[\\d]+$"))));
});
final View.OnClickListener storyActionListener = v -> {
final Object tag = v.getTag();
if (tag instanceof PollModel) {
poll = (PollModel) tag;
if (poll.getMyChoice() > -1)
new AlertDialog.Builder(this).setTitle(R.string.voted_story_poll)
.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new String[]{
(poll.getMyChoice() == 0 ? "" : "") + poll.getLeftChoice() + " (" + poll.getLeftCount() + ")",
(poll.getMyChoice() == 1 ? "" : "") + poll.getRightChoice() + " (" + poll.getRightCount() + ")"
}), null)
.setPositiveButton(R.string.ok, null)
.show();
else new AlertDialog.Builder(this).setTitle(poll.getQuestion())
.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new String[]{
poll.getLeftChoice() + " (" + poll.getLeftCount() + ")",
poll.getRightChoice() + " (" + poll.getRightCount() + ")"
}), (d, w) -> {
if (!Utils.isEmpty(cookie)) new VoteAction().execute(w);
})
.setPositiveButton(R.string.cancel, null)
.show();
}
else if (tag instanceof QuestionModel) {
question = (QuestionModel) tag;
final EditText input = new EditText(this);
input.setHint(R.string.answer_hint);
new AlertDialog.Builder(this).setTitle(question.getQuestion())
.setView(input)
.setPositiveButton(R.string.ok, (d,w) -> {
new RespondAction().execute(input.getText().toString());
})
.setNegativeButton(R.string.cancel, null)
.show();
}
else if (tag instanceof String[]) {
mentions = (String[]) tag;
new AlertDialog.Builder(this).setTitle(R.string.story_mentions)
.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mentions), (d,w) -> {
searchUsername(mentions[w]);
})
.setPositiveButton(R.string.cancel, null)
.show();
}
else if (tag instanceof QuizModel) {
quiz = quiz;
String[] choices = new String[quiz.getChoices().length];
for (int q = 0; q < choices.length; ++q) {
choices[q] = (quiz.getMyChoice() == q ? "" :"") + quiz.getChoices()[q]+ " (" + quiz.getCounts()[q] + ")";
}
new AlertDialog.Builder(this).setTitle(quiz.getMyChoice() > -1 ? getString(R.string.story_quizzed) : quiz.getQuestion())
.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, choices), (d,w) -> {
if (quiz.getMyChoice() == -1 && !Utils.isEmpty(cookie)) new QuizAction().execute(w);
})
.setPositiveButton(R.string.cancel, null)
.show();
}
};
storyViewerBinding.poll.setOnClickListener(storyActionListener);
storyViewerBinding.answer.setOnClickListener(storyActionListener);
storyViewerBinding.mention.setOnClickListener(storyActionListener);
storyViewerBinding.quiz.setOnClickListener(storyActionListener);
storiesAdapter.setData(storyModels);
if (storyModels.length > 1) storyViewerBinding.storiesList.setVisibility(View.VISIBLE);
currentStory = storyModels[0];
refreshStory();
}
private void setupVideo() {
storyViewerBinding.playerView.setVisibility(View.VISIBLE);
storyViewerBinding.progressView.setVisibility(View.GONE);
storyViewerBinding.imageViewer.setVisibility(View.GONE);
storyViewerBinding.imageViewer.setImageDrawable(null);
player = new SimpleExoPlayer.Builder(this).build();
storyViewerBinding.playerView.setPlayer(player);
player.setPlayWhenReady(settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));
final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(new DefaultDataSourceFactory(this, "instagram"))
.createMediaSource(Uri.parse(url));
mediaSource.addEventListener(new Handler(), new MediaSourceEventListener() {
@Override
public void onLoadCompleted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
if (menuDownload != null) menuDownload.setVisible(true);
if (currentStory.canReply() && menuDm != null && !Utils.isEmpty(cookie)) menuDm.setVisible(true);
storyViewerBinding.progressView.setVisibility(View.GONE);
}
@Override
public void onLoadStarted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
if (menuDownload != null) menuDownload.setVisible(true);
if (currentStory.canReply() && menuDm != null && !Utils.isEmpty(cookie)) menuDm.setVisible(true);
storyViewerBinding.progressView.setVisibility(View.VISIBLE);
}
@Override
public void onLoadCanceled(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
storyViewerBinding.progressView.setVisibility(View.GONE);
}
@Override
public void onLoadError(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData, final IOException error, final boolean wasCanceled) {
if (menuDownload != null) menuDownload.setVisible(false);
if (menuDm != null) menuDm.setVisible(false);
storyViewerBinding.progressView.setVisibility(View.GONE);
}
});
player.prepare(mediaSource);
storyViewerBinding.playerView.setOnClickListener(v -> {
if (player != null) {
if (player.getPlaybackState() == Player.STATE_ENDED) player.seekTo(0);
player.setPlayWhenReady(player.getPlaybackState() == Player.STATE_ENDED || !player.isPlaying());
}
});
}
private void setupImage() {
storyViewerBinding.progressView.setVisibility(View.VISIBLE);
storyViewerBinding.playerView.setVisibility(View.GONE);
storyViewerBinding.imageViewer.setImageDrawable(null);
storyViewerBinding.imageViewer.setVisibility(View.VISIBLE);
storyViewerBinding.imageViewer.setZoomable(true);
storyViewerBinding.imageViewer.setZoomTransitionDuration(420);
storyViewerBinding.imageViewer.setMaximumScale(7.2f);
Glide.with(this).load(url).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable final GlideException e, final Object model, final Target<Drawable> target, final boolean isFirstResource) {
storyViewerBinding.progressView.setVisibility(View.GONE);
return false;
}
@Override
public boolean onResourceReady(final Drawable resource, final Object model, final Target<Drawable> target, final DataSource dataSource, final boolean isFirstResource) {
if (menuDownload != null) menuDownload.setVisible(true);
if (currentStory.canReply() && menuDm != null && !Utils.isEmpty(cookie)) menuDm.setVisible(true);
storyViewerBinding.progressView.setVisibility(View.GONE);
return false;
}
}).into(storyViewerBinding.imageViewer);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
menu.findItem(R.id.action_settings).setVisible(false);
menu.findItem(R.id.action_search).setVisible(false);
menuDownload = menu.findItem(R.id.action_download);
menuDm = menu.findItem(R.id.action_dms);
menuDownload.setVisible(true);
menuDm.setVisible(false);
menuDownload.setOnMenuItemClickListener(item -> {
if (ContextCompat.checkSelfPermission(this, Utils.PERMS[0]) == PackageManager.PERMISSION_GRANTED)
downloadStory();
else
ActivityCompat.requestPermissions(this, Utils.PERMS, 8020);
return true;
});
menuDm.setOnMenuItemClickListener(item -> {
final EditText input = new EditText(this);
input.setHint(R.string.reply_hint);
new AlertDialog.Builder(this).setTitle(R.string.reply_story)
.setView(input)
.setPositiveButton(R.string.ok, (d,w) -> {
new CommentAction().execute(input.getText().toString());
})
.setNegativeButton(R.string.cancel, null)
.show();
return true;
});
return true;
}
@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 8020 && grantResults[0] == PackageManager.PERMISSION_GRANTED) downloadStory();
}
@Override
public void onPause() {
super.onPause();
if (Build.VERSION.SDK_INT < 24) releasePlayer();
}
@Override
public void onStop() {
super.onStop();
if (Build.VERSION.SDK_INT >= 24) releasePlayer();
}
private void downloadStory() {
int error = 0;
if (currentStory != null) {
File dir = new File(Environment.getExternalStorageDirectory(), "Download");
if (settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
final String customPath = settingsHelper.getString(FOLDER_PATH);
if (!Utils.isEmpty(customPath)) dir = new File(customPath);
}
if (settingsHelper.getBoolean(Constants.DOWNLOAD_USER_FOLDER) && !Utils.isEmpty(username))
dir = new File(dir, username);
if (dir.exists() || dir.mkdirs()) {
final String storyUrl = currentStory.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO ? currentStory.getVideoUrl() : currentStory.getStoryUrl();
final File saveFile = new File(dir, currentStory.getStoryMediaId() + "_" + currentStory.getTimestamp()
+ Utils.getExtensionFromModel(storyUrl, currentStory));
new DownloadAsync(this, storyUrl, saveFile, result -> {
final int toastRes = result != null && result.exists() ? R.string.downloader_complete
: R.string.downloader_error_download_file;
Toast.makeText(this, toastRes, Toast.LENGTH_SHORT).show();
}).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();
}
private void refreshStory() {
if (storyViewerBinding.storiesList.getVisibility() == View.VISIBLE) {
StoryModel item = storiesAdapter.getItemAt(lastSlidePos);
if (item != null) {
item.setCurrentSlide(false);
storiesAdapter.notifyItemChanged(lastSlidePos, item);
}
item = storiesAdapter.getItemAt(slidePos);
if (item != null) {
item.setCurrentSlide(true);
storiesAdapter.notifyItemChanged(slidePos, item);
}
}
lastSlidePos = slidePos;
final MediaItemType itemType = currentStory.getItemType();
if (menuDownload != null) menuDownload.setVisible(false);
url = itemType == MediaItemType.MEDIA_TYPE_VIDEO ? currentStory.getVideoUrl() : currentStory.getStoryUrl();
final String shortCode = currentStory.getTappableShortCode();
storyViewerBinding.viewStoryPost.setVisibility(shortCode != null ? View.VISIBLE : View.GONE);
storyViewerBinding.viewStoryPost.setTag(shortCode);
final String spotify = currentStory.getSpotify();
storyViewerBinding.spotify.setVisibility(spotify != null ? View.VISIBLE : View.GONE);
storyViewerBinding.spotify.setTag(spotify);
poll = currentStory.getPoll();
storyViewerBinding.poll.setVisibility(poll != null ? View.VISIBLE : View.GONE);
storyViewerBinding.poll.setTag(poll);
question = currentStory.getQuestion();
storyViewerBinding.answer.setVisibility((question != null && !Utils.isEmpty(cookie)) ? View.VISIBLE : View.GONE);
storyViewerBinding.answer.setTag(question);
mentions = currentStory.getMentions();
storyViewerBinding.mention.setVisibility((mentions != null && mentions.length > 0) ? View.VISIBLE : View.GONE);
storyViewerBinding.mention.setTag(mentions);
quiz = currentStory.getQuiz();
storyViewerBinding.quiz.setVisibility(quiz != null ? View.VISIBLE : View.GONE);
storyViewerBinding.quiz.setTag(quiz);
swipeUp = currentStory.getSwipeUp();
storyViewerBinding.swipeUp.setVisibility(swipeUp != null ? View.VISIBLE : View.GONE);
if (swipeUp != null) {
storyViewerBinding.swipeUp.setText(swipeUp.getText());
storyViewerBinding.swipeUp.setTag(swipeUp.getUrl());
}
releasePlayer();
final Intent intent = getIntent();
if (intent.getBooleanExtra(Constants.EXTRAS_HASHTAG, false)) {
storyViewerBinding.toolbar.toolbar.setTitle(currentStory.getUsername() + " (" + intent.getStringExtra(Constants.EXTRAS_USERNAME) + ")");
storyViewerBinding.toolbar.toolbar.setOnClickListener(v -> {
searchUsername(currentStory.getUsername());
});
}
if (itemType == MediaItemType.MEDIA_TYPE_VIDEO) setupVideo();
else setupImage();
if (!intent.hasExtra(Constants.EXTRAS_HIGHLIGHT))
storyViewerBinding.toolbar.toolbar.setSubtitle(Utils.datetimeParser.format(new Date(currentStory.getTimestamp() * 1000L)));
if (settingsHelper.getBoolean(MARK_AS_SEEN)) new SeenAction().execute();
}
private void searchUsername(final String text) {
startActivity(
new Intent(getApplicationContext(), ProfileViewer.class)
.putExtra(Constants.EXTRAS_USERNAME, text)
);
}
private void releasePlayer() {
if (player != null) {
try { player.stop(true); } catch (Exception ignored) { }
try { player.release(); } catch (Exception ignored) { }
player = null;
}
}
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 VoteAction extends AsyncTask<Integer, Void, Void> {
int ok = -1;
String action;
protected Void doInBackground(Integer... rawchoice) {
int choice = rawchoice[0];
final String url = "https://www.instagram.com/media/"+currentStory.getStoryMediaId().split("_")[0]+"/"+poll.getId()+"/story_poll_vote/";
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]);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", "6");
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes("vote="+choice);
wr.flush();
wr.close();
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = choice;
}
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "vote: " + ex);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if (ok > -1) {
poll.setMyChoice(ok);
Toast.makeText(getApplicationContext(), R.string.votef_story_poll, Toast.LENGTH_SHORT).show();
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
class QuizAction extends AsyncTask<Integer, Void, Void> {
int ok = -1;
String action;
protected Void doInBackground(Integer... rawchoice) {
int choice = rawchoice[0];
final String url = "https://i.instagram.com/api/v1/media/"+currentStory.getStoryMediaId().split("_")[0]+"/"+quiz.getId()+"/story_quiz_answer/";
try {
JSONObject ogbody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString()
+"\",\"mutation_token\":\"" + UUID.randomUUID().toString()
+"\",\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+"\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie)
+"\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+"\"}");
ogbody.put("answer", String.valueOf(choice));
String urlParameters = Utils.sign(ogbody.toString());
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
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();
Log.d("austin_debug", "quiz: "+url+" "+cookie+" "+urlParameters);
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = choice;
}
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "quiz: " + ex);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if (ok > -1) {
quiz.setMyChoice(ok);
Toast.makeText(getApplicationContext(), R.string.answered_story, Toast.LENGTH_SHORT).show();
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
class RespondAction extends AsyncTask<String, Void, Void> {
boolean ok = false;
String action;
protected Void doInBackground(String... rawchoice) {
final String url = "https://i.instagram.com/api/v1/media/"
+currentStory.getStoryMediaId().split("_")[0]+"/"+question.getId()+"/story_question_response/";
try {
JSONObject ogbody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString()
+"\",\"mutation_token\":\"" + UUID.randomUUID().toString()
+"\",\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+"\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie)
+"\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+"\"}");
String choice = rawchoice[0].replaceAll("\"", ("\\\""));
ogbody.put("response", choice);
String urlParameters = Utils.sign(ogbody.toString());
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
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();
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = true;
}
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "respond: " + ex);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if (ok) {
Toast.makeText(getApplicationContext(), R.string.answered_story, Toast.LENGTH_SHORT).show();
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
class SeenAction extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... lmao) {
final String url = "https://www.instagram.com/stories/reel/seen";
try {
String urlParameters = "reelMediaId="+currentStory.getStoryMediaId().split("_")[0]
+"&reelMediaOwnerId="+currentStory.getUserId()
+"&reelId="+currentStory.getUserId()
+"&reelMediaTakenAt="+ currentStory.getTimestamp()
+"&viewSeenAt="+ currentStory.getTimestamp();
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("x-csrftoken", cookie.split("csrftoken=")[1].split(";")[0]);
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();
urlConnection.connect();
Log.d("austin_debug", urlConnection.getResponseCode() + " " + Utils.readFromConnection(urlConnection));
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "seen: " + ex);
}
return null;
}
}
class CommentAction extends AsyncTask<String, Void, Void> {
protected Void doInBackground(String... rawAction) {
final String action = rawAction[0];
final String url = "https://i.instagram.com/api/v1/direct_v2/create_group_thread/";
try {
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
urlConnection.setUseCaches(false);
final String urlParameters = Utils.sign("{\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+"\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie)
+"\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+"\",\"recipient_users\":\"["+currentStory.getUserId() // <- string of array of number (not joking)
+"]\"}");
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", "" + urlParameters.getBytes().length);
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
final String threadid = new JSONObject(Utils.readFromConnection(urlConnection)).getString("thread_id");
final DirectThreadBroadcaster.StoryReplyBroadcastOptions options =
new DirectThreadBroadcaster.StoryReplyBroadcastOptions(
action,
currentStory.getStoryMediaId(),
currentStory.getUserId()
);
final DirectThreadBroadcaster broadcast = new DirectThreadBroadcaster(threadid);
broadcast.setOnTaskCompleteListener(result -> {
Toast.makeText(getApplicationContext(),
result != null ? R.string.answered_story : R.string.downloader_unknown_error,
Toast.LENGTH_SHORT).show();
});
broadcast.execute(options);
}
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "reply (CT): " + ex);
Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
return null;
}
}
}

View File

@ -0,0 +1,112 @@
package awais.instagrabber.adapters;
import android.annotation.SuppressLint;
import android.graphics.Typeface;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.databinding.PrefAccountSwitcherBinding;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DataBox;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class AccountSwitcherAdapter extends ListAdapter<DataBox.CookieModel, AccountSwitcherAdapter.ViewHolder> {
private static final String TAG = "AccountSwitcherAdapter";
private static final DiffUtil.ItemCallback<DataBox.CookieModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<DataBox.CookieModel>() {
@Override
public boolean areItemsTheSame(@NonNull final DataBox.CookieModel oldItem, @NonNull final DataBox.CookieModel newItem) {
return oldItem.getUid().equals(newItem.getUid());
}
@Override
public boolean areContentsTheSame(@NonNull final DataBox.CookieModel oldItem, @NonNull final DataBox.CookieModel newItem) {
return oldItem.getUid().equals(newItem.getUid());
}
};
private final OnAccountClickListener clickListener;
private final OnAccountLongClickListener longClickListener;
public AccountSwitcherAdapter(final OnAccountClickListener clickListener,
final OnAccountLongClickListener longClickListener) {
super(DIFF_CALLBACK);
this.clickListener = clickListener;
this.longClickListener = longClickListener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final PrefAccountSwitcherBinding binding = PrefAccountSwitcherBinding.inflate(layoutInflater, parent, false);
return new ViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {
final DataBox.CookieModel model = getItem(position);
if (model == null) return;
final String cookie = settingsHelper.getString(Constants.COOKIE);
final boolean isCurrent = model.getCookie().equals(cookie);
holder.bind(model, isCurrent, clickListener, longClickListener);
}
public interface OnAccountClickListener {
void onAccountClick(final DataBox.CookieModel model, final boolean isCurrent);
}
public interface OnAccountLongClickListener {
boolean onAccountLongClick(final DataBox.CookieModel model, final boolean isCurrent);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private final PrefAccountSwitcherBinding binding;
public ViewHolder(final PrefAccountSwitcherBinding binding) {
super(binding.getRoot());
this.binding = binding;
binding.arrowDown.setImageResource(R.drawable.ic_check_24);
}
@SuppressLint("SetTextI18n")
public void bind(final DataBox.CookieModel model,
final boolean isCurrent,
final OnAccountClickListener clickListener,
final OnAccountLongClickListener longClickListener) {
// Log.d(TAG, model.getFullName());
itemView.setOnClickListener(v -> {
if (clickListener == null) return;
clickListener.onAccountClick(model, isCurrent);
});
itemView.setOnLongClickListener(v -> {
if (longClickListener == null) return false;
return longClickListener.onAccountLongClick(model, isCurrent);
});
binding.profilePic.setImageURI(model.getProfilePic());
binding.username.setText("@" + model.getUsername());
binding.fullName.setTypeface(null);
final String fullName = model.getFullName();
if (TextUtils.isEmpty(fullName)) {
binding.fullName.setVisibility(View.GONE);
} else {
binding.fullName.setVisibility(View.VISIBLE);
binding.fullName.setText(fullName);
}
if (!isCurrent) {
binding.arrowDown.setVisibility(View.GONE);
return;
}
binding.fullName.setTypeface(binding.fullName.getTypeface(), Typeface.BOLD);
binding.arrowDown.setVisibility(View.VISIBLE);
}
}
}

View File

@ -10,9 +10,6 @@ import android.widget.Filterable;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import java.util.ArrayList;
import awais.instagrabber.R;
@ -21,10 +18,18 @@ import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.TextUtils;
public final class CommentsAdapter extends RecyclerView.Adapter<CommentViewHolder> implements Filterable {
private CommentModel[] filteredCommentModels;
private LayoutInflater layoutInflater;
private final boolean isParent;
private final View.OnClickListener onClickListener;
private final MentionClickListener mentionClickListener;
private final CommentModel[] commentModels;
private final String[] quantityStrings = new String[2];
private final Filter filter = new Filter() {
@NonNull
@Override
@ -33,7 +38,7 @@ public final class CommentsAdapter extends RecyclerView.Adapter<CommentViewHolde
results.values = commentModels;
final int commentsLen = commentModels == null ? 0 : commentModels.length;
if (commentModels != null && commentsLen > 0 && !Utils.isEmpty(filter)) {
if (commentModels != null && commentsLen > 0 && !TextUtils.isEmpty(filter)) {
final String query = filter.toString().toLowerCase();
final ArrayList<CommentModel> filterList = new ArrayList<>(commentsLen);
@ -66,15 +71,12 @@ public final class CommentsAdapter extends RecyclerView.Adapter<CommentViewHolde
}
}
};
private final View.OnClickListener onClickListener;
private final MentionClickListener mentionClickListener;
private final CommentModel[] commentModels;
private final String[] quantityStrings = new String[2];
private LayoutInflater layoutInflater;
private CommentModel[] filteredCommentModels;
public CommentsAdapter(final CommentModel[] commentModels, final boolean isParent, final View.OnClickListener onClickListener,
public CommentsAdapter(final CommentModel[] commentModels,
final boolean isParent,
final View.OnClickListener onClickListener,
final MentionClickListener mentionClickListener) {
super();
this.commentModels = this.filteredCommentModels = commentModels;
this.isParent = isParent;
this.onClickListener = onClickListener;
@ -93,10 +95,13 @@ public final class CommentsAdapter extends RecyclerView.Adapter<CommentViewHolde
if (quantityStrings[0] == null) quantityStrings[0] = context.getString(R.string.single_like);
if (quantityStrings[1] == null) quantityStrings[1] = context.getString(R.string.multiple_likes);
if (layoutInflater == null) layoutInflater = LayoutInflater.from(context);
return new CommentViewHolder(layoutInflater.inflate(
isParent ? R.layout.item_comment // parent
: R.layout.item_comment_small, // child
parent, false), onClickListener, mentionClickListener);
final View view = layoutInflater.inflate(isParent ? R.layout.item_comment
: R.layout.item_comment_small,
parent,
false);
return new CommentViewHolder(view,
onClickListener,
mentionClickListener);
}
@Override
@ -105,7 +110,7 @@ public final class CommentsAdapter extends RecyclerView.Adapter<CommentViewHolde
if (commentModel != null) {
holder.setCommentModel(commentModel);
holder.setCommment(commentModel.getText());
holder.setComment(commentModel.getText());
holder.setDate(commentModel.getDateTime());
holder.setLiked(commentModel.getLiked());
@ -115,12 +120,8 @@ public final class CommentsAdapter extends RecyclerView.Adapter<CommentViewHolde
final ProfileModel profileModel = commentModel.getProfileModel();
if (profileModel != null) {
holder.setUsername(profileModel.getUsername());
Glide.with(layoutInflater.getContext())
.applyDefaultRequestOptions(new RequestOptions().skipMemoryCache(true))
.load(profileModel.getSdProfilePic()).into(holder.getProfilePicView());
holder.getProfilePicView().setImageURI(profileModel.getSdProfilePic());
}
if (holder.isParent()) {
final CommentModel[] childCommentModels = commentModel.getChildCommentModels();
if (childCommentModels != null && childCommentModels.length > 0)

View File

@ -8,7 +8,7 @@ import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import awais.instagrabber.adapters.viewholder.DirectMessageInboxItemViewHolder;
import awais.instagrabber.databinding.LayoutIncludeSimpleItemBinding;
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
import awais.instagrabber.models.direct_messages.InboxThreadModel;
public final class DirectMessageInboxAdapter extends ListAdapter<InboxThreadModel, DirectMessageInboxItemViewHolder> {
@ -35,7 +35,7 @@ public final class DirectMessageInboxAdapter extends ListAdapter<InboxThreadMode
@Override
public DirectMessageInboxItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final LayoutIncludeSimpleItemBinding binding = LayoutIncludeSimpleItemBinding.inflate(layoutInflater, parent, false);
final LayoutDmInboxItemBinding binding = LayoutDmInboxItemBinding.inflate(layoutInflater, parent, false);
return new DirectMessageInboxItemViewHolder(binding);
}

View File

@ -1,6 +1,5 @@
package awais.instagrabber.adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -8,52 +7,37 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.FollowsViewHolder;
import awais.instagrabber.databinding.ItemFollowBinding;
import awais.instagrabber.models.ProfileModel;
public final class DirectMessageMembersAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public final class DirectMessageMembersAdapter extends RecyclerView.Adapter<FollowsViewHolder> {
private final ProfileModel[] profileModels;
private final List<Long> admins;
private final View.OnClickListener onClickListener;
private final LayoutInflater layoutInflater;
public DirectMessageMembersAdapter(final ProfileModel[] profileModels, final List<Long> admins,
final Context context, final View.OnClickListener onClickListener) {
public DirectMessageMembersAdapter(final ProfileModel[] profileModels,
final List<Long> admins,
final View.OnClickListener onClickListener) {
this.profileModels = profileModels;
this.admins = admins;
this.layoutInflater = LayoutInflater.from(context);
this.onClickListener = onClickListener;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final View view = layoutInflater.inflate(R.layout.item_follow, parent, false);
return new FollowsViewHolder(view);
public FollowsViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemFollowBinding binding = ItemFollowBinding.inflate(layoutInflater, parent, false);
return new FollowsViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
public void onBindViewHolder(@NonNull final FollowsViewHolder holder, final int position) {
final ProfileModel model = profileModels[position];
final FollowsViewHolder followHolder = (FollowsViewHolder) holder;
if (model != null) {
followHolder.itemView.setTag(model);
followHolder.itemView.setOnClickListener(onClickListener);
followHolder.tvUsername.setText(model.getUsername());
followHolder.tvFullName.setText(model.getName());
if (admins != null && admins.contains(Long.parseLong(model.getId())))
followHolder.isAdmin.setVisibility(View.VISIBLE);
Glide.with(layoutInflater.getContext()).load(model.getSdProfilePic()).into(followHolder.profileImage);
}
holder.bind(model, admins, onClickListener);
}
@Override

View File

@ -1,87 +1,57 @@
package awais.instagrabber.adapters;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
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.util.ArrayList;
import androidx.recyclerview.widget.DiffUtil;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.DiscoverViewHolder;
import awais.instagrabber.models.DiscoverItemModel;
import awais.instagrabber.models.enums.MediaItemType;
public final class DiscoverAdapter extends RecyclerView.Adapter<DiscoverViewHolder> {
private final ArrayList<DiscoverItemModel> discoverItemModels;
private final View.OnClickListener clickListener;
private final View.OnLongClickListener longClickListener;
private LayoutInflater layoutInflater;
public boolean isSelecting = false;
public final class DiscoverAdapter extends MultiSelectListAdapter<DiscoverItemModel, DiscoverViewHolder> {
public DiscoverAdapter(final ArrayList<DiscoverItemModel> discoverItemModels, final View.OnClickListener clickListener,
final View.OnLongClickListener longClickListener) {
this.discoverItemModels = discoverItemModels;
this.longClickListener = longClickListener;
this.clickListener = clickListener;
private static final DiffUtil.ItemCallback<DiscoverItemModel> diffCallback = new DiffUtil.ItemCallback<DiscoverItemModel>() {
@Override
public boolean areItemsTheSame(@NonNull final DiscoverItemModel oldItem, @NonNull final DiscoverItemModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
@Override
public boolean areContentsTheSame(@NonNull final DiscoverItemModel oldItem, @NonNull final DiscoverItemModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
};
public DiscoverAdapter(final OnItemClickListener<DiscoverItemModel> clickListener,
final OnItemLongClickListener<DiscoverItemModel> longClickListener) {
super(diffCallback, clickListener, longClickListener);
}
@NonNull
@Override
public DiscoverViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
if (layoutInflater == null) layoutInflater = LayoutInflater.from(parent.getContext());
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
return new DiscoverViewHolder(layoutInflater.inflate(R.layout.item_post, parent, false));
}
@Override
public void onBindViewHolder(@NonNull final DiscoverViewHolder holder, final int position) {
final DiscoverItemModel itemModel = discoverItemModels.get(position);
final DiscoverItemModel itemModel = getItem(position);
if (itemModel != null) {
itemModel.setPosition(position);
holder.itemView.setTag(itemModel);
holder.itemView.setOnClickListener(clickListener);
holder.itemView.setOnLongClickListener(longClickListener);
holder.itemView.setOnClickListener(v -> getInternalOnItemClickListener().onItemClick(itemModel, position));
holder.itemView.setOnLongClickListener(v -> getInternalOnLongItemClickListener().onItemLongClick(itemModel, position));
final MediaItemType mediaType = itemModel.getItemType();
holder.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER
? View.VISIBLE : View.GONE);
holder.typeIcon.setImageResource(mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? R.drawable.slider : R.drawable.video);
holder.typeIcon.setVisibility(
mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
holder.typeIcon.setImageResource(mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? R.drawable.ic_slider_24 : R.drawable.ic_video_24);
holder.selectedView.setVisibility(itemModel.isSelected() ? View.VISIBLE : View.GONE);
holder.progressView.setVisibility(View.VISIBLE);
Glide.with(layoutInflater.getContext()).load(itemModel.getDisplayUrl()).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable final GlideException e, final Object model, final Target<Drawable> target, final boolean isFirstResource) {
holder.progressView.setVisibility(View.GONE);
return false;
}
@Override
public boolean onResourceReady(final Drawable resource, final Object model, final Target<Drawable> target, final DataSource dataSource, final boolean isFirstResource) {
holder.progressView.setVisibility(View.GONE);
return false;
}
}).into(holder.postImage);
holder.postImage.setImageURI(itemModel.getDisplayUrl());
}
}
@Override
public int getItemCount() {
return discoverItemModels == null ? 0 : discoverItemModels.size();
}
}

View File

@ -1,496 +1,111 @@
package awais.instagrabber.adapters;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestManager;
import com.github.chrisbanes.photoview.PhotoView;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Collections;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.activities.CommentsViewer;
import awais.instagrabber.activities.PostViewer;
import awais.instagrabber.adapters.viewholder.FeedItemViewHolder;
import awais.instagrabber.customviews.CommentMentionClickSpan;
import awais.instagrabber.adapters.viewholder.feed.FeedItemViewHolder;
import awais.instagrabber.adapters.viewholder.feed.FeedPhotoViewHolder;
import awais.instagrabber.adapters.viewholder.feed.FeedSliderViewHolder;
import awais.instagrabber.adapters.viewholder.feed.FeedVideoViewHolder;
import awais.instagrabber.customviews.RamboTextView;
import awais.instagrabber.databinding.ItemFeedPhotoBinding;
import awais.instagrabber.databinding.ItemFeedSliderBinding;
import awais.instagrabber.databinding.ItemFeedVideoBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.BasePostModel;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.ViewerPostModel;
import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.ItemGetType;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class FeedAdapter extends RecyclerView.Adapter<FeedItemViewHolder> {
private final static String ellipsize = "… more";
private final Activity activity;
private final LayoutInflater layoutInflater;
private final ArrayList<FeedModel> feedModels;
public final class FeedAdapter extends ListAdapter<FeedModel, FeedItemViewHolder> {
private static final String TAG = "FeedAdapter";
private final View.OnClickListener clickListener;
private final MentionClickListener mentionClickListener;
private final View.OnClickListener clickListener = new View.OnClickListener() {
@Override
public void onClick(@NonNull final View v) {
final Object tag = v.getTag();
if (tag instanceof FeedModel) {
final FeedModel feedModel = (FeedModel) tag;
if (v instanceof RamboTextView) {
if (feedModel.isMentionClicked())
feedModel.toggleCaption();
feedModel.setMentionClicked(false);
if (!expandCollapseTextView((RamboTextView) v, feedModel))
feedModel.toggleCaption();
} else {
final int id = v.getId();
switch (id) {
case R.id.btnComments:
activity.startActivityForResult(new Intent(activity, CommentsViewer.class)
.putExtra(Constants.EXTRAS_SHORTCODE, feedModel.getShortCode())
.putExtra(Constants.EXTRAS_POST, feedModel.getPostId())
.putExtra(Constants.EXTRAS_USER, feedModel.getProfileModel().getId()), 6969);
break;
case R.id.viewStoryPost:
activity.startActivity(new Intent(activity, PostViewer.class)
.putExtra(Constants.EXTRAS_INDEX, feedModel.getPosition())
.putExtra(Constants.EXTRAS_POST, new PostModel(feedModel.getShortCode(), false))
.putExtra(Constants.EXTRAS_TYPE, ItemGetType.FEED_ITEMS));
break;
case R.id.btnDownload:
final Context context = v.getContext();
ProfileModel profileModel = feedModel.getProfileModel();
final String username = profileModel != null ? profileModel.getUsername() : null;
final ViewerPostModel[] sliderItems = feedModel.getSliderItems();
if (feedModel.getItemType() != MediaItemType.MEDIA_TYPE_SLIDER || sliderItems == null || sliderItems.length == 1)
Utils.batchDownload(context, username, DownloadMethod.DOWNLOAD_FEED, Collections.singletonList(feedModel));
else {
final ArrayList<BasePostModel> postModels = new ArrayList<>();
final DialogInterface.OnClickListener clickListener = (dialog, which) -> {
postModels.clear();
final boolean breakWhenFoundSelected = which == DialogInterface.BUTTON_POSITIVE;
for (final ViewerPostModel sliderItem : sliderItems) {
if (sliderItem != null) {
if (!breakWhenFoundSelected) postModels.add(sliderItem);
else if (sliderItem.isSelected()) {
postModels.add(sliderItem);
break;
}
}
}
// shows 0 items on first item of viewpager cause onPageSelected hasn't been called yet
if (breakWhenFoundSelected && postModels.size() == 0)
postModels.add(sliderItems[0]);
if (postModels.size() > 0)
Utils.batchDownload(context, username, DownloadMethod.DOWNLOAD_FEED, postModels);
};
new AlertDialog.Builder(context).setTitle(R.string.post_viewer_download_dialog_title)
.setPositiveButton(R.string.post_viewer_download_current, clickListener)
.setNegativeButton(R.string.post_viewer_download_album, clickListener).show();
}
break;
case R.id.ivProfilePic:
if (mentionClickListener != null) {
profileModel = feedModel.getProfileModel();
if (profileModel != null)
mentionClickListener.onClick(null, profileModel.getUsername(), false);
}
break;
}
}
}
}
};
private final View.OnLongClickListener longClickListener = v -> {
final Object tag;
if (v instanceof RamboTextView && (tag = v.getTag()) instanceof FeedModel)
Utils.copyText(v.getContext(), ((FeedModel) tag).getPostCaption());
return true;
};
public SimpleExoPlayer pagerPlayer;
private final PlayerChangeListener playerChangeListener = (childPos, player) -> {
// todo
pagerPlayer = player;
private static final DiffUtil.ItemCallback<FeedModel> diffCallback = new DiffUtil.ItemCallback<FeedModel>() {
@Override
public boolean areItemsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
@Override
public boolean areContentsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
};
public FeedAdapter(final Activity activity, final ArrayList<FeedModel> FeedModels, final MentionClickListener mentionClickListener) {
this.activity = activity;
this.feedModels = FeedModels;
public FeedAdapter(final View.OnClickListener clickListener,
final MentionClickListener mentionClickListener) {
super(diffCallback);
// private final static String ellipsize = "… more";
this.clickListener = clickListener;
this.mentionClickListener = mentionClickListener;
this.layoutInflater = LayoutInflater.from(activity);
}
@NonNull
@Override
public FeedItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final View view;
if (viewType == MediaItemType.MEDIA_TYPE_VIDEO.ordinal())
view = layoutInflater.inflate(R.layout.item_feed_video, parent, false);
else if (viewType == MediaItemType.MEDIA_TYPE_SLIDER.ordinal())
view = layoutInflater.inflate(R.layout.item_feed_slider, parent, false);
else
view = layoutInflater.inflate(R.layout.item_feed, parent, false);
return new FeedItemViewHolder(view);
}
@SuppressLint("SetTextI18n")
@Override
public void onBindViewHolder(@NonNull final FeedItemViewHolder viewHolder, final int position) {
final FeedModel feedModel = feedModels.get(position);
if (feedModel != null) {
final RequestManager glideRequestManager = Glide.with(viewHolder.itemView);
feedModel.setPosition(position);
viewHolder.viewPost.setTag(feedModel);
viewHolder.profilePic.setTag(feedModel);
viewHolder.btnDownload.setTag(feedModel);
viewHolder.viewerCaption.setTag(feedModel);
final ProfileModel profileModel = feedModel.getProfileModel();
if (profileModel != null) {
glideRequestManager.load(profileModel.getSdProfilePic()).into(viewHolder.profilePic);
final int titleLen = profileModel.getUsername().length() + 1;
final SpannableString spannableString = new SpannableString("@"+profileModel.getUsername());
spannableString.setSpan(new CommentMentionClickSpan(), 0, titleLen, 0);
viewHolder.username.setText(spannableString);
viewHolder.username.setMovementMethod(new LinkMovementMethod());
viewHolder.username.setMentionClickListener((view, text, isHashtag) ->
mentionClickListener.onClick(null, profileModel.getUsername(), false));
final Context context = parent.getContext();
final LayoutInflater layoutInflater = LayoutInflater.from(context);
final MediaItemType type = MediaItemType.valueOf(viewType);
switch (type) {
case MEDIA_TYPE_VIDEO: {
final ItemFeedVideoBinding binding = ItemFeedVideoBinding.inflate(layoutInflater, parent, false);
return new FeedVideoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
}
viewHolder.viewPost.setOnClickListener(clickListener);
viewHolder.profilePic.setOnClickListener(clickListener);
viewHolder.btnDownload.setOnClickListener(clickListener);
viewHolder.tvPostDate.setText(feedModel.getPostDate());
final long commentsCount = feedModel.getCommentsCount();
viewHolder.commentsCount.setText(String.valueOf(commentsCount));
viewHolder.btnComments.setTag(feedModel);
viewHolder.btnComments.setOnClickListener(clickListener);
viewHolder.btnComments.setEnabled(true);
final JSONObject location = feedModel.getLocation();
if (location == null) {
viewHolder.location.setVisibility(View.GONE);
viewHolder.username.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT
));
case MEDIA_TYPE_SLIDER: {
final ItemFeedSliderBinding binding = ItemFeedSliderBinding.inflate(layoutInflater, parent, false);
return new FeedSliderViewHolder(binding, mentionClickListener, clickListener, longClickListener);
}
else {
viewHolder.location.setVisibility(View.VISIBLE);
viewHolder.location.setText(location.optString("name"));
viewHolder.username.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT
));
viewHolder.location.setOnClickListener(v ->
new AlertDialog.Builder(v.getContext()).setTitle(location.optString("name"))
.setMessage(R.string.comment_view_mention_location_search)
.setNegativeButton(R.string.cancel, null).setPositiveButton(R.string.ok,
(dialog, which) -> mentionClickListener.onClick(null, location.optString("id")+"/"+location.optString("slug"), false)).show()
);
}
final String thumbnailUrl = feedModel.getThumbnailUrl();
final String displayUrl = feedModel.getDisplayUrl();
CharSequence postCaption = feedModel.getPostCaption();
final boolean captionEmpty = Utils.isEmpty(postCaption);
viewHolder.viewerCaption.setOnClickListener(clickListener);
viewHolder.viewerCaption.setOnLongClickListener(longClickListener);
viewHolder.viewerCaption.setVisibility(captionEmpty ? View.GONE : View.VISIBLE);
if (!captionEmpty && Utils.hasMentions(postCaption)) {
postCaption = Utils.getMentionText(postCaption);
feedModel.setPostCaption(postCaption);
viewHolder.viewerCaption.setText(postCaption, TextView.BufferType.SPANNABLE);
viewHolder.viewerCaption.setMentionClickListener(mentionClickListener);
} else {
viewHolder.viewerCaption.setText(postCaption);
}
expandCollapseTextView(viewHolder.viewerCaption, feedModel);
final MediaItemType itemType = feedModel.getItemType();
final View viewToChangeHeight;
if (itemType == MediaItemType.MEDIA_TYPE_VIDEO) {
viewToChangeHeight = viewHolder.playerView;
final Player player = viewHolder.playerView.getPlayer();
if (player != null) {
final boolean shouldAutoplay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
player.setPlayWhenReady(shouldAutoplay);
}
viewHolder.videoViewsParent.setVisibility(View.VISIBLE);
viewHolder.videoViews.setText(String.valueOf(feedModel.getViewCount()));
} else {
viewHolder.videoViewsParent.setVisibility(View.GONE);
viewHolder.btnMute.setVisibility(View.GONE);
if (itemType == MediaItemType.MEDIA_TYPE_SLIDER) {
viewToChangeHeight = viewHolder.mediaList;
final ViewerPostModel[] sliderItems = feedModel.getSliderItems();
final int sliderItemLen = sliderItems != null ? sliderItems.length : 0;
if (sliderItemLen > 0) {
viewHolder.mediaCounter.setText("1/" + sliderItemLen);
viewHolder.mediaList.setOffscreenPageLimit(Math.min(5, sliderItemLen));
final ViewPager.SimpleOnPageChangeListener simpleOnPageChangeListener = new ViewPager.SimpleOnPageChangeListener() {
private int prevPos = 0;
@Override
public void onPageSelected(final int position) {
ViewerPostModel sliderItem = sliderItems[prevPos];
if (sliderItem != null) sliderItem.setSelected(false);
sliderItem = sliderItems[position];
if (sliderItem != null) sliderItem.setSelected(true);
View childAt = viewHolder.mediaList.getChildAt(prevPos);
if (childAt instanceof PlayerView) {
pagerPlayer = (SimpleExoPlayer) ((PlayerView) childAt).getPlayer();
if (pagerPlayer != null) pagerPlayer.setPlayWhenReady(false);
}
childAt = viewHolder.mediaList.getChildAt(position);
if (childAt instanceof PlayerView) {
pagerPlayer = (SimpleExoPlayer) ((PlayerView) childAt).getPlayer();
if (pagerPlayer != null) pagerPlayer.setPlayWhenReady(true);
}
prevPos = position;
viewHolder.mediaCounter.setText((position + 1) + "/" + sliderItemLen);
}
};
//noinspection deprecation
viewHolder.mediaList.setOnPageChangeListener(simpleOnPageChangeListener); // cause add listeners might add to recycled holders
final View.OnClickListener muteClickListener = v -> {
Player player = null;
if (v instanceof PlayerView) player = ((PlayerView) v).getPlayer();
else if (v instanceof ImageView || v == viewHolder.btnMute) {
final int currentItem = viewHolder.mediaList.getCurrentItem();
if (currentItem < viewHolder.mediaList.getChildCount()) {
final View childAt = viewHolder.mediaList.getChildAt(currentItem);
if (childAt instanceof PlayerView) player = ((PlayerView) childAt).getPlayer();
}
} else {
final Object tag = v.getTag();
if (tag instanceof Player) player = (Player) tag;
}
if (player instanceof SimpleExoPlayer) {
final SimpleExoPlayer exoPlayer = (SimpleExoPlayer) player;
final float intVol = exoPlayer.getVolume() == 0f ? 1f : 0f;
exoPlayer.setVolume(intVol);
viewHolder.btnMute.setImageResource(intVol == 0f ? R.drawable.mute : R.drawable.vol);
Utils.sessionVolumeFull = intVol == 1f;
}
};
viewHolder.btnMute.setOnClickListener(muteClickListener);
viewHolder.mediaList.setAdapter(new ChildMediaItemsAdapter(sliderItems, viewHolder.btnMute, playerChangeListener));
}
} else {
viewToChangeHeight = viewHolder.imageView;
String url = displayUrl;
if (Utils.isEmpty(url)) url = thumbnailUrl;
glideRequestManager.load(url).into(viewHolder.imageView);
}
}
if (viewToChangeHeight != null) {
final ViewGroup.LayoutParams layoutParams = viewToChangeHeight.getLayoutParams();
layoutParams.height = Utils.displayMetrics.widthPixels + 1;
viewToChangeHeight.setLayoutParams(layoutParams);
case MEDIA_TYPE_IMAGE:
default: {
final ItemFeedPhotoBinding binding = ItemFeedPhotoBinding.inflate(layoutInflater, parent, false);
return new FeedPhotoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
}
}
}
@Override
public int getItemCount() {
return feedModels == null ? 0 : feedModels.size();
public void onBindViewHolder(@NonNull final FeedItemViewHolder viewHolder, final int position) {
final FeedModel feedModel = getItem(position);
if (feedModel == null) {
return;
}
feedModel.setPosition(position);
viewHolder.bind(feedModel);
}
@Override
public int getItemViewType(final int position) {
if (feedModels != null) return feedModels.get(position).getItemType().ordinal();
return MediaItemType.MEDIA_TYPE_IMAGE.ordinal();
return getItem(position).getItemType().getId();
}
/**
* expands or collapses {@link RamboTextView} [stg idek why i wrote this documentation]
*
* @param textView the {@link RamboTextView} view, to expand and collapse
* @param feedModel the {@link FeedModel} model to check wether model is collapsed to expanded
*
* @return true if expanded/collapsed, false if empty or text size is <= 255 chars
*/
public static boolean expandCollapseTextView(@NonNull final RamboTextView textView, @NonNull final FeedModel feedModel) {
final CharSequence caption = feedModel.getPostCaption();
if (Utils.isEmpty(caption)) return false;
final TextView.BufferType bufferType = caption instanceof Spanned ? TextView.BufferType.SPANNABLE : TextView.BufferType.NORMAL;
if (!feedModel.isCaptionExpanded()) {
int i = Utils.indexOfChar(caption, '\r', 0);
if (i == -1) i = Utils.indexOfChar(caption, '\n', 0);
if (i == -1) i = 255;
final int captionLen = caption.length();
final int minTrim = Math.min(255, i);
if (captionLen <= minTrim) return false;
if (Utils.hasMentions(caption))
textView.setText(Utils.getMentionText(caption), TextView.BufferType.SPANNABLE);
textView.setCaptionIsExpandable(true);
textView.setCaptionIsExpanded(true);
} else {
textView.setText(caption, bufferType);
textView.setCaptionIsExpanded(false);
}
return true;
@Override
public void onViewAttachedToWindow(@NonNull final FeedItemViewHolder holder) {
super.onViewAttachedToWindow(holder);
// Log.d(TAG, "attached holder: " + holder);
if (!(holder instanceof FeedSliderViewHolder)) return;
final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;
feedSliderViewHolder.startPlayingVideo();
}
private interface PlayerChangeListener {
void playerChanged(final int childPos, final SimpleExoPlayer player);
}
private static final class ChildMediaItemsAdapter extends PagerAdapter {
private final PlayerChangeListener playerChangeListener;
private final ViewerPostModel[] sliderItems;
private final View btnMute;
private SimpleExoPlayer player;
private ChildMediaItemsAdapter(final ViewerPostModel[] sliderItems, final View btnMute,
final PlayerChangeListener playerChangeListener) {
this.sliderItems = sliderItems;
this.btnMute = btnMute;
if (BuildConfig.DEBUG) this.playerChangeListener = playerChangeListener;
else this.playerChangeListener = null;
}
@NonNull
@Override
public Object instantiateItem(@NonNull final ViewGroup container, final int position) {
final Context context = container.getContext();
final ViewerPostModel sliderItem = sliderItems[position];
if (sliderItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
if (btnMute != null) btnMute.setVisibility(View.VISIBLE);
final PlayerView playerView = new PlayerView(context);
player = new SimpleExoPlayer.Builder(context).build();
playerView.setPlayer(player);
float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;
player.setVolume(vol);
player.setPlayWhenReady(Utils.settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));
final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(new DefaultDataSourceFactory(context, "instagram"))
.createMediaSource(Uri.parse(sliderItem.getDisplayUrl()));
player.setRepeatMode(Player.REPEAT_MODE_ALL);
player.prepare(mediaSource);
player.setVolume(vol);
playerView.setTag(player);
if (playerChangeListener != null) {
//todo
// playerChangeListener.playerChanged(position, player);
Log.d("AWAISKING_APP", "playerChangeListener: " + playerChangeListener);
}
container.addView(playerView);
return playerView;
} else {
if (btnMute != null) btnMute.setVisibility(View.GONE);
final PhotoView photoView = new PhotoView(context);
photoView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
Glide.with(context).load(sliderItem.getDisplayUrl()).into(photoView);
container.addView(photoView);
return photoView;
}
}
@Override
public void destroyItem(@NonNull final ViewGroup container, final int position, @NonNull final Object object) {
final Player player = object instanceof PlayerView ? ((PlayerView) object).getPlayer() : this.player;
if (player == this.player && this.player != null) {
this.player.stop(true);
this.player.release();
} else if (player != null) {
player.stop(true);
player.release();
}
container.removeView((View) object);
}
@Override
public int getCount() {
return sliderItems != null ? sliderItems.length : 0;
}
@Override
public boolean isViewFromObject(@NonNull final View view, @NonNull final Object object) {
return view == object;
}
@Override
public void onViewDetachedFromWindow(@NonNull final FeedItemViewHolder holder) {
super.onViewDetachedFromWindow(holder);
// Log.d(TAG, "detached holder: " + holder);
if (!(holder instanceof FeedSliderViewHolder)) return;
final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;
feedSliderViewHolder.stopPlayingVideo();
}
}

View File

@ -1,59 +1,51 @@
package awais.instagrabber.adapters;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import com.bumptech.glide.Glide;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.HighlightViewHolder;
import awais.instagrabber.adapters.viewholder.FeedStoryViewHolder;
import awais.instagrabber.databinding.ItemHighlightBinding;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.ProfileModel;
public final class FeedStoriesAdapter extends RecyclerView.Adapter<HighlightViewHolder> {
private final View.OnClickListener clickListener;
private LayoutInflater layoutInflater;
private FeedStoryModel[] feedStoryModels;
public final class FeedStoriesAdapter extends ListAdapter<FeedStoryModel, FeedStoryViewHolder> {
private final OnFeedStoryClickListener listener;
public FeedStoriesAdapter(final FeedStoryModel[] feedStoryModels, final View.OnClickListener clickListener) {
this.feedStoryModels = feedStoryModels;
this.clickListener = clickListener;
private static final DiffUtil.ItemCallback<FeedStoryModel> diffCallback = new DiffUtil.ItemCallback<FeedStoryModel>() {
@Override
public boolean areItemsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId());
}
@Override
public boolean areContentsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId());
}
};
public FeedStoriesAdapter(final OnFeedStoryClickListener listener) {
super(diffCallback);
this.listener = listener;
}
@NonNull
@Override
public HighlightViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
if (layoutInflater == null) layoutInflater = LayoutInflater.from(parent.getContext());
return new HighlightViewHolder(layoutInflater.inflate(R.layout.item_highlight, parent, false));
public FeedStoryViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemHighlightBinding binding = ItemHighlightBinding.inflate(layoutInflater, parent, false);
return new FeedStoryViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final HighlightViewHolder holder, final int position) {
final FeedStoryModel feedStoryModel = feedStoryModels[position];
if (feedStoryModel != null) {
holder.itemView.setTag(feedStoryModel);
holder.itemView.setOnClickListener(clickListener);
final ProfileModel profileModel = feedStoryModel.getProfileModel();
holder.title.setText(profileModel.getUsername());
Glide.with(layoutInflater.getContext()).load(profileModel.getSdProfilePic()).into(holder.icon);
holder.icon.setAlpha(feedStoryModel.getFullyRead() ? 0.5F : 1.0F);
holder.title.setAlpha(feedStoryModel.getFullyRead() ? 0.5F : 1.0F);
}
public void onBindViewHolder(@NonNull final FeedStoryViewHolder holder, final int position) {
final FeedStoryModel model = getItem(position);
holder.bind(model, position, listener);
}
public void setData(final FeedStoryModel[] feedStoryModels) {
this.feedStoryModels = feedStoryModels;
notifyDataSetChanged();
}
@Override
public int getItemCount() {
return feedStoryModels == null ? 0 : feedStoryModels.length;
public interface OnFeedStoryClickListener {
void onFeedStoryClick(FeedStoryModel model, int position);
}
}

View File

@ -1,6 +1,5 @@
package awais.instagrabber.adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -11,16 +10,15 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.util.ArrayList;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.FollowsViewHolder;
import awais.instagrabber.databinding.ItemFollowBinding;
import awais.instagrabber.interfaces.OnGroupClickListener;
import awais.instagrabber.models.FollowModel;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.TextUtils;
import thoughtbot.expandableadapter.ExpandableGroup;
import thoughtbot.expandableadapter.ExpandableList;
import thoughtbot.expandableadapter.ExpandableListPosition;
@ -34,7 +32,7 @@ public final class FollowAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
@Override
protected FilterResults performFiltering(final CharSequence filter) {
if (expandableList.groups != null) {
final boolean isFilterEmpty = Utils.isEmpty(filter);
final boolean isFilterEmpty = TextUtils.isEmpty(filter);
final String query = isFilterEmpty ? null : filter.toString().toLowerCase();
for (int x = 0; x < expandableList.groups.size(); ++x) {
@ -46,25 +44,30 @@ public final class FollowAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
final FollowModel followModel = items.get(i);
if (isFilterEmpty) followModel.setShown(true);
else followModel.setShown(Utils.hasKey(query, followModel.getUsername(), followModel.getFullName()));
else followModel.setShown(hasKey(query, followModel.getUsername(), followModel.getFullName()));
}
}
}
return null;
}
private boolean hasKey(final String key, final String username, final String name) {
if (TextUtils.isEmpty(key)) return true;
final boolean hasUserName = username != null && username.toLowerCase().contains(key);
if (!hasUserName && name != null) return name.toLowerCase().contains(key);
return true;
}
@Override
protected void publishResults(final CharSequence constraint, final FilterResults results) {
notifyDataSetChanged();
}
};
private final View.OnClickListener onClickListener;
private final LayoutInflater layoutInflater;
private final ExpandableList expandableList;
private final boolean hasManyGroups;
public FollowAdapter(final Context context, final View.OnClickListener onClickListener, @NonNull final ArrayList<ExpandableGroup> groups) {
this.layoutInflater = LayoutInflater.from(context);
public FollowAdapter(final View.OnClickListener onClickListener, @NonNull final ArrayList<ExpandableGroup> groups) {
this.expandableList = new ExpandableList(groups);
this.onClickListener = onClickListener;
this.hasManyGroups = groups.size() > 1;
@ -79,10 +82,15 @@ public final class FollowAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final boolean isGroup = hasManyGroups && viewType == ExpandableListPosition.GROUP;
final View view = layoutInflater.inflate(isGroup ? R.layout.header_follow : R.layout.item_follow, parent, false);
return isGroup ? new GroupViewHolder(view, this) : new FollowsViewHolder(view);
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final View view;
if (isGroup) {
view = layoutInflater.inflate(R.layout.header_follow, parent, false);
return new GroupViewHolder(view, this);
} else {
final ItemFollowBinding binding = ItemFollowBinding.inflate(layoutInflater, parent, false);
return new FollowsViewHolder(binding);
}
}
@Override
@ -94,21 +102,10 @@ public final class FollowAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
final GroupViewHolder gvh = (GroupViewHolder) holder;
gvh.setTitle(group.getTitle());
gvh.toggle(isGroupExpanded(group));
} else {
final FollowModel model = group.getItems(true).get(hasManyGroups ? listPos.childPos : position);
final FollowsViewHolder followHolder = (FollowsViewHolder) holder;
if (model != null) {
followHolder.itemView.setTag(model);
followHolder.itemView.setOnClickListener(onClickListener);
followHolder.tvUsername.setText(model.getUsername());
followHolder.tvFullName.setText(model.getFullName());
Glide.with(layoutInflater.getContext()).load(model.getProfilePicUrl()).into(followHolder.profileImage);
}
return;
}
final FollowModel model = group.getItems(true).get(hasManyGroups ? listPos.childPos : position);
((FollowsViewHolder) holder).bind(model, onClickListener);
}
@Override

View File

@ -1,53 +1,55 @@
package awais.instagrabber.adapters;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import com.bumptech.glide.Glide;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.HighlightViewHolder;
import awais.instagrabber.databinding.ItemHighlightBinding;
import awais.instagrabber.models.HighlightModel;
public final class HighlightsAdapter extends RecyclerView.Adapter<HighlightViewHolder> {
private final View.OnClickListener clickListener;
private LayoutInflater layoutInflater;
private HighlightModel[] highlightModels;
public final class HighlightsAdapter extends ListAdapter<HighlightModel, HighlightViewHolder> {
public HighlightsAdapter(final HighlightModel[] highlightModels, final View.OnClickListener clickListener) {
this.highlightModels = highlightModels;
private final OnHighlightClickListener clickListener;
private static final DiffUtil.ItemCallback<HighlightModel> diffCallback = new DiffUtil.ItemCallback<HighlightModel>() {
@Override
public boolean areItemsTheSame(@NonNull final HighlightModel oldItem, @NonNull final HighlightModel newItem) {
return oldItem.getId().equals(newItem.getId());
}
@Override
public boolean areContentsTheSame(@NonNull final HighlightModel oldItem, @NonNull final HighlightModel newItem) {
return oldItem.getId().equals(newItem.getId());
}
};
public HighlightsAdapter(final OnHighlightClickListener clickListener) {
super(diffCallback);
this.clickListener = clickListener;
}
@NonNull
@Override
public HighlightViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
if (layoutInflater == null) layoutInflater = LayoutInflater.from(parent.getContext());
return new HighlightViewHolder(layoutInflater.inflate(R.layout.item_highlight, parent, false));
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemHighlightBinding binding = ItemHighlightBinding.inflate(layoutInflater, parent, false);
return new HighlightViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final HighlightViewHolder holder, final int position) {
final HighlightModel highlightModel = highlightModels[position];
if (highlightModel != null) {
holder.itemView.setTag(highlightModel);
holder.itemView.setOnClickListener(clickListener);
holder.title.setText(highlightModel.getTitle());
Glide.with(holder.itemView).load(highlightModel.getThumbnailUrl()).into(holder.icon);
final HighlightModel highlightModel = getItem(position);
if (clickListener != null) {
holder.itemView.setOnClickListener(v -> clickListener.onHighlightClick(highlightModel, position));
}
holder.bind(highlightModel);
}
public void setData(final HighlightModel[] highlightModels) {
this.highlightModels = highlightModels;
notifyDataSetChanged();
}
@Override
public int getItemCount() {
return highlightModels == null ? 0 : highlightModels.length;
public interface OnHighlightClickListener {
void onHighlightClick(final HighlightModel model, final int position);
}
}

View File

@ -0,0 +1,104 @@
package awais.instagrabber.adapters;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public abstract class MultiSelectListAdapter<T extends MultiSelectListAdapter.Selectable, VH extends RecyclerView.ViewHolder> extends
ListAdapter<T, VH> {
private boolean isSelecting;
private final OnItemClickListener<T> internalOnItemClickListener;
private final OnItemLongClickListener<T> internalOnLongItemClickListener;
private final List<T> selectedItems = new ArrayList<>();
protected MultiSelectListAdapter(@NonNull final DiffUtil.ItemCallback<T> diffCallback,
final OnItemClickListener<T> clickListener,
final OnItemLongClickListener<T> 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<T> getInternalOnItemClickListener() {
return internalOnItemClickListener;
}
public OnItemLongClickListener<T> 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<T> 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<T> {
void onItemClick(T item, int position);
}
public interface OnItemLongClickListener<T> {
boolean onItemLongClick(T item, int position);
}
}

View File

@ -1,91 +1,55 @@
package awais.instagrabber.adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestManager;
import com.bumptech.glide.request.RequestOptions;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.NotificationViewHolder;
import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.NotificationModel;
import awais.instagrabber.models.enums.NotificationType;
public final class NotificationsAdapter extends RecyclerView.Adapter<NotificationViewHolder> {
private final View.OnClickListener onClickListener;
public final class NotificationsAdapter extends ListAdapter<NotificationModel, NotificationViewHolder> {
private final OnNotificationClickListener notificationClickListener;
private final MentionClickListener mentionClickListener;
private final NotificationModel[] notificationModels;
private LayoutInflater layoutInflater;
public NotificationsAdapter(final NotificationModel[] notificationModels, final View.OnClickListener onClickListener,
private static final DiffUtil.ItemCallback<NotificationModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<NotificationModel>() {
@Override
public boolean areItemsTheSame(@NonNull final NotificationModel oldItem, @NonNull final NotificationModel newItem) {
return oldItem.getId().equals(newItem.getId());
}
@Override
public boolean areContentsTheSame(@NonNull final NotificationModel oldItem, @NonNull final NotificationModel newItem) {
return oldItem.getId().equals(newItem.getId());
}
};
public NotificationsAdapter(final OnNotificationClickListener notificationClickListener,
final MentionClickListener mentionClickListener) {
this.notificationModels = notificationModels;
this.onClickListener = onClickListener;
super(DIFF_CALLBACK);
this.notificationClickListener = notificationClickListener;
this.mentionClickListener = mentionClickListener;
}
@NonNull
@Override
public NotificationViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
final Context context = parent.getContext();
if (layoutInflater == null) layoutInflater = LayoutInflater.from(context);
return new NotificationViewHolder(layoutInflater.inflate(R.layout.item_notification,
parent, false), onClickListener, mentionClickListener);
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemNotificationBinding binding = ItemNotificationBinding.inflate(layoutInflater, parent, false);
return new NotificationViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final NotificationViewHolder holder, final int position) {
final NotificationModel notificationModel = notificationModels[position];
if (notificationModel != null) {
holder.setNotificationModel(notificationModel);
int text = -1;
CharSequence subtext = null;
switch (notificationModel.getType()) {
case LIKE:
text = R.string.liked_notif;
break;
case COMMENT:
text = R.string.comment_notif;
subtext = notificationModel.getText();
break;
case MENTION:
text = R.string.mention_notif;
subtext = notificationModel.getText();
break;
case FOLLOW:
text = R.string.follow_notif;
break;
case REQUEST:
text = R.string.request_notif;
subtext = notificationModel.getText();
break;
}
holder.setCommment(text);
holder.setSubCommment(subtext);
if (notificationModel.getType() != NotificationType.REQUEST)
holder.setDate(notificationModel.getDateTime());
holder.setUsername(notificationModel.getUsername());
final RequestManager rm = Glide.with(layoutInflater.getContext())
.applyDefaultRequestOptions(new RequestOptions().skipMemoryCache(true));
rm.load(notificationModel.getProfilePic()).into(holder.getProfilePicView());
rm.load(notificationModel.getPreviewPic()).into(holder.getPreviewPicView());
}
final NotificationModel notificationModel = getItem(position);
holder.bind(notificationModel, notificationClickListener);
}
@Override
public int getItemCount() {
return notificationModels == null ? 0 : notificationModels.length;
public interface OnNotificationClickListener {
void onNotificationClick(final NotificationModel model);
}
}

View File

@ -0,0 +1,77 @@
package awais.instagrabber.adapters;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import java.util.Arrays;
import awais.instagrabber.adapters.viewholder.PostViewerViewHolder;
import awais.instagrabber.databinding.ItemFullPostViewBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.ViewerPostModelWrapper;
public class PostViewAdapter extends ListAdapter<ViewerPostModelWrapper, PostViewerViewHolder> {
private final OnPostViewChildViewClickListener clickListener;
private final OnPostCaptionLongClickListener longClickListener;
private final MentionClickListener mentionClickListener;
private static final DiffUtil.ItemCallback<ViewerPostModelWrapper> diffCallback = new DiffUtil.ItemCallback<ViewerPostModelWrapper>() {
@Override
public boolean areItemsTheSame(@NonNull final ViewerPostModelWrapper oldItem,
@NonNull final ViewerPostModelWrapper newItem) {
return oldItem.getPosition() == newItem.getPosition();
}
@Override
public boolean areContentsTheSame(@NonNull final ViewerPostModelWrapper oldItem,
@NonNull final ViewerPostModelWrapper newItem) {
return Arrays.equals(oldItem.getViewerPostModels(), newItem.getViewerPostModels());
}
};
public PostViewAdapter(final OnPostViewChildViewClickListener clickListener,
final OnPostCaptionLongClickListener longClickListener,
final MentionClickListener mentionClickListener) {
super(diffCallback);
this.clickListener = clickListener;
this.longClickListener = longClickListener;
this.mentionClickListener = mentionClickListener;
}
@Override
public void onViewDetachedFromWindow(@NonNull final PostViewerViewHolder holder) {
holder.stopPlayingVideo();
}
@NonNull
@Override
public PostViewerViewHolder onCreateViewHolder(@NonNull final ViewGroup parent,
final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemFullPostViewBinding binding = ItemFullPostViewBinding
.inflate(layoutInflater, parent, false);
return new PostViewerViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final PostViewerViewHolder holder, final int position) {
final ViewerPostModelWrapper item = getItem(position);
holder.bind(item, position, clickListener, longClickListener, mentionClickListener);
}
public interface OnPostViewChildViewClickListener {
void onClick(View v,
ViewerPostModelWrapper viewerPostModelWrapper,
int postPosition,
int childPosition);
}
public interface OnPostCaptionLongClickListener {
void onLongClick(String text);
}
}

View File

@ -0,0 +1,203 @@
package awais.instagrabber.adapters;
import android.net.Uri;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import awais.instagrabber.customviews.drawee.ZoomableDraweeView;
import awais.instagrabber.models.ViewerPostModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
public class PostViewerChildAdapter extends ListAdapter<ViewerPostModel, PostViewerChildAdapter.ChildViewHolder> {
private static final DiffUtil.ItemCallback<ViewerPostModel> diffCallback = new DiffUtil.ItemCallback<ViewerPostModel>() {
@Override
public boolean areItemsTheSame(@NonNull final ViewerPostModel oldItem, @NonNull final ViewerPostModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId()) && oldItem.getShortCode().equals(newItem.getShortCode());
}
@Override
public boolean areContentsTheSame(@NonNull final ViewerPostModel oldItem, @NonNull final ViewerPostModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId()) && oldItem.getShortCode().equals(newItem.getShortCode());
}
};
public PostViewerChildAdapter() {
super(diffCallback);
}
@NonNull
@Override
public ChildViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
// final AppCompatTextView textView = new AppCompatTextView(parent.getContext());
// textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
// return new ChildViewHolder(textView);
final MediaItemType mediaItemType = MediaItemType.valueOf(viewType);
if (mediaItemType == null) return getPlaceholder(parent);
switch (mediaItemType) {
case MEDIA_TYPE_IMAGE:
return getImageViewHolder(parent);
case MEDIA_TYPE_VIDEO:
return getVideoViewHolder(parent);
default:
return getPlaceholder(parent);
}
}
private ChildViewHolder getImageViewHolder(final ViewGroup parent) {
final ZoomableDraweeView view = new ZoomableDraweeView(parent.getContext());
view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return new ChildViewHolder(view);
}
private ChildViewHolder getVideoViewHolder(final ViewGroup parent) {
final PlayerView view = new PlayerView(parent.getContext());
view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return new ChildViewHolder(view);
}
private ChildViewHolder getPlaceholder(final ViewGroup parent) {
final AppCompatTextView textView = new AppCompatTextView(parent.getContext());
textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
textView.setGravity(Gravity.CENTER);
textView.setText("Placeholder");
return new ChildViewHolder(textView);
}
@Override
public void onBindViewHolder(@NonNull final ChildViewHolder holder, final int position) {
holder.bind(getItem(position));
}
@Override
public int getItemViewType(final int position) {
final ViewerPostModel item = getItem(position);
return item.getItemType().getId();
}
@Override
public void onViewDetachedFromWindow(@NonNull final ChildViewHolder holder) {
if (holder.itemView instanceof PlayerView) {
final Player player = ((PlayerView) holder.itemView).getPlayer();
if (player != null) {
player.setPlayWhenReady(false);
}
}
}
@Override
public void onViewRecycled(@NonNull final ChildViewHolder holder) {
if (holder.itemView instanceof PlayerView) {
final Player player = ((PlayerView) holder.itemView).getPlayer();
if (player != null) {
player.release();
}
return;
}
if (holder.itemView instanceof ZoomableDraweeView) {
((ZoomableDraweeView) holder.itemView).setController(null);
}
}
public static class ChildViewHolder extends RecyclerView.ViewHolder {
public ChildViewHolder(@NonNull final View itemView) {
super(itemView);
}
public void bind(final ViewerPostModel item) {
final MediaItemType mediaItemType = item.getItemType();
switch (mediaItemType) {
case MEDIA_TYPE_IMAGE:
bindImage(item);
break;
case MEDIA_TYPE_VIDEO:
bindVideo(item);
break;
default:
}
}
private void bindImage(final ViewerPostModel item) {
final ZoomableDraweeView imageView = (ZoomableDraweeView) itemView;
imageView.setController(null);
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(item.getDisplayUrl()))
.setLocalThumbnailPreviewsEnabled(true)
.setProgressiveRenderingEnabled(true)
.build();
final DraweeController controller = Fresco.newDraweeControllerBuilder()
.setImageRequest(requestBuilder)
.setOldController(imageView.getController())
// .setControllerListener(new BaseControllerListener<ImageInfo>() {
//
// @Override
// public void onFailure(final String id, final Throwable throwable) {
// // viewerBinding.progressView.setVisibility(View.GONE);
// }
//
// @Override
// public void onFinalImageSet(final String id, final ImageInfo imageInfo, final Animatable animatable) {
// // viewerBinding.progressView.setVisibility(View.GONE);
// }
// })
.build();
imageView.setController(controller);
}
private void bindVideo(final ViewerPostModel item) {
final SimpleExoPlayer player = new SimpleExoPlayer.Builder(itemView.getContext()).build();
final PlayerView playerView = (PlayerView) itemView;
playerView.setPlayer(player);
float vol = Utils.settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;
player.setVolume(vol);
player.setPlayWhenReady(Utils.settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));
final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(new DefaultDataSourceFactory(itemView.getContext(), "instagram"))
.createMediaSource(Uri.parse(item.getDisplayUrl()));
// mediaSource.addEventListener(new Handler(), new MediaSourceEventListener() {
// @Override
// public void onLoadCompleted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
// viewerBinding.progressView.setVisibility(View.GONE);
// }
//
// @Override
// public void onLoadStarted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
// viewerBinding.progressView.setVisibility(View.VISIBLE);
// }
//
// @Override
// public void onLoadCanceled(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
// viewerBinding.progressView.setVisibility(View.GONE);
// }
//
// @Override
// public void onLoadError(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData, final IOException error, final boolean wasCanceled) {
// viewerBinding.progressView.setVisibility(View.GONE);
// }
// });
player.prepare(mediaSource);
player.setVolume(vol);
// viewerBinding.bottomPanel.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
// viewerBinding.bottomPanel.btnMute.setOnClickListener(onClickListener);
}
}
}

View File

@ -1,92 +1,45 @@
package awais.instagrabber.adapters;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.DiffUtil;
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.util.ArrayList;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.PostViewHolder;
import awais.instagrabber.databinding.ItemPostBinding;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.enums.MediaItemType;
public final class PostsAdapter extends RecyclerView.Adapter<PostViewHolder> {
private final ArrayList<PostModel> postModels;
private final View.OnClickListener clickListener;
private final View.OnLongClickListener longClickListener;
private LayoutInflater layoutInflater;
public boolean isSelecting = false;
public final class PostsAdapter extends MultiSelectListAdapter<PostModel, PostViewHolder> {
public PostsAdapter(final ArrayList<PostModel> postModels, final View.OnClickListener clickListener,
final View.OnLongClickListener longClickListener) {
this.postModels = postModels;
this.clickListener = clickListener;
this.longClickListener = longClickListener;
private static final DiffUtil.ItemCallback<PostModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<PostModel>() {
@Override
public boolean areItemsTheSame(@NonNull final PostModel oldItem, @NonNull final PostModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
@Override
public boolean areContentsTheSame(@NonNull final PostModel oldItem, @NonNull final PostModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
};
public PostsAdapter(final OnItemClickListener<PostModel> clickListener,
final OnItemLongClickListener<PostModel> longClickListener) {
super(DIFF_CALLBACK, clickListener, longClickListener);
}
@NonNull
@Override
public PostViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
if (layoutInflater == null) layoutInflater = LayoutInflater.from(parent.getContext());
return new PostViewHolder(layoutInflater.inflate(R.layout.item_post, parent, false));
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemPostBinding binding = ItemPostBinding.inflate(layoutInflater, parent, false);
return new PostViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final PostViewHolder holder, final int position) {
final PostModel postModel = postModels.get(position);
if (postModel != null) {
postModel.setPosition(position);
holder.itemView.setTag(postModel);
holder.itemView.setOnClickListener(clickListener);
holder.itemView.setOnLongClickListener(longClickListener);
final MediaItemType itemType = postModel.getItemType();
final boolean isSlider = itemType == MediaItemType.MEDIA_TYPE_SLIDER;
holder.isDownloaded.setVisibility(postModel.isDownloaded() ? View.VISIBLE : View.GONE);
holder.typeIcon.setVisibility(itemType == MediaItemType.MEDIA_TYPE_VIDEO || isSlider ? View.VISIBLE : View.GONE);
holder.typeIcon.setImageResource(isSlider ? R.drawable.slider : R.drawable.video);
holder.selectedView.setVisibility(postModel.isSelected() ? View.VISIBLE : View.GONE);
holder.progressView.setVisibility(View.VISIBLE);
final RequestManager glideRequestManager = Glide.with(holder.postImage);
glideRequestManager.load(postModel.getThumbnailUrl()).listener(new RequestListener<Drawable>() {
@Override
public boolean onResourceReady(final Drawable resource, final Object model, final Target<Drawable> target, final DataSource dataSource, final boolean isFirstResource) {
holder.progressView.setVisibility(View.GONE);
return false;
}
@Override
public boolean onLoadFailed(@Nullable final GlideException e, final Object model, final Target<Drawable> target, final boolean isFirstResource) {
holder.progressView.setVisibility(View.GONE);
// glideRequestManager.load(postModel.getDisplayUrl()).into(holder.postImage);
return false;
}
}).into(holder.postImage);
}
}
@Override
public int getItemCount() {
return postModels == null ? 0 : postModels.size();
final PostModel postModel = getItem(position);
holder.bind(postModel, position, getInternalOnItemClickListener(), getInternalOnLongItemClickListener());
}
}

View File

@ -7,16 +7,14 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.PostMediaViewHolder;
import awais.instagrabber.databinding.ItemChildPostBinding;
import awais.instagrabber.models.BasePostModel;
import awais.instagrabber.models.ViewerPostModel;
public final class PostsMediaAdapter extends RecyclerView.Adapter<PostMediaViewHolder> {
private final View.OnClickListener clickListener;
private LayoutInflater layoutInflater;
private ViewerPostModel[] postModels;
public PostsMediaAdapter(final ViewerPostModel[] postModels, final View.OnClickListener clickListener) {
@ -27,25 +25,16 @@ public final class PostsMediaAdapter extends RecyclerView.Adapter<PostMediaViewH
@NonNull
@Override
public PostMediaViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
if (layoutInflater == null) layoutInflater = LayoutInflater.from(parent.getContext());
return new PostMediaViewHolder(layoutInflater.inflate(R.layout.item_child_post, parent, false));
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
layoutInflater.inflate(R.layout.item_child_post, parent, false);
final ItemChildPostBinding binding = ItemChildPostBinding.inflate(layoutInflater, parent, false);
return new PostMediaViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final PostMediaViewHolder holder, final int position) {
final ViewerPostModel postModel = postModels[position];
if (postModel != null) {
postModel.setPosition(position);
holder.itemView.setTag(postModel);
holder.itemView.setOnClickListener(clickListener);
holder.selectedView.setVisibility(postModel.isCurrentSlide() ? View.VISIBLE : View.GONE);
holder.isDownloaded.setVisibility(postModel.isDownloaded() ? View.VISIBLE : View.GONE);
Glide.with(layoutInflater.getContext()).load(postModel.getSliderDisplayUrl()).into(holder.icon);
}
holder.bind(postModel, position, clickListener);
}
public void setData(final ViewerPostModel[] postModels) {

View File

@ -1,84 +1,80 @@
package awais.instagrabber.adapters;
import android.content.Context;
import android.content.res.Resources;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import awais.instagrabber.R;
import awais.instagrabber.databinding.ItemStoryBinding;
import awais.instagrabber.models.StoryModel;
public final class StoriesAdapter extends RecyclerView.Adapter<StoriesAdapter.StoryViewHolder> {
private final View.OnClickListener clickListener;
private LayoutInflater layoutInflater;
private StoryModel[] storyModels;
private Resources resources;
private int width, height;
public final class StoriesAdapter extends ListAdapter<StoryModel, StoriesAdapter.StoryViewHolder> {
private final OnItemClickListener onItemClickListener;
public StoriesAdapter(final StoryModel[] storyModels, final View.OnClickListener clickListener) {
this.storyModels = storyModels;
this.clickListener = clickListener;
private static final DiffUtil.ItemCallback<StoryModel> diffCallback = new DiffUtil.ItemCallback<StoryModel>() {
@Override
public boolean areItemsTheSame(@NonNull final StoryModel oldItem, @NonNull final StoryModel newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId());
}
@Override
public boolean areContentsTheSame(@NonNull final StoryModel oldItem, @NonNull final StoryModel newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId());
}
};
public StoriesAdapter(final OnItemClickListener onItemClickListener) {
super(diffCallback);
this.onItemClickListener = onItemClickListener;
}
@NonNull
@Override
public StoryViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final Context context = parent.getContext();
if (layoutInflater == null) layoutInflater = LayoutInflater.from(context);
if (resources == null) resources = context.getResources();
height = Math.round(resources.getDimension(R.dimen.story_item_height));
width = Math.round(resources.getDimension(R.dimen.story_item_width));
return new StoryViewHolder(layoutInflater.inflate(R.layout.item_story, parent, false));
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemStoryBinding binding = ItemStoryBinding.inflate(layoutInflater, parent, false);
return new StoryViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final StoryViewHolder holder, final int position) {
final StoryModel storyModel = storyModels[position];
if (storyModel != null) {
storyModel.setPosition(position);
holder.itemView.setTag(storyModel);
holder.itemView.setOnClickListener(clickListener);
holder.selectedView.setVisibility(storyModel.isCurrentSlide() ? View.VISIBLE : View.GONE);
Glide.with(holder.itemView).load(storyModel.getStoryUrl())
.apply(new RequestOptions().override(width, height))
.into(holder.icon);
}
}
public void setData(final StoryModel[] storyModels) {
this.storyModels = storyModels;
notifyDataSetChanged();
}
public StoryModel getItemAt(final int position) {
return storyModels == null ? null : storyModels[position];
}
@Override
public int getItemCount() {
return storyModels == null ? 0 : storyModels.length;
final StoryModel storyModel = getItem(position);
holder.bind(storyModel, position, onItemClickListener);
}
public final static class StoryViewHolder extends RecyclerView.ViewHolder {
public final ImageView icon, selectedView;
private final ItemStoryBinding binding;
public StoryViewHolder(@NonNull final View itemView) {
super(itemView);
selectedView = itemView.findViewById(R.id.selectedView);
icon = itemView.findViewById(R.id.icon);
public StoryViewHolder(final ItemStoryBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final StoryModel model,
final int position,
final OnItemClickListener clickListener) {
if (model == null) return;
model.setPosition(position);
itemView.setTag(model);
itemView.setOnClickListener(v -> {
if (clickListener == null) return;
clickListener.onItemClick(model, position);
});
binding.selectedView.setVisibility(model.isCurrentSlide() ? View.VISIBLE : View.GONE);
binding.icon.setImageURI(model.getStoryUrl());
// Glide.with(itemView).load(model.getStoryUrl())
// .apply(new RequestOptions().override(width, height))
// .into(holder.icon);
}
}
public interface OnItemClickListener {
void onItemClick(StoryModel storyModel, int position);
}
}

View File

@ -2,60 +2,87 @@ package awais.instagrabber.adapters;
import android.content.Context;
import android.database.Cursor;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cursoradapter.widget.CursorAdapter;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestManager;
import com.bumptech.glide.request.RequestOptions;
import awais.instagrabber.R;
import awais.instagrabber.databinding.ItemSuggestionBinding;
import awais.instagrabber.models.enums.SuggestionType;
public final class SuggestionsAdapter extends CursorAdapter {
private final LayoutInflater layoutInflater;
private final View.OnClickListener onClickListener;
private final RequestManager glideRequestManager;
private static final String TAG = "SuggestionsAdapter";
public SuggestionsAdapter(final Context context, final View.OnClickListener onClickListener) {
private final OnSuggestionClickListener onSuggestionClickListener;
public SuggestionsAdapter(final Context context,
final OnSuggestionClickListener onSuggestionClickListener) {
super(context, null, FLAG_REGISTER_CONTENT_OBSERVER);
this.glideRequestManager = Glide.with(context);
this.layoutInflater = LayoutInflater.from(context);
this.onClickListener = onClickListener;
this.onSuggestionClickListener = onSuggestionClickListener;
}
@Override
public View newView(final Context context, final Cursor cursor, final ViewGroup parent) {
return layoutInflater.inflate(R.layout.item_suggestion, parent, false);
final LayoutInflater layoutInflater = LayoutInflater.from(context);
final ItemSuggestionBinding binding = ItemSuggestionBinding.inflate(layoutInflater, parent, false);
return binding.getRoot();
// return layoutInflater.inflate(R.layout.item_suggestion, parent, false);
}
@Override
public void bindView(@NonNull final View view, final Context context, @NonNull final Cursor cursor) {
// i, username, fullname, type, picUrl, verified
// 0, 1 , 2 , 3 , 4 , 5
final String fullname = cursor.getString(2);
final String fullName = cursor.getString(2);
String username = cursor.getString(1);
final String picUrl = cursor.getString(4);
final boolean verified = cursor.getString(5).charAt(0) == 't';
if ("TYPE_HASHTAG".equals(cursor.getString(3))) username = '#' + username;
else if ("TYPE_USER".equals(cursor.getString(3))) username = '@' + username;
final String type = cursor.getString(3);
SuggestionType suggestionType = null;
try {
suggestionType = SuggestionType.valueOf(type);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Unknown suggestion type: " + type, e);
}
if (suggestionType == null) return;
final String query;
switch (suggestionType) {
case TYPE_USER:
username = '@' + username;
query = username;
break;
case TYPE_HASHTAG:
username = '#' + username;
query = username;
break;
case TYPE_LOCATION:
query = fullName;
break;
default:
return; // will never come here
}
view.setOnClickListener(onClickListener);
view.setTag("TYPE_LOCATION".equals(cursor.getString(3)) ? fullname : username);
if (onSuggestionClickListener != null) {
final SuggestionType finalSuggestionType = suggestionType;
view.setOnClickListener(v -> onSuggestionClickListener.onSuggestionClick(finalSuggestionType, query));
}
final ItemSuggestionBinding binding = ItemSuggestionBinding.bind(view);
binding.isVerified.setVisibility(verified ? View.VISIBLE : View.GONE);
binding.tvUsername.setText(username);
if (suggestionType.equals(SuggestionType.TYPE_LOCATION)) {
binding.tvFullName.setVisibility(View.GONE);
} else {
binding.tvFullName.setVisibility(View.VISIBLE);
binding.tvFullName.setText(fullName);
}
binding.ivProfilePic.setImageURI(picUrl);
}
view.findViewById(R.id.isVerified).setVisibility(verified ? View.VISIBLE : View.GONE);
((TextView) view.findViewById(R.id.tvUsername)).setText(username);
((TextView) view.findViewById(R.id.tvFullName)).setText(fullname);
glideRequestManager.applyDefaultRequestOptions(new RequestOptions().skipMemoryCache(true))
.load(picUrl == null ? R.drawable.ic_location : picUrl).into((ImageView) view.findViewById(R.id.ivProfilePic));
public interface OnSuggestionClickListener {
void onSuggestionClick(final SuggestionType type, final String query);
}
}

View File

@ -2,12 +2,13 @@ package awais.instagrabber.adapters.viewholder;
import android.text.Spannable;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.view.SimpleDraweeView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.CommentsAdapter;
import awais.instagrabber.customviews.RamboTextView;
@ -17,11 +18,16 @@ import awais.instagrabber.models.CommentModel;
public final class CommentViewHolder extends RecyclerView.ViewHolder {
private final MentionClickListener mentionClickListener;
private final RecyclerView rvChildComments;
private final ImageView ivProfilePic;
private final TextView tvUsername, tvDate, tvComment, tvLikes;
private final SimpleDraweeView ivProfilePic;
private final TextView tvUsername;
private final TextView tvDate;
private final TextView tvComment;
private final TextView tvLikes;
private final View container;
public CommentViewHolder(@NonNull final View itemView, final View.OnClickListener onClickListener, final MentionClickListener mentionClickListener) {
public CommentViewHolder(@NonNull final View itemView,
final View.OnClickListener onClickListener,
final MentionClickListener mentionClickListener) {
super(itemView);
container = itemView.findViewById(R.id.container);
@ -41,7 +47,7 @@ public final class CommentViewHolder extends RecyclerView.ViewHolder {
rvChildComments = itemView.findViewById(R.id.rvChildComments);
}
public final ImageView getProfilePicView() {
public final SimpleDraweeView getProfilePicView() {
return ivProfilePic;
}
@ -69,9 +75,9 @@ public final class CommentViewHolder extends RecyclerView.ViewHolder {
if (liked) container.setBackgroundColor(0x40FF69B4);
}
public final void setCommment(final CharSequence commment) {
public final void setComment(final CharSequence comment) {
if (tvComment != null) {
tvComment.setText(commment, commment instanceof Spannable ? TextView.BufferType.SPANNABLE : TextView.BufferType.NORMAL);
tvComment.setText(comment, comment instanceof Spannable ? TextView.BufferType.SPANNABLE : TextView.BufferType.NORMAL);
((RamboTextView) tvComment).setMentionClickListener(mentionClickListener);
}
}

View File

@ -1,19 +1,22 @@
package awais.instagrabber.adapters.viewholder;
import android.content.Context;
import android.net.Uri;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.core.text.HtmlCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestManager;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.view.SimpleDraweeView;
import com.facebook.imagepipeline.common.ResizeOptions;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutIncludeSimpleItemBinding;
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.direct_messages.InboxThreadModel;
@ -21,19 +24,18 @@ import awais.instagrabber.models.enums.DirectItemType;
public final class DirectMessageInboxItemViewHolder extends RecyclerView.ViewHolder {
private final LinearLayout multipleProfilePicsContainer;
private final ImageView[] multipleProfilePics;
private final LayoutIncludeSimpleItemBinding binding;
private final SimpleDraweeView[] multipleProfilePics;
private final LayoutDmInboxItemBinding binding;
public DirectMessageInboxItemViewHolder(@NonNull final LayoutIncludeSimpleItemBinding binding) {
public DirectMessageInboxItemViewHolder(@NonNull final LayoutDmInboxItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
binding.tvLikes.setVisibility(View.GONE);
multipleProfilePicsContainer = binding.container;
multipleProfilePicsContainer = binding.multiPicContainer;
final LinearLayout containerChild = (LinearLayout) multipleProfilePicsContainer.getChildAt(1);
multipleProfilePics = new ImageView[]{
(ImageView) multipleProfilePicsContainer.getChildAt(0),
(ImageView) containerChild.getChildAt(0),
(ImageView) containerChild.getChildAt(1)
multipleProfilePics = new SimpleDraweeView[]{
(SimpleDraweeView) multipleProfilePicsContainer.getChildAt(0),
(SimpleDraweeView) containerChild.getChildAt(0),
(SimpleDraweeView) containerChild.getChildAt(1)
};
binding.tvDate.setSelected(true);
binding.tvUsername.setSelected(true);
@ -45,22 +47,35 @@ public final class DirectMessageInboxItemViewHolder extends RecyclerView.ViewHol
return;
}
itemView.setTag(model);
final RequestManager glideRequestManager = Glide.with(itemView);
final ProfileModel[] users = model.getUsers();
if (users.length > 1) {
binding.ivProfilePic.setVisibility(View.GONE);
multipleProfilePicsContainer.setVisibility(View.VISIBLE);
for (int i = 0; i < Math.min(3, users.length); ++i)
glideRequestManager.load(users[i].getSdProfilePic()).into(multipleProfilePics[i]);
for (int i = 0; i < Math.min(3, users.length); ++i) {
multipleProfilePics[i].setImageURI(users[i].getSdProfilePic());
}
} else {
binding.ivProfilePic.setVisibility(View.VISIBLE);
multipleProfilePicsContainer.setVisibility(View.GONE);
glideRequestManager.load(users.length == 1 ? users[0].getSdProfilePic() : null).into(binding.ivProfilePic);
final String uriString = users.length == 1 ? users[0].getSdProfilePic() : null;
if (uriString == null) {
binding.ivProfilePic.setVisibility(View.GONE);
} else {
binding.ivProfilePic.setVisibility(View.VISIBLE);
multipleProfilePicsContainer.setVisibility(View.GONE);
final ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(uriString))
.setResizeOptions(new ResizeOptions(50, 50))
.build();
binding.ivProfilePic.setController(
Fresco.newDraweeControllerBuilder()
.setOldController(binding.ivProfilePic.getController())
.setImageRequest(request)
.build()
);
}
}
binding.tvUsername.setText(model.getThreadTitle());
final DirectItemModel lastItemModel = itemModels[itemModels.length - 1];
final DirectItemType itemType = lastItemModel.getItemType();
binding.notTextType.setVisibility(itemType != DirectItemType.TEXT ? View.VISIBLE : View.GONE);
// binding.notTextType.setVisibility(itemType != DirectItemType.TEXT ? View.VISIBLE : View.GONE);
final Context context = itemView.getContext();
final CharSequence messageText;
switch (itemType) {
@ -104,6 +119,6 @@ public final class DirectMessageInboxItemViewHolder extends RecyclerView.ViewHol
}
binding.tvComment.setText(HtmlCompat.fromHtml(messageText.toString(), HtmlCompat.FROM_HTML_MODE_COMPACT));
binding.tvDate.setText(lastItemModel.getDateTime());
binding.unread.setVisibility(model.getUnreadCount() > 0L ? View.VISIBLE : View.GONE);
binding.unread.setVisibility(model.getUnreadCount() > 0L ? View.VISIBLE : View.INVISIBLE);
}
}

View File

@ -6,17 +6,21 @@ import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.view.SimpleDraweeView;
import awais.instagrabber.R;
public final class DiscoverViewHolder extends RecyclerView.ViewHolder {
public final ImageView postImage, typeIcon;
public final View selectedView, progressView;
public final SimpleDraweeView postImage;
public final ImageView typeIcon;
public final View selectedView;
// public final View progressView;
public DiscoverViewHolder(@NonNull final View itemView) {
super(itemView);
typeIcon = itemView.findViewById(R.id.typeIcon);
postImage = itemView.findViewById(R.id.postImage);
selectedView = itemView.findViewById(R.id.selectedView);
progressView = itemView.findViewById(R.id.progressView);
// progressView = itemView.findViewById(R.id.progressView);
}
}

View File

@ -1,53 +0,0 @@
package awais.instagrabber.adapters.viewholder;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.ViewPager;
import com.github.chrisbanes.photoview.PhotoView;
import com.google.android.exoplayer2.ui.PlayerView;
import awais.instagrabber.R;
import awais.instagrabber.customviews.RamboTextView;
public final class FeedItemViewHolder extends RecyclerView.ViewHolder {
public final ImageView profilePic, btnMute, btnDownload;
public final TextView commentsCount, videoViews, mediaCounter, tvPostDate, location;
public final RamboTextView username, viewerCaption;
public final View btnComments, videoViewsParent, viewPost;
public final ViewPager mediaList;
public final PhotoView imageView;
public final PlayerView playerView;
public FeedItemViewHolder(@NonNull final View itemView) {
super(itemView);
// common
viewerCaption = itemView.findViewById(R.id.viewerCaption);
btnDownload = itemView.findViewById(R.id.btnDownload);
btnComments = itemView.findViewById(R.id.btnComments);
profilePic = itemView.findViewById(R.id.ivProfilePic);
tvPostDate = itemView.findViewById(R.id.tvPostDate);
viewPost = itemView.findViewById(R.id.viewStoryPost);
username = itemView.findViewById(R.id.title);
location = itemView.findViewById(R.id.location);
// video view
btnMute = itemView.findViewById(R.id.btnMute);
videoViews = itemView.findViewById(R.id.tvVideoViews);
commentsCount = btnComments.findViewById(R.id.commentsCount);
videoViewsParent = videoViews != null ? (View) videoViews.getParent() : null;
// slider view
mediaCounter = itemView.findViewById(R.id.mediaCounter);
// different types
mediaList = itemView.findViewById(R.id.media_list);
imageView = itemView.findViewById(R.id.imageViewer);
playerView = itemView.findViewById(R.id.playerView);
}
}

View File

@ -0,0 +1,33 @@
package awais.instagrabber.adapters.viewholder;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.adapters.FeedStoriesAdapter;
import awais.instagrabber.databinding.ItemHighlightBinding;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.ProfileModel;
public final class FeedStoryViewHolder extends RecyclerView.ViewHolder {
private final ItemHighlightBinding binding;
public FeedStoryViewHolder(final ItemHighlightBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final FeedStoryModel model,
final int position,
final FeedStoriesAdapter.OnFeedStoryClickListener listener) {
if (model == null) return;
binding.getRoot().setOnClickListener(v -> {
if (listener == null) return;
listener.onFeedStoryClick(model, position);
});
final ProfileModel profileModel = model.getProfileModel();
binding.title.setText(profileModel.getUsername());
binding.title.setAlpha(model.getFullyRead() ? 0.5F : 1.0F);
binding.icon.setImageURI(profileModel.getSdProfilePic());
binding.icon.setAlpha(model.getFullyRead() ? 0.5F : 1.0F);
}
}

View File

@ -1,23 +1,45 @@
package awais.instagrabber.adapters.viewholder;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import java.util.List;
import awais.instagrabber.databinding.ItemFollowBinding;
import awais.instagrabber.models.FollowModel;
import awais.instagrabber.models.ProfileModel;
public final class FollowsViewHolder extends RecyclerView.ViewHolder {
public final ImageView profileImage, isAdmin;
public final TextView tvFullName, tvUsername;
public FollowsViewHolder(@NonNull final View itemView) {
super(itemView);
profileImage = itemView.findViewById(R.id.ivProfilePic);
tvFullName = itemView.findViewById(R.id.tvFullName);
tvUsername = itemView.findViewById(R.id.tvUsername);
isAdmin = itemView.findViewById(R.id.isAdmin);
private final ItemFollowBinding binding;
public FollowsViewHolder(final ItemFollowBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final ProfileModel model,
final List<Long> admins,
final View.OnClickListener onClickListener) {
if (model == null) return;
itemView.setTag(model);
itemView.setOnClickListener(onClickListener);
binding.tvUsername.setText(model.getUsername());
binding.tvFullName.setText(model.getName());
if (admins != null && admins.contains(Long.parseLong(model.getId()))) {
binding.isAdmin.setVisibility(View.VISIBLE);
}
binding.ivProfilePic.setImageURI(model.getSdProfilePic());
}
public void bind(final FollowModel model,
final View.OnClickListener onClickListener) {
if (model == null) return;
itemView.setTag(model);
itemView.setOnClickListener(onClickListener);
binding.tvUsername.setText(model.getUsername());
binding.tvFullName.setText(model.getFullName());
binding.ivProfilePic.setImageURI(model.getProfilePicUrl());
}
}

View File

@ -1,21 +1,31 @@
package awais.instagrabber.adapters.viewholder;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.databinding.ItemHighlightBinding;
import awais.instagrabber.models.HighlightModel;
public final class HighlightViewHolder extends RecyclerView.ViewHolder {
public final ImageView icon;
public final TextView title;
public HighlightViewHolder(@NonNull final View itemView) {
super(itemView);
icon = itemView.findViewById(R.id.icon);
title = itemView.findViewById(R.id.title);
private final ItemHighlightBinding binding;
public HighlightViewHolder(final ItemHighlightBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final HighlightModel model) {
if (model == null) return;
binding.title.setText(model.getTitle());
binding.icon.setImageURI(model.getThumbnailUrl());
// binding.getRoot().setOnClickListener(v -> {
// if (listener == null) return;
// listener.onFeedStoryClick(model, position);
// });
// final ProfileModel profileModel = model.getProfileModel();
// binding.title.setText(profileModel.getUsername());
// binding.title.setAlpha(model.getFullyRead() ? 0.5F : 1.0F);
// binding.icon.setImageURI(profileModel.getSdProfilePic());
// binding.icon.setAlpha(model.getFullyRead() ? 0.5F : 1.0F);
}
}

View File

@ -1,75 +1,68 @@
package awais.instagrabber.adapters.viewholder;
import android.text.Spannable;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.customviews.RamboTextView;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener;
import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.models.NotificationModel;
import awais.instagrabber.models.enums.NotificationType;
public final class NotificationViewHolder extends RecyclerView.ViewHolder {
private final MentionClickListener mentionClickListener;
private final ImageView ivProfilePic, ivPreviewPic;
private final TextView tvUsername, tvDate, tvComment, tvSubComment;
private final View container, rightContainer;
private final ItemNotificationBinding binding;
public NotificationViewHolder(@NonNull final View itemView, final View.OnClickListener onClickListener, final MentionClickListener mentionClickListener) {
super(itemView);
container = itemView.findViewById(R.id.container);
rightContainer = itemView.findViewById(R.id.rightContainer);
if (onClickListener != null) container.setOnClickListener(onClickListener);
this.mentionClickListener = mentionClickListener;
ivProfilePic = itemView.findViewById(R.id.ivProfilePic);
ivPreviewPic = itemView.findViewById(R.id.ivPreviewPic);
tvUsername = itemView.findViewById(R.id.tvUsername);
tvDate = itemView.findViewById(R.id.tvDate);
tvComment = itemView.findViewById(R.id.tvComment);
tvSubComment = itemView.findViewById(R.id.tvSubComment);
tvUsername.setSelected(true);
tvDate.setSelected(true);
public NotificationViewHolder(final ItemNotificationBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public final ImageView getProfilePicView() {
return ivProfilePic;
}
public final ImageView getPreviewPicView() {
return ivPreviewPic;
}
public final void setNotificationModel(final NotificationModel notificationModel) {
if (container != null) container.setTag(notificationModel);
if (rightContainer != null) rightContainer.setTag(notificationModel);
}
public final void setUsername(final String username) {
if (tvUsername != null) tvUsername.setText(username);
}
public final void setDate(final String date) {
if (tvDate != null) tvDate.setText(date);
}
public final void setCommment(final int commment) {
if (tvComment != null) {
tvComment.setText(commment);
public void bind(final NotificationModel model,
final OnNotificationClickListener notificationClickListener) {
if (model == null) return;
itemView.setOnClickListener(v -> {
if (notificationClickListener == null) return;
notificationClickListener.onNotificationClick(model);
});
int text = -1;
CharSequence subtext = null;
switch (model.getType()) {
case LIKE:
text = R.string.liked_notif;
break;
case COMMENT:
text = R.string.comment_notif;
subtext = model.getText();
break;
case MENTION:
text = R.string.mention_notif;
subtext = model.getText();
break;
case FOLLOW:
text = R.string.follow_notif;
break;
case REQUEST:
text = R.string.request_notif;
subtext = model.getText();
break;
}
}
public final void setSubCommment(final CharSequence commment) {
if (tvSubComment != null) {
tvSubComment.setText(commment, commment instanceof Spannable ? TextView.BufferType.SPANNABLE : TextView.BufferType.NORMAL);
((RamboTextView) tvSubComment).setMentionClickListener(mentionClickListener);
binding.tvUsername.setText(model.getUsername());
binding.tvComment.setText(text);
binding.tvSubComment.setText(subtext, subtext instanceof Spannable ? TextView.BufferType.SPANNABLE : TextView.BufferType.NORMAL);
// binding.tvSubComment.setMentionClickListener(mentionClickListener);
if (model.getType() != NotificationType.REQUEST) {
binding.tvDate.setText(model.getDateTime());
}
binding.ivProfilePic.setImageURI(model.getProfilePic());
if (TextUtils.isEmpty(model.getPreviewPic())) {
binding.ivPreviewPic.setVisibility(View.GONE);
} else {
binding.ivPreviewPic.setVisibility(View.VISIBLE);
binding.ivPreviewPic.setImageURI(model.getPreviewPic());
}
}
}

View File

@ -1,20 +1,29 @@
package awais.instagrabber.adapters.viewholder;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.databinding.ItemChildPostBinding;
import awais.instagrabber.models.ViewerPostModel;
public final class PostMediaViewHolder extends RecyclerView.ViewHolder {
public final ImageView icon, isDownloaded, selectedView;
public PostMediaViewHolder(@NonNull final View itemView) {
super(itemView);
selectedView = itemView.findViewById(R.id.selectedView);
isDownloaded = itemView.findViewById(R.id.isDownloaded);
icon = itemView.findViewById(R.id.icon);
private final ItemChildPostBinding binding;
public PostMediaViewHolder(@NonNull final ItemChildPostBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final ViewerPostModel model, final int position, final View.OnClickListener clickListener) {
if (model == null) return;
model.setPosition(position);
itemView.setTag(model);
itemView.setOnClickListener(clickListener);
binding.selectedView.setVisibility(model.isCurrentSlide() ? View.VISIBLE : View.GONE);
binding.isDownloaded.setVisibility(model.isDownloaded() ? View.VISIBLE : View.GONE);
binding.icon.setImageURI(model.getSliderDisplayUrl());
}
}

View File

@ -1,23 +1,43 @@
package awais.instagrabber.adapters.viewholder;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.MultiSelectListAdapter.OnItemClickListener;
import awais.instagrabber.adapters.MultiSelectListAdapter.OnItemLongClickListener;
import awais.instagrabber.databinding.ItemPostBinding;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.enums.MediaItemType;
public final class PostViewHolder extends RecyclerView.ViewHolder {
public final ImageView postImage, typeIcon;
public final View selectedView, progressView, isDownloaded;
private final ItemPostBinding binding;
public PostViewHolder(@NonNull final View itemView) {
super(itemView);
typeIcon = itemView.findViewById(R.id.typeIcon);
postImage = itemView.findViewById(R.id.postImage);
isDownloaded = itemView.findViewById(R.id.isDownloaded);
selectedView = itemView.findViewById(R.id.selectedView);
progressView = itemView.findViewById(R.id.progressView);
public PostViewHolder(@NonNull final ItemPostBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final PostModel postModel,
final int position,
final OnItemClickListener<PostModel> clickListener,
final OnItemLongClickListener<PostModel> longClickListener) {
if (postModel == null) return;
postModel.setPosition(position);
itemView.setOnClickListener(v -> clickListener.onItemClick(postModel, position));
itemView.setOnLongClickListener(v -> longClickListener.onItemLongClick(postModel, position));
final MediaItemType itemType = postModel.getItemType();
final boolean isSlider = itemType == MediaItemType.MEDIA_TYPE_SLIDER;
binding.isDownloaded.setVisibility(postModel.isDownloaded() ? View.VISIBLE : View.GONE);
binding.typeIcon.setVisibility(itemType == MediaItemType.MEDIA_TYPE_VIDEO || isSlider ? View.VISIBLE : View.GONE);
binding.typeIcon.setImageResource(isSlider ? R.drawable.ic_slider_24 : R.drawable.ic_video_24);
binding.selectedView.setVisibility(postModel.isSelected() ? View.VISIBLE : View.GONE);
binding.postImage.setImageURI(postModel.getThumbnailUrl());
}
}

View File

@ -0,0 +1,239 @@
package awais.instagrabber.adapters.viewholder;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.ui.PlayerView;
import java.util.ArrayList;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.PostViewAdapter.OnPostCaptionLongClickListener;
import awais.instagrabber.adapters.PostViewAdapter.OnPostViewChildViewClickListener;
import awais.instagrabber.adapters.PostViewerChildAdapter;
import awais.instagrabber.databinding.ItemFullPostViewBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.ViewerPostModel;
import awais.instagrabber.models.ViewerPostModelWrapper;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public class PostViewerViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = "PostViewerViewHolder";
private final ItemFullPostViewBinding binding;
private int currentChildPosition;
public PostViewerViewHolder(@NonNull final ItemFullPostViewBinding binding) {
super(binding.getRoot());
this.binding = binding;
binding.topPanel.viewStoryPost.setVisibility(View.GONE);
}
public void bind(final ViewerPostModelWrapper wrapper,
final int position,
final OnPostViewChildViewClickListener clickListener,
final OnPostCaptionLongClickListener longClickListener,
final MentionClickListener mentionClickListener) {
if (wrapper == null) return;
final ViewerPostModel[] items = wrapper.getViewerPostModels();
if (items == null || items.length <= 0) return;
if (items[0] == null) return;
final PostViewerChildAdapter adapter = new PostViewerChildAdapter();
binding.mediaViewPager.setAdapter(adapter);
final ViewerPostModel firstPost = items[0];
setPostInfo(firstPost, mentionClickListener);
setMediaItems(items, adapter);
setupListeners(wrapper,
position,
clickListener,
longClickListener,
mentionClickListener,
firstPost.getLocation());
}
private void setPostInfo(final ViewerPostModel firstPost,
final MentionClickListener mentionClickListener) {
final ProfileModel profileModel = firstPost.getProfileModel();
if (profileModel == null) return;
binding.topPanel.title.setText(profileModel.getUsername());
final String locationName = firstPost.getLocationName();
if (!TextUtils.isEmpty(locationName)) {
binding.topPanel.location.setVisibility(View.VISIBLE);
binding.topPanel.location.setText(locationName);
} else binding.topPanel.location.setVisibility(View.GONE);
binding.topPanel.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
binding.bottomPanel.commentsCount.setText(String.valueOf(firstPost.getCommentsCount()));
final CharSequence postCaption = firstPost.getPostCaption();
if (TextUtils.hasMentions(postCaption)) {
binding.bottomPanel.viewerCaption.setMentionClickListener(mentionClickListener);
binding.bottomPanel.viewerCaption
.setText(TextUtils.getMentionText(postCaption), TextView.BufferType.SPANNABLE);
} else {
binding.bottomPanel.viewerCaption.setMentionClickListener(null);
binding.bottomPanel.viewerCaption.setText(postCaption);
}
binding.bottomPanel.tvPostDate.setText(firstPost.getPostDate());
setupLikes(firstPost);
setupSave(firstPost);
}
private void setupLikes(final ViewerPostModel firstPost) {
final boolean liked = firstPost.getLike();
final long likeCount = firstPost.getLikes();
final Resources resources = itemView.getContext().getResources();
if (liked) {
final String unlikeString = resources.getString(R.string.unlike, String.valueOf(likeCount));
binding.btnLike.setText(unlikeString);
ViewCompat.setBackgroundTintList(binding.btnLike,
ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_pink_background)));
} else {
final String likeString = resources.getString(R.string.like, String.valueOf(likeCount));
binding.btnLike.setText(likeString);
ViewCompat.setBackgroundTintList(binding.btnLike,
ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_lightpink_background)));
}
}
private void setupSave(final ViewerPostModel firstPost) {
final boolean saved = firstPost.getBookmark();
if (saved) {
binding.btnBookmark.setText(R.string.unbookmark);
ViewCompat.setBackgroundTintList(binding.btnBookmark,
ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_orange_background)));
} else {
binding.btnBookmark.setText(R.string.bookmark);
ViewCompat.setBackgroundTintList(
binding.btnBookmark,
ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_lightorange_background)));
}
}
private void setupListeners(final ViewerPostModelWrapper wrapper,
final int position,
final OnPostViewChildViewClickListener clickListener,
final OnPostCaptionLongClickListener longClickListener,
final MentionClickListener mentionClickListener,
final String location) {
final View.OnClickListener onClickListener = v -> clickListener
.onClick(v, wrapper, position, currentChildPosition);
binding.bottomPanel.btnComments.setOnClickListener(onClickListener);
binding.topPanel.title.setOnClickListener(onClickListener);
binding.topPanel.ivProfilePic.setOnClickListener(onClickListener);
binding.bottomPanel.btnDownload.setOnClickListener(onClickListener);
binding.bottomPanel.viewerCaption.setOnClickListener(onClickListener);
binding.btnLike.setOnClickListener(onClickListener);
binding.btnBookmark.setOnClickListener(onClickListener);
binding.bottomPanel.viewerCaption.setOnLongClickListener(v -> {
longClickListener.onLongClick(binding.bottomPanel.viewerCaption.getText().toString());
return true;
});
if (!TextUtils.isEmpty(location)) {
binding.topPanel.location.setOnClickListener(v -> mentionClickListener
.onClick(binding.topPanel.location, location, false, true));
}
}
private void setMediaItems(final ViewerPostModel[] items,
final PostViewerChildAdapter adapter) {
final List<ViewerPostModel> filteredList = new ArrayList<>();
for (final ViewerPostModel model : items) {
final MediaItemType itemType = model.getItemType();
if (itemType == MediaItemType.MEDIA_TYPE_VIDEO || itemType == MediaItemType.MEDIA_TYPE_IMAGE) {
filteredList.add(model);
}
}
binding.mediaCounter.setVisibility(filteredList.size() > 1 ? View.VISIBLE : View.GONE);
final String counter = "1/" + filteredList.size();
binding.mediaCounter.setText(counter);
adapter.submitList(filteredList);
binding.mediaViewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(final int position) {
if (filteredList.size() <= 0 || position >= filteredList.size()) return;
currentChildPosition = position;
final String counter = (position + 1) + "/" + filteredList.size();
binding.mediaCounter.setText(counter);
final ViewerPostModel viewerPostModel = filteredList.get(position);
if (viewerPostModel.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
setVideoDetails(viewerPostModel);
setVolumeListener(position);
return;
}
setImageDetails();
}
});
}
private void setVolumeListener(final int position) {
binding.bottomPanel.btnMute.setOnClickListener(v -> {
try {
final RecyclerView.ViewHolder viewHolder = ((RecyclerView) binding.mediaViewPager
.getChildAt(0)).findViewHolderForAdapterPosition(position);
if (viewHolder != null) {
final View itemView = viewHolder.itemView;
if (itemView instanceof PlayerView) {
final SimpleExoPlayer player = (SimpleExoPlayer) ((PlayerView) itemView)
.getPlayer();
if (player == null) return;
final float vol = player.getVolume() == 0f ? 1f : 0f;
player.setVolume(vol);
binding.bottomPanel.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24
: R.drawable.ic_volume_off_24);
Utils.sessionVolumeFull = vol == 1f;
}
}
} catch (Exception e) {
Log.e(TAG, "Error", e);
}
});
}
private void setImageDetails() {
binding.bottomPanel.btnMute.setVisibility(View.GONE);
binding.bottomPanel.videoViewsContainer.setVisibility(View.GONE);
}
private void setVideoDetails(final ViewerPostModel viewerPostModel) {
binding.bottomPanel.btnMute.setVisibility(View.VISIBLE);
final long videoViews = viewerPostModel.getVideoViews();
if (videoViews < 0) {
binding.bottomPanel.videoViewsContainer.setVisibility(View.GONE);
return;
}
binding.bottomPanel.tvVideoViews.setText(String.valueOf(videoViews));
binding.bottomPanel.videoViewsContainer.setVisibility(View.VISIBLE);
}
public void stopPlayingVideo() {
try {
final RecyclerView.ViewHolder viewHolder = ((RecyclerView) binding.mediaViewPager
.getChildAt(0)).findViewHolderForAdapterPosition(currentChildPosition);
if (viewHolder != null) {
final View itemView = viewHolder.itemView;
if (itemView instanceof PlayerView) {
final Player player = ((PlayerView) itemView).getPlayer();
if (player != null) {
player.setPlayWhenReady(false);
}
}
}
} catch (Exception e) {
Log.e(TAG, "Error", e);
}
}
}

View File

@ -4,6 +4,8 @@ import android.view.View;
import androidx.annotation.NonNull;
import com.facebook.drawee.backends.pipeline.Fresco;
import awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
@ -22,8 +24,10 @@ public class DirectMessageAnimatedMediaViewHolder extends DirectMessageItemViewH
@Override
public void bindItem(final DirectItemModel directItemModel) {
getGlideRequestManager().asGif().load(directItemModel.getAnimatedMediaModel().getGifUrl())
.into(binding.ivAnimatedMessage);
binding.ivAnimatedMessage.setController(Fresco.newDraweeControllerBuilder()
.setUri(directItemModel.getAnimatedMediaModel().getGifUrl())
.setAutoPlayAnimations(true)
.build());
binding.ivAnimatedMessage.setVisibility(View.VISIBLE);
}
}

View File

@ -8,37 +8,32 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestManager;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public abstract class DirectMessageItemViewHolder extends RecyclerView.ViewHolder {
private static final int MESSAGE_INCOMING = 69;
private static final int MESSAGE_OUTGOING = 420;
private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel(Utils.getUserIdFromCookie(Utils.settingsHelper.getString(Constants.COOKIE)));
private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel(
CookieUtils.getUserIdFromCookie(Utils.settingsHelper.getString(Constants.COOKIE)));
private final LayoutDmBaseBinding binding;
private final String strDmYou;
private final int itemMargin;
private final RequestManager glideRequestManager;
public DirectMessageItemViewHolder(@NonNull final LayoutDmBaseBinding binding, @NonNull final View.OnClickListener onClickListener) {
super(binding.getRoot());
this.binding = binding;
binding.ivProfilePic.setOnClickListener(onClickListener);
binding.messageCard.setOnClickListener(onClickListener);
strDmYou = binding.getRoot().getContext().getString(R.string.direct_messages_you);
// final String strDmYou = binding.getRoot().getContext().getString(R.string.direct_messages_you);
itemMargin = Utils.displayMetrics.widthPixels / 5;
glideRequestManager = Glide.with(itemView);
}
public void bind(final DirectItemModel directItemModel, final List<ProfileModel> users, final List<ProfileModel> leftUsers) {
@ -47,7 +42,7 @@ public abstract class DirectMessageItemViewHolder extends RecyclerView.ViewHolde
final RecyclerView.LayoutParams itemViewLayoutParams = (RecyclerView.LayoutParams) itemView.getLayoutParams();
itemViewLayoutParams.setMargins(type == MESSAGE_OUTGOING ? itemMargin : 0, 0,
type == MESSAGE_INCOMING ? itemMargin : 0, 0);
type == MESSAGE_INCOMING ? itemMargin : 0, 0);
final ViewGroup messageCardParent = (ViewGroup) binding.messageCard.getParent();
binding.contentContainer.setGravity(type == MESSAGE_INCOMING ? Gravity.START : Gravity.END);
@ -56,7 +51,7 @@ public abstract class DirectMessageItemViewHolder extends RecyclerView.ViewHolde
if (user != null && user != myProfileHolder) {
text = user.getUsername();
} else if (user == myProfileHolder) text = "";
text = (Utils.isEmpty(text) ? "" : text + " - ") + directItemModel.getDateTime();
text = (TextUtils.isEmpty(text) ? "" : text + " - ") + directItemModel.getDateTime();
binding.tvUsername.setText(text);
binding.tvUsername.setGravity(type == MESSAGE_INCOMING ? Gravity.START : Gravity.END);
binding.ivProfilePic.setVisibility(type == MESSAGE_INCOMING ? View.VISIBLE : View.GONE);
@ -66,7 +61,7 @@ public abstract class DirectMessageItemViewHolder extends RecyclerView.ViewHolde
binding.messageCard.setTag(directItemModel);
if (type == MESSAGE_INCOMING && user != null) {
glideRequestManager.load(user.getSdProfilePic()).into(binding.ivProfilePic);
binding.ivProfilePic.setImageURI(user.getSdProfilePic());
}
bindItem(directItemModel);
@ -76,10 +71,6 @@ public abstract class DirectMessageItemViewHolder extends RecyclerView.ViewHolde
this.binding.messageCard.addView(view);
}
public RequestManager getGlideRequestManager() {
return glideRequestManager;
}
public abstract void bindItem(final DirectItemModel directItemModel);
@Nullable

View File

@ -7,7 +7,7 @@ import androidx.annotation.NonNull;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmLinkBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.TextUtils;
public class DirectMessageLinkViewHolder extends DirectMessageItemViewHolder {
@ -26,21 +26,21 @@ public class DirectMessageLinkViewHolder extends DirectMessageItemViewHolder {
final DirectItemModel.DirectItemLinkModel link = directItemModel.getLinkModel();
final DirectItemModel.DirectItemLinkContext linkContext = link.getLinkContext();
final String linkImageUrl = linkContext.getLinkImageUrl();
if (Utils.isEmpty(linkImageUrl)) {
if (TextUtils.isEmpty(linkImageUrl)) {
binding.ivLinkPreview.setVisibility(View.GONE);
} else {
getGlideRequestManager().load(linkImageUrl).into(binding.ivLinkPreview);
binding.ivLinkPreview.setImageURI(linkImageUrl);
}
if (Utils.isEmpty(linkContext.getLinkTitle())) {
if (TextUtils.isEmpty(linkContext.getLinkTitle())) {
binding.tvLinkTitle.setVisibility(View.GONE);
} else {
binding.tvLinkTitle.setText(linkContext.getLinkTitle());
}
if (Utils.isEmpty(linkContext.getLinkSummary())) {
if (TextUtils.isEmpty(linkContext.getLinkSummary())) {
binding.tvLinkSummary.setVisibility(View.GONE);
} else {
binding.tvLinkSummary.setText(linkContext.getLinkSummary());
}
binding.tvMessage.setText(Utils.getSpannableUrl(link.getText()));
binding.tvMessage.setText(TextUtils.getSpannableUrl(link.getText()));
}
}

View File

@ -34,11 +34,13 @@ public class DirectMessageMediaShareViewHolder extends DirectMessageItemViewHold
final DirectItemMediaModel mediaModel = directItemModel.getMediaModel();
final ProfileModel modelUser = mediaModel.getUser();
if (modelUser != null) {
binding.tvMessage.setText(HtmlCompat.fromHtml("<small>" + context.getString(R.string.dms_inbox_media_shared_from, modelUser.getUsername()) + "</small>", FROM_HTML_MODE_COMPACT));
binding.tvMessage.setText(HtmlCompat.fromHtml(
"<small>" + context.getString(R.string.dms_inbox_media_shared_from, modelUser.getUsername()) + "</small>",
FROM_HTML_MODE_COMPACT));
}
getGlideRequestManager().load(mediaModel.getThumbUrl()).into(binding.ivMediaPreview);
binding.ivMediaPreview.setImageURI(mediaModel.getThumbUrl());
final MediaItemType modelMediaType = mediaModel.getMediaType();
binding.typeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO
|| modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
|| modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
}
}

View File

@ -24,9 +24,9 @@ public class DirectMessageMediaViewHolder extends DirectMessageItemViewHolder {
@Override
public void bindItem(final DirectItemModel directItemModel) {
final DirectItemModel.DirectItemMediaModel mediaModel = directItemModel.getMediaModel();
getGlideRequestManager().load(mediaModel.getThumbUrl()).into(binding.ivMediaPreview);
binding.ivMediaPreview.setImageURI(mediaModel.getThumbUrl());
final MediaItemType modelMediaType = mediaModel.getMediaType();
binding.typeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO
|| modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
|| modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
}
}

View File

@ -4,8 +4,6 @@ import android.view.View;
import androidx.annotation.NonNull;
import com.bumptech.glide.Glide;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmProfileBinding;
import awais.instagrabber.models.ProfileModel;
@ -27,9 +25,8 @@ public class DirectMessageProfileViewHolder extends DirectMessageItemViewHolder
@Override
public void bindItem(final DirectItemModel directItemModel) {
final ProfileModel profileModel = directItemModel.getProfileModel();
Glide.with(binding.profileInfo)
.load(profileModel.getSdProfilePic())
.into(binding.profileInfo);
if (profileModel == null) return;
binding.profileInfo.setImageURI(profileModel.getSdProfilePic());
binding.btnOpenProfile.setTag(profileModel);
binding.tvFullName.setText(profileModel.getName());
binding.profileInfoText.setText(profileModel.getUsername());

View File

@ -12,7 +12,7 @@ import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.enums.RavenExpiringMediaType;
import awais.instagrabber.models.enums.RavenMediaViewType;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.TextUtils;
public class DirectMessageRavenMediaViewHolder extends DirectMessageItemViewHolder {
@ -33,7 +33,7 @@ public class DirectMessageRavenMediaViewHolder extends DirectMessageItemViewHold
final DirectItemModel.DirectItemRavenMediaModel ravenMediaModel = directItemModel.getRavenMediaModel();
DirectItemModel.DirectItemMediaModel mediaModel = directItemModel.getMediaModel();
final boolean isExpired = ravenMediaModel == null || (mediaModel = ravenMediaModel.getMedia()) == null ||
Utils.isEmpty(mediaModel.getThumbUrl()) && mediaModel.getPk() < 1;
TextUtils.isEmpty(mediaModel.getThumbUrl()) && mediaModel.getPk() < 1;
DirectItemModel.RavenExpiringMediaActionSummaryModel mediaActionSummary = null;
if (ravenMediaModel != null) {
@ -73,8 +73,8 @@ public class DirectMessageRavenMediaViewHolder extends DirectMessageItemViewHold
final MediaItemType mediaType = mediaModel.getMediaType();
textRes = -1;
binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO ||
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
getGlideRequestManager().load(mediaModel.getThumbUrl()).into(binding.ivMediaPreview);
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
binding.ivMediaPreview.setImageURI(mediaModel.getThumbUrl());
}
}
if (textRes != -1) {

View File

@ -9,7 +9,7 @@ import awais.instagrabber.databinding.LayoutDmRavenMediaBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.TextUtils;
public class DirectMessageReelShareViewHolder extends DirectMessageItemViewHolder {
@ -29,10 +29,10 @@ public class DirectMessageReelShareViewHolder extends DirectMessageItemViewHolde
public void bindItem(final DirectItemModel directItemModel) {
final DirectItemModel.DirectItemReelShareModel reelShare = directItemModel.getReelShare();
CharSequence text = reelShare.getText();
if (Utils.isEmpty(text)) {
if (TextUtils.isEmpty(text)) {
binding.tvMessage.setVisibility(View.GONE);
} else {
if (Utils.hasMentions(text)) text = Utils.getMentionText(text); // for mentions
if (TextUtils.hasMentions(text)) text = TextUtils.getMentionText(text); // for mentions
binding.tvMessage.setText(text);
}
final DirectItemModel.DirectItemMediaModel reelShareMedia = reelShare.getMedia();
@ -41,8 +41,8 @@ public class DirectMessageReelShareViewHolder extends DirectMessageItemViewHolde
binding.mediaExpiredIcon.setVisibility(View.VISIBLE);
} else {
binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO ||
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
getGlideRequestManager().load(reelShareMedia.getThumbUrl()).into(binding.ivMediaPreview);
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
binding.ivMediaPreview.setImageURI(reelShareMedia.getThumbUrl());
}
}
}

View File

@ -9,7 +9,7 @@ import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmStoryShareBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.TextUtils;
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT;
@ -34,15 +34,14 @@ public class DirectMessageStoryShareViewHolder extends DirectMessageItemViewHold
binding.tvMessage.setVisibility(View.VISIBLE);
} else {
final String text = reelShare.getText();
if (!Utils.isEmpty(text)) {
if (!TextUtils.isEmpty(text)) {
binding.tvMessage.setText(text);
binding.tvMessage.setVisibility(View.VISIBLE);
}
else {
} else {
final DirectItemModel.DirectItemMediaModel reelShareMedia = reelShare.getMedia();
final MediaItemType mediaType = reelShareMedia.getMediaType();
binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO ? View.VISIBLE : View.GONE);
getGlideRequestManager().load(reelShareMedia.getThumbUrl()).into(binding.ivMediaPreview);
binding.ivMediaPreview.setImageURI(reelShareMedia.getThumbUrl());
}
}
}

View File

@ -12,7 +12,7 @@ import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.TextUtils;
public class DirectMessageTextViewHolder extends DirectMessageItemViewHolder {
@ -32,8 +32,8 @@ public class DirectMessageTextViewHolder extends DirectMessageItemViewHolder {
public void bindItem(final DirectItemModel directItemModel) {
final Context context = itemView.getContext();
CharSequence text = directItemModel.getText();
text = Utils.getSpannableUrl(text.toString()); // for urls
if (Utils.hasMentions(text)) text = Utils.getMentionText(text); // for mentions
text = TextUtils.getSpannableUrl(text.toString()); // for urls
if (TextUtils.hasMentions(text)) text = TextUtils.getMentionText(text); // for mentions
if (text instanceof Spanned)
binding.tvMessage.setText(text, TextView.BufferType.SPANNABLE);
else if (text == "") {

View File

@ -9,7 +9,7 @@ import androidx.annotation.NonNull;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmVoiceMediaBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NumberUtils;
public class DirectMessageVoiceMediaViewHolder extends DirectMessageItemViewHolder {
@ -61,13 +61,13 @@ public class DirectMessageVoiceMediaViewHolder extends DirectMessageItemViewHold
if (waveformData != null) binding.waveformSeekBar.setSample(waveformData);
final long durationMs = voiceMediaModel.getDurationMs();
binding.tvVoiceDuration.setText(Utils.millisToString(durationMs));
binding.tvVoiceDuration.setText(NumberUtils.millisToString(durationMs));
binding.waveformSeekBar.setProgress(voiceMediaModel.getProgress());
binding.waveformSeekBar.setProgressChangeListener((waveformSeekBar, progress, fromUser) -> {
// todo progress audio player
voiceMediaModel.setProgress(progress);
if (fromUser)
binding.tvVoiceDuration.setText(Utils.millisToString(durationMs * progress / 100));
binding.tvVoiceDuration.setText(NumberUtils.millisToString(durationMs * progress / 100));
});
binding.btnPlayVoice.setTag(voiceMediaModel);
} else {

View File

@ -0,0 +1,138 @@
package awais.instagrabber.adapters.viewholder.feed;
import android.text.SpannableString;
import android.text.Spanned;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.customviews.CommentMentionClickSpan;
import awais.instagrabber.customviews.RamboTextView;
import awais.instagrabber.databinding.ItemFeedBottomBinding;
import awais.instagrabber.databinding.ItemFeedTopBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.TextUtils;
public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
public static final int MAX_CHARS = 255;
private final ItemFeedTopBinding topBinding;
private final ItemFeedBottomBinding bottomBinding;
private final MentionClickListener mentionClickListener;
public FeedItemViewHolder(@NonNull final View root,
final ItemFeedTopBinding topBinding,
final ItemFeedBottomBinding bottomBinding,
final MentionClickListener mentionClickListener,
final View.OnClickListener clickListener,
final View.OnLongClickListener longClickListener) {
super(root);
this.topBinding = topBinding;
this.bottomBinding = bottomBinding;
this.mentionClickListener = mentionClickListener;
// topBinding.title.setMovementMethod(new LinkMovementMethod());
bottomBinding.btnComments.setOnClickListener(clickListener);
topBinding.viewStoryPost.setOnClickListener(clickListener);
topBinding.ivProfilePic.setOnClickListener(clickListener);
bottomBinding.btnDownload.setOnClickListener(clickListener);
bottomBinding.viewerCaption.setOnClickListener(clickListener);
bottomBinding.viewerCaption.setOnLongClickListener(longClickListener);
bottomBinding.viewerCaption.setMentionClickListener(mentionClickListener);
}
public void bind(final FeedModel feedModel) {
if (feedModel == null) {
return;
}
topBinding.viewStoryPost.setTag(feedModel);
topBinding.ivProfilePic.setTag(feedModel);
bottomBinding.btnDownload.setTag(feedModel);
bottomBinding.viewerCaption.setTag(feedModel);
bottomBinding.btnComments.setTag(feedModel);
final ProfileModel profileModel = feedModel.getProfileModel();
if (profileModel != null) {
topBinding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
final int titleLen = profileModel.getUsername().length() + 1;
final SpannableString spannableString = new SpannableString("@" + profileModel.getUsername());
spannableString.setSpan(new CommentMentionClickSpan(), 0, titleLen, 0);
topBinding.title.setText(spannableString);
topBinding.title.setMentionClickListener(
(view, text, isHashtag, isLocation) -> mentionClickListener.onClick(null, profileModel.getUsername(), false, false));
}
bottomBinding.tvPostDate.setText(feedModel.getPostDate());
final long commentsCount = feedModel.getCommentsCount();
bottomBinding.commentsCount.setText(String.valueOf(commentsCount));
final String locationName = feedModel.getLocationName();
final String locationId = feedModel.getLocationId();
setLocation(locationName, locationId);
CharSequence postCaption = feedModel.getPostCaption();
final boolean captionEmpty = TextUtils.isEmpty(postCaption);
bottomBinding.viewerCaption.setVisibility(captionEmpty ? View.GONE : View.VISIBLE);
if (!captionEmpty) {
if (TextUtils.hasMentions(postCaption)) {
postCaption = TextUtils.getMentionText(postCaption);
feedModel.setPostCaption(postCaption);
bottomBinding.viewerCaption.setText(postCaption, TextView.BufferType.SPANNABLE);
} else {
bottomBinding.viewerCaption.setText(postCaption);
}
}
expandCollapseTextView(bottomBinding.viewerCaption, feedModel.getPostCaption());
bindItem(feedModel);
}
private void setLocation(final String locationName, final String locationId) {
if (TextUtils.isEmpty(locationName)) {
topBinding.location.setVisibility(View.GONE);
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT
));
} else {
topBinding.location.setVisibility(View.VISIBLE);
topBinding.location.setText(locationName);
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT
));
topBinding.location.setOnClickListener(v -> mentionClickListener.onClick(topBinding.location, locationId, false, true));
}
}
/**
* expands or collapses {@link RamboTextView} [stg idek why i wrote this documentation]
*
* @param textView the {@link RamboTextView} view, to expand and collapse
* @param caption caption
* @return isExpanded
*/
public static boolean expandCollapseTextView(@NonNull final RamboTextView textView, final CharSequence caption) {
if (TextUtils.isEmpty(caption)) return false;
final TextView.BufferType bufferType = caption instanceof Spanned ? TextView.BufferType.SPANNABLE : TextView.BufferType.NORMAL;
if (textView.isCaptionExpanded()) {
textView.setText(caption, bufferType);
textView.setCaptionIsExpanded(false);
return true;
}
int i = TextUtils.indexOfChar(caption, '\r', 0);
if (i == -1) i = TextUtils.indexOfChar(caption, '\n', 0);
if (i == -1) i = MAX_CHARS;
final int captionLen = caption.length();
final int minTrim = Math.min(MAX_CHARS, i);
if (captionLen <= minTrim) return false;
if (TextUtils.hasMentions(caption))
textView.setText(TextUtils.getMentionText(caption), TextView.BufferType.SPANNABLE);
textView.setCaptionIsExpandable(true);
textView.setCaptionIsExpanded(true);
return true;
}
public abstract void bindItem(final FeedModel feedModel);
}

View File

@ -0,0 +1,76 @@
package awais.instagrabber.adapters.viewholder.feed;
import android.net.Uri;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchy;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import awais.instagrabber.databinding.ItemFeedPhotoBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public class FeedPhotoViewHolder extends FeedItemViewHolder {
private static final String TAG = "FeedPhotoViewHolder";
private final ItemFeedPhotoBinding binding;
public FeedPhotoViewHolder(@NonNull final ItemFeedPhotoBinding binding,
final MentionClickListener mentionClickListener,
final View.OnClickListener clickListener,
final View.OnLongClickListener longClickListener) {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, mentionClickListener, clickListener, longClickListener);
this.binding = binding;
binding.itemFeedBottom.videoViewsContainer.setVisibility(View.GONE);
binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
binding.imageViewer.setAllowTouchInterceptionWhileZoomed(false);
final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(itemView.getContext().getResources())
.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
.build();
binding.imageViewer.setHierarchy(hierarchy);
}
@Override
public void bindItem(final FeedModel feedModel) {
if (feedModel == null) {
return;
}
final ViewGroup.LayoutParams layoutParams = binding.imageViewer.getLayoutParams();
final int requiredWidth = Utils.displayMetrics.widthPixels;
layoutParams.width = feedModel.getImageWidth() == 0 ? requiredWidth : feedModel.getImageWidth();
layoutParams.height = feedModel.getImageHeight() == 0 ? requiredWidth + 1 : feedModel.getImageHeight();
binding.imageViewer.requestLayout();
final String thumbnailUrl = feedModel.getThumbnailUrl();
String url = feedModel.getDisplayUrl();
if (TextUtils.isEmpty(url)) url = thumbnailUrl;
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
.setLocalThumbnailPreviewsEnabled(true)
.setProgressiveRenderingEnabled(true)
.build();
binding.imageViewer.setController(Fresco.newDraweeControllerBuilder()
.setImageRequest(requestBuilder)
.setOldController(binding.imageViewer.getController())
.setLowResImageRequest(ImageRequest.fromUri(thumbnailUrl))
.build());
// binding.imageViewer.setImageURI(url);
// final RequestBuilder<Bitmap> thumbnailRequestBuilder = glide
// .asBitmap()
// .load(thumbnailUrl)
// .diskCacheStrategy(DiskCacheStrategy.ALL);
// glide.asBitmap()
// .load(url)
// .thumbnail(thumbnailRequestBuilder)
// .diskCacheStrategy(DiskCacheStrategy.ALL)
// .into(customTarget);
}
}

View File

@ -0,0 +1,341 @@
package awais.instagrabber.adapters.viewholder.feed;
import android.content.Context;
import android.net.Uri;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ViewSwitcher;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchy;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.view.SimpleDraweeView;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import awais.instagrabber.R;
import awais.instagrabber.databinding.ItemFeedSliderBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.ViewerPostModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class FeedSliderViewHolder extends FeedItemViewHolder {
private static final String TAG = "FeedSliderViewHolder";
private static final boolean shouldAutoPlay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
private final ItemFeedSliderBinding binding;
private final DefaultDataSourceFactory dataSourceFactory;
private final PlayerChangeListener playerChangeListener = (position, player) -> {
pagerPlayer = player;
playerPosition = position;
};
private CacheDataSourceFactory cacheDataSourceFactory;
private SimpleExoPlayer pagerPlayer;
private int playerPosition = 0;
public FeedSliderViewHolder(@NonNull final ItemFeedSliderBinding binding,
final MentionClickListener mentionClickListener,
final View.OnClickListener clickListener,
final View.OnLongClickListener longClickListener) {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, mentionClickListener, clickListener, longClickListener);
this.binding = binding;
binding.itemFeedBottom.videoViewsContainer.setVisibility(View.GONE);
binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams();
layoutParams.height = Utils.displayMetrics.widthPixels + 1;
binding.mediaList.setLayoutParams(layoutParams);
final Context context = binding.getRoot().getContext();
dataSourceFactory = new DefaultDataSourceFactory(context, "instagram");
final SimpleCache simpleCache = Utils.getSimpleCacheInstance(context);
if (simpleCache != null) {
cacheDataSourceFactory = new CacheDataSourceFactory(simpleCache, dataSourceFactory);
}
}
@Override
public void bindItem(final FeedModel feedModel) {
final ViewerPostModel[] sliderItems = feedModel.getSliderItems();
final int sliderItemLen = sliderItems != null ? sliderItems.length : 0;
if (sliderItemLen <= 0) {
return;
}
final String text = "1/" + sliderItemLen;
binding.mediaCounter.setText(text);
binding.mediaList.setOffscreenPageLimit(Math.min(5, sliderItemLen));
final PagerAdapter adapter = binding.mediaList.getAdapter();
if (adapter != null) {
final int count = adapter.getCount();
for (int i = 0; i < count; i++) {
adapter.destroyItem(binding.mediaList, i, binding.mediaList.getChildAt(i));
}
}
final ChildMediaItemsAdapter itemsAdapter = new ChildMediaItemsAdapter(sliderItems,
cacheDataSourceFactory != null
? cacheDataSourceFactory
: dataSourceFactory,
playerChangeListener);
binding.mediaList.setAdapter(itemsAdapter);
//noinspection deprecation
binding.mediaList.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
private int prevPos = 0;
@Override
public void onPageSelected(final int position) {
ViewerPostModel sliderItem = sliderItems[prevPos];
if (sliderItem != null) {
sliderItem.setSelected(false);
if (sliderItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
// stop playing prev video
final ViewSwitcher prevChild = (ViewSwitcher) binding.mediaList.getChildAt(prevPos);
if (prevChild == null || prevChild.getTag() == null || !(prevChild.getTag() instanceof SimpleExoPlayer)) {
return;
}
((SimpleExoPlayer) prevChild.getTag()).setPlayWhenReady(false);
}
}
sliderItem = sliderItems[position];
if (sliderItem == null) return;
sliderItem.setSelected(true);
final String text = (position + 1) + "/" + sliderItemLen;
binding.mediaCounter.setText(text);
prevPos = position;
if (sliderItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
if (shouldAutoPlay) {
autoPlay(position);
}
} else binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
}
});
final View.OnClickListener muteClickListener = v -> {
final int currentItem = binding.mediaList.getCurrentItem();
if (currentItem < 0 || currentItem >= binding.mediaList.getChildCount()) {
return;
}
final ViewerPostModel sliderItem = sliderItems[currentItem];
if (sliderItem.getItemType() != MediaItemType.MEDIA_TYPE_VIDEO) {
return;
}
final View currentView = binding.mediaList.getChildAt(currentItem);
if (!(currentView instanceof ViewSwitcher)) {
return;
}
final ViewSwitcher viewSwitcher = (ViewSwitcher) currentView;
final Object tag = viewSwitcher.getTag();
if (!(tag instanceof SimpleExoPlayer)) {
return;
}
final SimpleExoPlayer player = (SimpleExoPlayer) tag;
final float intVol = player.getVolume() == 0f ? 1f : 0f;
player.setVolume(intVol);
binding.itemFeedBottom.btnMute.setImageResource(intVol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
Utils.sessionVolumeFull = intVol == 1f;
};
final ViewerPostModel firstItem = sliderItems[0];
if (firstItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
}
binding.itemFeedBottom.btnMute.setImageResource(Utils.sessionVolumeFull ? R.drawable.ic_volume_off_24 : R.drawable.ic_volume_up_24);
binding.itemFeedBottom.btnMute.setOnClickListener(muteClickListener);
}
private void autoPlay(final int position) {
if (!shouldAutoPlay) {
return;
}
final ChildMediaItemsAdapter adapter = (ChildMediaItemsAdapter) binding.mediaList.getAdapter();
if (adapter == null) {
return;
}
final ViewerPostModel sliderItem = adapter.getItemAtPosition(position);
if (sliderItem.getItemType() != MediaItemType.MEDIA_TYPE_VIDEO) {
return;
}
final ViewSwitcher viewSwitcher = (ViewSwitcher) binding.mediaList.getChildAt(position);
loadPlayer(binding.getRoot().getContext(),
position, sliderItem.getDisplayUrl(),
viewSwitcher,
cacheDataSourceFactory != null ? cacheDataSourceFactory : dataSourceFactory,
playerChangeListener);
}
public void startPlayingVideo() {
autoPlay(playerPosition);
}
public void stopPlayingVideo() {
if (pagerPlayer == null) {
return;
}
pagerPlayer.setPlayWhenReady(false);
}
private interface PlayerChangeListener {
void playerChanged(final int position, final SimpleExoPlayer player);
}
private static void loadPlayer(final Context context,
final int position, final String displayUrl,
final ViewSwitcher viewSwitcher,
final DataSource.Factory factory,
final PlayerChangeListener playerChangeListener) {
if (viewSwitcher == null) {
return;
}
SimpleExoPlayer player = (SimpleExoPlayer) viewSwitcher.getTag();
if (player != null) {
player.setPlayWhenReady(true);
return;
}
player = new SimpleExoPlayer.Builder(context).build();
final PlayerView playerView = (PlayerView) viewSwitcher.getChildAt(1);
playerView.setPlayer(player);
if (viewSwitcher.getDisplayedChild() == 0) {
viewSwitcher.showNext();
}
playerView.setControllerShowTimeoutMs(1000);
float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;
player.setVolume(vol);
player.setPlayWhenReady(Utils.settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));
final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(factory).createMediaSource(Uri.parse(displayUrl));
player.setRepeatMode(Player.REPEAT_MODE_ALL);
player.prepare(mediaSource);
player.setVolume(vol);
playerChangeListener.playerChanged(position, player);
viewSwitcher.setTag(player);
}
private static final class ChildMediaItemsAdapter extends PagerAdapter {
// private static final String TAG = "ChildMediaItemsAdapter";
private final ViewerPostModel[] sliderItems;
private final DataSource.Factory factory;
private final PlayerChangeListener playerChangeListener;
private final ViewGroup.LayoutParams layoutParams;
private ChildMediaItemsAdapter(final ViewerPostModel[] sliderItems,
final DataSource.Factory factory,
final PlayerChangeListener playerChangeListener) {
this.sliderItems = sliderItems;
this.factory = factory;
this.playerChangeListener = playerChangeListener;
layoutParams = new ViewGroup.LayoutParams(Utils.displayMetrics.widthPixels, Utils.displayMetrics.widthPixels + 1);
}
@NonNull
@Override
public Object instantiateItem(@NonNull final ViewGroup container, final int position) {
final Context context = container.getContext();
final ViewerPostModel sliderItem = sliderItems[position];
final String displayUrl = sliderItem.getDisplayUrl();
if (sliderItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
final ViewSwitcher viewSwitcher = createViewSwitcher(context, position, sliderItem.getSliderDisplayUrl(), displayUrl);
container.addView(viewSwitcher);
return viewSwitcher;
}
final GenericDraweeHierarchy hierarchy = GenericDraweeHierarchyBuilder.newInstance(container.getResources())
.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
.build();
final SimpleDraweeView photoView = new SimpleDraweeView(context, hierarchy);
photoView.setLayoutParams(layoutParams);
final ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(displayUrl))
.setLocalThumbnailPreviewsEnabled(true)
.setProgressiveRenderingEnabled(true)
.build();
photoView.setImageRequest(imageRequest);
container.addView(photoView);
return photoView;
}
@NonNull
private ViewSwitcher createViewSwitcher(final Context context, final int position, final String sliderDisplayUrl, final String displayUrl) {
final ViewSwitcher viewSwitcher = new ViewSwitcher(context);
viewSwitcher.setLayoutParams(layoutParams);
final FrameLayout frameLayout = new FrameLayout(context);
frameLayout.setLayoutParams(layoutParams);
final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(context.getResources())
.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
.build();
final SimpleDraweeView simpleDraweeView = new SimpleDraweeView(context, hierarchy);
simpleDraweeView.setLayoutParams(layoutParams);
simpleDraweeView.setImageURI(sliderDisplayUrl);
frameLayout.addView(simpleDraweeView);
final AppCompatImageView imageView = new AppCompatImageView(context);
final int px = Utils.convertDpToPx(50);
final FrameLayout.LayoutParams playButtonLayoutParams = new FrameLayout.LayoutParams(px, px);
playButtonLayoutParams.gravity = Gravity.CENTER;
imageView.setLayoutParams(playButtonLayoutParams);
imageView.setImageResource(R.drawable.exo_icon_play);
frameLayout.addView(imageView);
viewSwitcher.addView(frameLayout);
final PlayerView playerView = new PlayerView(context);
viewSwitcher.addView(playerView);
if (shouldAutoPlay && position == 0) {
loadPlayer(context, position, displayUrl, viewSwitcher, factory, playerChangeListener);
} else
frameLayout.setOnClickListener(v -> loadPlayer(context, position, displayUrl, viewSwitcher, factory, playerChangeListener));
return viewSwitcher;
}
@Override
public void destroyItem(@NonNull final ViewGroup container, final int position, @NonNull final Object object) {
final View view = container.getChildAt(position);
// Log.d(TAG, "destroy position: " + position + ", view: " + view);
if (view instanceof ViewSwitcher) {
final Object tag = view.getTag();
if (tag instanceof SimpleExoPlayer) {
final SimpleExoPlayer player = (SimpleExoPlayer) tag;
player.release();
}
}
container.removeView((View) object);
}
@Override
public int getCount() {
return sliderItems != null ? sliderItems.length : 0;
}
@Override
public boolean isViewFromObject(@NonNull final View view, @NonNull final Object object) {
return view.equals(object);
}
public ViewerPostModel getItemAtPosition(final int position) {
return sliderItems[0];
}
}
}

View File

@ -0,0 +1,158 @@
package awais.instagrabber.adapters.viewholder.feed;
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import awais.instagrabber.R;
import awais.instagrabber.databinding.ItemFeedVideoBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class FeedVideoViewHolder extends FeedItemViewHolder {
private static final String TAG = "FeedVideoViewHolder";
private final ItemFeedVideoBinding binding;
private final Handler handler;
private final DefaultDataSourceFactory dataSourceFactory;
private CacheDataSourceFactory cacheDataSourceFactory;
private FeedModel feedModel;
private SimpleExoPlayer player;
private final Runnable loadRunnable = new Runnable() {
@Override
public void run() {
loadPlayer(feedModel);
}
};
public FeedVideoViewHolder(@NonNull final ItemFeedVideoBinding binding,
final MentionClickListener mentionClickListener,
final View.OnClickListener clickListener,
final View.OnLongClickListener longClickListener) {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, mentionClickListener, clickListener, longClickListener);
this.binding = binding;
binding.itemFeedBottom.videoViewsContainer.setVisibility(View.VISIBLE);
handler = new Handler(Looper.getMainLooper());
final Context context = binding.getRoot().getContext();
dataSourceFactory = new DefaultDataSourceFactory(context, "instagram");
final SimpleCache simpleCache = Utils.getSimpleCacheInstance(context);
if (simpleCache != null) {
cacheDataSourceFactory = new CacheDataSourceFactory(simpleCache, dataSourceFactory);
}
}
@Override
public void bindItem(final FeedModel feedModel) {
// Log.d(TAG, "Binding post: " + feedModel.getPostId());
this.feedModel = feedModel;
setThumbnail(feedModel);
binding.itemFeedBottom.tvVideoViews.setText(String.valueOf(feedModel.getViewCount()));
}
private void setThumbnail(final FeedModel feedModel) {
final ViewGroup.LayoutParams layoutParams = binding.thumbnailParent.getLayoutParams();
final int requiredWidth = Utils.displayMetrics.widthPixels;
layoutParams.width = feedModel.getImageWidth() == 0 ? requiredWidth : feedModel.getImageWidth();
layoutParams.height = feedModel.getImageHeight() == 0 ? requiredWidth + 1 : feedModel.getImageHeight();
binding.thumbnailParent.requestLayout();
final ImageRequest thumbnailRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(feedModel.getThumbnailUrl()))
.setProgressiveRenderingEnabled(true)
.build();
final DraweeController controller = Fresco.newDraweeControllerBuilder()
.setImageRequest(thumbnailRequest)
.build();
binding.thumbnail.setController(controller);
binding.thumbnailParent.setOnClickListener(v -> loadPlayer(feedModel));
}
private void loadPlayer(final FeedModel feedModel) {
if (feedModel == null) {
return;
}
// Log.d(TAG, "playing post:" + feedModel.getPostId());
if (binding.viewSwitcher.getDisplayedChild() == 0) {
binding.viewSwitcher.showNext();
}
binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
final ViewGroup.LayoutParams layoutParams = binding.playerView.getLayoutParams();
final int requiredWidth = Utils.displayMetrics.widthPixels;
final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, feedModel.getImageHeight(), feedModel.getImageWidth());
layoutParams.width = requiredWidth;
layoutParams.height = resultingHeight;
binding.playerView.requestLayout();
float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;
setMuteIcon(vol);
player = (SimpleExoPlayer) binding.playerView.getPlayer();
if (player != null) {
player.release();
}
player = new SimpleExoPlayer.Builder(itemView.getContext())
.setLooper(Looper.getMainLooper())
.build();
player.setVolume(vol);
player.setPlayWhenReady(true);
final DataSource.Factory factory = cacheDataSourceFactory != null ? cacheDataSourceFactory : dataSourceFactory;
final ProgressiveMediaSource.Factory sourceFactory = new ProgressiveMediaSource.Factory(factory);
final ProgressiveMediaSource mediaSource = sourceFactory.createMediaSource(Uri.parse(feedModel.getDisplayUrl()));
player.setRepeatMode(Player.REPEAT_MODE_ALL);
player.prepare(mediaSource);
binding.playerView.setPlayer(player);
final SimpleExoPlayer finalPlayer = player;
binding.itemFeedBottom.btnMute.setOnClickListener(v -> {
final float intVol = finalPlayer.getVolume() == 0f ? 1f : 0f;
finalPlayer.setVolume(intVol);
setMuteIcon(intVol);
Utils.sessionVolumeFull = intVol == 1f;
});
binding.playerView.setOnClickListener(v -> finalPlayer.setPlayWhenReady(!finalPlayer.getPlayWhenReady()));
}
private void setMuteIcon(final float vol) {
binding.itemFeedBottom.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
}
public FeedModel getCurrentFeedModel() {
return feedModel;
}
public void stopPlaying() {
// Log.d(TAG, "Stopping post: " + feedModel.getPostId() + ", player: " + player + ", player.isPlaying: " + (player != null && player.isPlaying()));
handler.removeCallbacks(loadRunnable);
if (player != null) {
player.release();
}
if (binding.viewSwitcher.getDisplayedChild() == 1) {
binding.viewSwitcher.showPrevious();
}
}
public void startPlaying() {
handler.removeCallbacks(loadRunnable);
handler.postDelayed(loadRunnable, 800);
}
}

View File

@ -0,0 +1,83 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONObject;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class CommentAction extends AsyncTask<String, Void, String> {
private static final String TAG = "CommentAction";
private final String cookie;
private final StoryModel storyModel;
private final OnTaskCompleteListener onTaskCompleteListener;
public CommentAction(final String cookie, final StoryModel storyModel, final OnTaskCompleteListener onTaskCompleteListener) {
this.cookie = cookie;
this.storyModel = storyModel;
this.onTaskCompleteListener = onTaskCompleteListener;
}
protected String doInBackground(String... rawAction) {
final String action = rawAction[0];
final String url = "https://i.instagram.com/api/v1/direct_v2/create_group_thread/";
HttpURLConnection urlConnection = null;
try {
urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
urlConnection.setUseCaches(false);
final String urlParameters = Utils.sign("{\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+ "\",\"_uid\":\"" + CookieUtils.getUserIdFromCookie(cookie)
+ "\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+ "\",\"recipient_users\":\"[" + storyModel.getUserId() // <- string of array of number (not joking)
+ "]\"}");
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
if (urlParameters != null) {
urlConnection.setRequestProperty("Content-Length", "" + urlParameters.getBytes().length);
}
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
return new JSONObject(NetworkUtils.readFromConnection(urlConnection)).getString("thread_id");
}
} catch (Throwable ex) {
Log.e(TAG, "reply (CT): " + ex);
// Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return null;
}
@Override
protected void onPostExecute(final String threadId) {
if (threadId == null || onTaskCompleteListener == null) {
return;
}
onTaskCompleteListener.onTaskComplete(threadId);
}
public interface OnTaskCompleteListener {
void onTaskComplete(final String threadId);
}
}

View File

@ -19,7 +19,8 @@ import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.TextUtils;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector;
@ -57,7 +58,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
final int childCommentsLen = childCommentModels.length;
final CommentModel lastChild = childCommentModels[childCommentsLen - 1];
if (lastChild != null && lastChild.hasNextPage() && !Utils.isEmpty(lastChild.getEndCursor())) {
if (lastChild != null && lastChild.hasNextPage() && !TextUtils.isEmpty(lastChild.getEndCursor())) {
final CommentModel[] remoteChildComments = getChildComments(commentModel.getId());
commentModel.setChildCommentModels(remoteChildComments);
lastChild.setPageCursor(false, null);
@ -94,12 +95,12 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) break;
else {
final JSONObject data = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("data")
.getJSONObject("comment").getJSONObject("edge_threaded_comments");
final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
.getJSONObject("comment").getJSONObject("edge_threaded_comments");
final JSONObject pageInfo = data.getJSONObject("page_info");
endCursor = pageInfo.getString("end_cursor");
if (Utils.isEmpty(endCursor)) endCursor = null;
if (TextUtils.isEmpty(endCursor)) endCursor = null;
final JSONArray childComments = data.optJSONArray("edges");
if (childComments != null) {
@ -158,12 +159,12 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) break;
else {
final JSONObject parentComments = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("data")
.getJSONObject("shortcode_media").getJSONObject("edge_media_to_parent_comment");
final JSONObject parentComments = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
.getJSONObject("shortcode_media").getJSONObject("edge_media_to_parent_comment");
final JSONObject pageInfo = parentComments.getJSONObject("page_info");
endCursor = pageInfo.optString("end_cursor");
if (Utils.isEmpty(endCursor)) endCursor = null;
if (TextUtils.isEmpty(endCursor)) endCursor = null;
// final boolean containsToken = endCursor.contains("bifilter_token");
// if (!Utils.isEmpty(endCursor) && (containsToken || endCursor.contains("cached_comments_cursor"))) {
@ -216,7 +217,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
final boolean hasNextPage;
if ((tempJsonObject = tempJsonObject.optJSONObject("page_info")) != null) {
childEndCursor = tempJsonObject.optString("end_cursor");
hasNextPage = tempJsonObject.optBoolean("has_next_page", !Utils.isEmpty(childEndCursor));
hasNextPage = tempJsonObject.optBoolean("has_next_page", !TextUtils.isEmpty(childEndCursor));
} else {
childEndCursor = null;
hasNextPage = false;

View File

@ -21,6 +21,10 @@ import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.DiscoverItemModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
@ -67,7 +71,7 @@ public final class DiscoverFetcher extends AsyncTask<Void, Void, DiscoverItemMod
private ArrayList<DiscoverItemModel> fetchItems(ArrayList<DiscoverItemModel> discoverItemModels, final String maxId) {
try {
final String url = "https://www.instagram.com/explore/grid/?is_prefetch=false&omit_cover_media=true&module=explore_popular" +
"&use_sectional_payload=false&cluster_id="+cluster+"&include_fixed_destinations=true&session_id="+rankToken+maxId;
"&use_sectional_payload=false&cluster_id=" + cluster + "&include_fixed_destinations=true&session_id=" + rankToken + maxId;
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
@ -75,7 +79,7 @@ public final class DiscoverFetcher extends AsyncTask<Void, Void, DiscoverItemMod
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject discoverResponse = new JSONObject(Utils.readFromConnection(urlConnection));
final JSONObject discoverResponse = new JSONObject(NetworkUtils.readFromConnection(urlConnection));
moreAvailable = discoverResponse.getBoolean("more_available");
nextMaxId = discoverResponse.optString("next_max_id");
@ -130,10 +134,10 @@ public final class DiscoverFetcher extends AsyncTask<Void, Void, DiscoverItemMod
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ASYNC_DISCOVER_FETCHER, "fetchItems",
new Pair<>("maxId", maxId),
new Pair<>("lastId", lastId),
new Pair<>("isFirst", isFirst),
new Pair<>("nextMaxId", nextMaxId));
new Pair<>("maxId", maxId),
new Pair<>("lastId", lastId),
new Pair<>("isFirst", isFirst),
new Pair<>("nextMaxId", nextMaxId));
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
}
@ -160,25 +164,27 @@ public final class DiscoverFetcher extends AsyncTask<Void, Void, DiscoverItemMod
// comment = caption instanceof JSONObject ? ((JSONObject) caption).getString("text") : null;
// }
final MediaItemType mediaType = Utils.getMediaItemType(media.getInt("media_type"));
final MediaItemType mediaType = ResponseBodyUtils.getMediaItemType(media.getInt("media_type"));
final DiscoverItemModel model = new DiscoverItemModel(mediaType,
media.getString(Constants.EXTRAS_ID),
media.getString("code"),
Utils.getThumbnailUrl(media, mediaType));
media.getString("pk"),
media.getString("code"),
ResponseBodyUtils.getThumbnailUrl(media, mediaType));
final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" +
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : ""));
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/" + username) : ""));
// to check if file exists
File customDir = null;
if (settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
final String customPath = settingsHelper.getString(FOLDER_PATH);
if (!Utils.isEmpty(customPath)) customDir = new File(customPath +
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : ""));
if (!TextUtils.isEmpty(customPath)) customDir = new File(customPath +
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER)
? "/" + username
: ""));
}
Utils.checkExistence(downloadDir, customDir, mediaType == MediaItemType.MEDIA_TYPE_SLIDER, model);
DownloadUtils.checkExistence(downloadDir, customDir, mediaType == MediaItemType.MEDIA_TYPE_SLIDER, model);
return model;
}

View File

@ -1,7 +1,5 @@
package awais.instagrabber.asyncs;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
@ -28,25 +26,24 @@ import androidx.core.content.FileProvider;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.atomic.AtomicReference;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.activities.ProfilePicViewer;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.CHANNEL_ID;
import static awais.instagrabber.utils.Utils.CHANNEL_NAME;
import static awais.instagrabber.utils.Utils.NOTIF_GROUP_NAME;
import static awais.instagrabber.utils.Utils.isChannelCreated;
import static awais.instagrabber.utils.Constants.NOTIF_GROUP_NAME;
import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.notificationManager;
import static awaisomereport.LogCollector.LogFile;
public final class DownloadAsync extends AsyncTask<Void, Float, File> {
private static final String TAG = "DownloadAsync";
private static int lastNotifId = 1;
private final int currentNotifId;
private final AtomicReference<Context> context;
@ -56,8 +53,12 @@ public final class DownloadAsync extends AsyncTask<Void, Float, File> {
private final Resources resources;
private final NotificationCompat.Builder downloadNotif;
private String shortCode, username;
private final NotificationManagerCompat notificationManager;
public DownloadAsync(final Context context, final String url, final File outFile, final FetchListener<File> fetchListener) {
public DownloadAsync(@NonNull final Context context,
final String url,
final File outFile,
final FetchListener<File> fetchListener) {
this.context = new AtomicReference<>(context);
this.resources = context.getResources();
this.url = url;
@ -67,22 +68,18 @@ public final class DownloadAsync extends AsyncTask<Void, Float, File> {
this.currentNotifId = ++lastNotifId;
if (++lastNotifId + 1 == Integer.MAX_VALUE) lastNotifId = 1;
if (notificationManager == null)
notificationManager = NotificationManagerCompat.from(context.getApplicationContext());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !isChannelCreated) {
notificationManager.createNotificationChannel(new NotificationChannel(CHANNEL_ID,
CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH));
isChannelCreated = true;
}
@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)
.setProgress(100, 0, false).setAutoCancel(false).setOnlyAlertOnce(true)
@StringRes final int titleRes = R.string.downloader_downloading_post;
downloadNotif = new NotificationCompat.Builder(context, Constants.DOWNLOAD_CHANNEL_ID)
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentText(shortCode == null ? username : shortCode)
.setOngoing(true)
.setProgress(100, 0, false)
.setAutoCancel(false)
.setOnlyAlertOnce(true)
.setContentTitle(resources.getString(titleRes));
notificationManager = NotificationManagerCompat.from(context.getApplicationContext());
notificationManager.notify(currentNotifId, downloadNotif.build());
}
@ -99,7 +96,7 @@ public final class DownloadAsync extends AsyncTask<Void, Float, File> {
try {
final URLConnection urlConnection = new URL(url).openConnection();
final long fileSize = Build.VERSION.SDK_INT >= 24 ? urlConnection.getContentLengthLong() :
urlConnection.getContentLength();
urlConnection.getContentLength();
float totalRead = 0;
try (final BufferedInputStream bis = new BufferedInputStream(urlConnection.getInputStream());
@ -152,13 +149,13 @@ public final class DownloadAsync extends AsyncTask<Void, Float, File> {
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogFile.ASYNC_DOWNLOADER, "doInBackground",
new Pair<>("context", context.get()),
new Pair<>("resources", resources),
new Pair<>("lastNotifId", lastNotifId),
new Pair<>("downloadNotif", downloadNotif),
new Pair<>("currentNotifId", currentNotifId),
new Pair<>("notificationManager", notificationManager));
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
new Pair<>("context", context.get()),
new Pair<>("resources", resources),
new Pair<>("lastNotifId", lastNotifId),
new Pair<>("downloadNotif", downloadNotif),
new Pair<>("currentNotifId", currentNotifId),
new Pair<>("notificationManager", notificationManager));
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
}
return null;
}
@ -182,23 +179,23 @@ public final class DownloadAsync extends AsyncTask<Void, Float, File> {
final Context context = this.context.get();
context.sendBroadcast(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT ?
new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.fromFile(Environment.getExternalStorageDirectory())) :
new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(result.getAbsoluteFile()))
new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.fromFile(Environment.getExternalStorageDirectory())) :
new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(result.getAbsoluteFile()))
);
MediaScannerConnection.scanFile(context, new String[]{result.getAbsolutePath()}, null, null);
if (notificationManager != null) {
final Uri uri = FileProvider.getUriForFile(context, "me.austinhuang.instagrabber.provider", result);
final Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", result);
final ContentResolver contentResolver = context.getContentResolver();
Bitmap bitmap = null;
if (Utils.isImage(uri, contentResolver)) {
try {
bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri));
try (final InputStream inputStream = contentResolver.openInputStream(uri)) {
bitmap = BitmapFactory.decodeStream(inputStream);
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogFile.ASYNC_DOWNLOADER, "onPostExecute::bitmap_1");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
}
}
@ -228,15 +225,16 @@ public final class DownloadAsync extends AsyncTask<Void, Float, File> {
final String downloadComplete = resources.getString(R.string.downloader_complete);
downloadNotif.setContentText(null).setContentTitle(downloadComplete).setProgress(0, 0, false)
.setWhen(System.currentTimeMillis()).setOngoing(false).setOnlyAlertOnce(false).setAutoCancel(true)
.setGroup(NOTIF_GROUP_NAME).setGroupSummary(true).setContentIntent(
.setWhen(System.currentTimeMillis()).setOngoing(false).setOnlyAlertOnce(false).setAutoCancel(true)
.setGroup(NOTIF_GROUP_NAME).setGroupSummary(true).setContentIntent(
PendingIntent.getActivity(context, 2020, new Intent(Intent.ACTION_VIEW, uri)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_FROM_BACKGROUND | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_FROM_BACKGROUND | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
.putExtra(Intent.EXTRA_STREAM, uri), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT));
if (bitmap != null)
downloadNotif.setStyle(new NotificationCompat.BigPictureStyle().setBigContentTitle(downloadComplete).bigPicture(bitmap))
.setLargeIcon(bitmap).setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL);
.setLargeIcon(bitmap).setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL);
notificationManager.cancel(currentNotifId);
notificationManager.notify(currentNotifId + 1, downloadNotif.build());

View File

@ -19,12 +19,16 @@ import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.ViewerPostModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector;
public final class FeedFetcher extends AsyncTask<Void, Void, FeedModel[]> {
private static final String TAG = "FeedFetcher";
private static final int maxItemsToLoad = 25; // max is 50, but that's too many posts, setting more than 30 is gay
private final String endCursor;
private final FetchListener<FeedModel[]> fetchListener;
@ -61,8 +65,10 @@ public final class FeedFetcher extends AsyncTask<Void, Void, FeedModel[]> {
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject timelineFeed = new JSONObject(Utils.readFromConnection(urlConnection)).getJSONObject("data")
.getJSONObject(Constants.EXTRAS_USER).getJSONObject("edge_web_feed_timeline");
final String json = NetworkUtils.readFromConnection(urlConnection);
// Log.d(TAG, json);
final JSONObject timelineFeed = new JSONObject(json).getJSONObject("data")
.getJSONObject(Constants.EXTRAS_USER).getJSONObject("edge_web_feed_timeline");
final String endCursor;
final boolean hasNextPage;
@ -83,30 +89,41 @@ public final class FeedFetcher extends AsyncTask<Void, Void, FeedModel[]> {
for (int i = 0; i < feedLen; ++i) {
final JSONObject feedItem = feedItems.getJSONObject(i).getJSONObject("node");
final String mediaType = feedItem.optString("__typename");
if (mediaType.isEmpty() || "GraphSuggestedUserFeedUnit".equals(mediaType)) continue;
if (mediaType.isEmpty() || "GraphSuggestedUserFeedUnit".equals(mediaType))
continue;
final boolean isVideo = feedItem.optBoolean("is_video");
final long videoViews = feedItem.optLong("video_view_count", 0);
final String displayUrl = feedItem.optString("display_url");
if (Utils.isEmpty(displayUrl)) continue;
if (TextUtils.isEmpty(displayUrl)) continue;
final String resourceUrl;
if (isVideo) resourceUrl = feedItem.getString("video_url");
else resourceUrl = feedItem.has("display_resources") ? Utils.getHighQualityImage(feedItem) : displayUrl;
else
resourceUrl = feedItem.has("display_resources") ? ResponseBodyUtils.getHighQualityImage(feedItem) : displayUrl;
ProfileModel profileModel = null;
if (feedItem.has("owner")) {
final JSONObject owner = feedItem.getJSONObject("owner");
profileModel = new ProfileModel(owner.optBoolean("is_private"),
profileModel = new ProfileModel(
owner.optBoolean("is_private"),
false, // if you can see it then you def follow
owner.optBoolean("is_verified"),
owner.getString(Constants.EXTRAS_ID),
owner.getString(Constants.EXTRAS_USERNAME),
owner.optString("full_name"),
null, null,
null,
null,
owner.getString("profile_pic_url"),
null, 0, 0, 0, false, false, false, false);
null,
0,
0,
0,
false,
false,
false,
false);
}
JSONObject tempJsonObject = feedItem.optJSONObject("edge_media_preview_comment");
@ -122,7 +139,21 @@ public final class FeedFetcher extends AsyncTask<Void, Void, FeedModel[]> {
captionText = tempJsonObject.getString("text");
}
final FeedModel feedModel = new FeedModel(profileModel,
final JSONObject location = feedItem.optJSONObject("location");
// Log.d(TAG, "location: " + (location == null ? null : location.toString()));
String locationId = null;
String locationName = null;
if (location != null) {
locationName = location.optString("name");
if (location.has("id")) {
locationId = location.getString("id");
} else if (location.has("pk")) {
locationId = location.getString("pk");
}
// Log.d(TAG, "locationId: " + locationId);
}
final FeedModel feedModel = new FeedModel(
profileModel,
isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
videoViews,
feedItem.getString(Constants.EXTRAS_ID),
@ -135,7 +166,8 @@ public final class FeedFetcher extends AsyncTask<Void, Void, FeedModel[]> {
feedItem.getBoolean("viewer_has_liked"),
feedItem.getBoolean("viewer_has_saved"),
feedItem.getJSONObject("edge_media_preview_like").getLong("count"),
feedItem.optJSONObject("location"));
locationName,
locationId);
final boolean isSlider = "GraphSidecar".equals(mediaType) && feedItem.has("edge_sidecar_to_children");
@ -154,14 +186,17 @@ public final class FeedFetcher extends AsyncTask<Void, Void, FeedModel[]> {
sliderItems[j] = new ViewerPostModel(
isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
node.getString(Constants.EXTRAS_ID),
isChildVideo ? node.getString("video_url") : Utils.getHighQualityImage(node),
null, null, null,
node.optLong("video_view_count", -1), -1, false, false,
isChildVideo ? node.getString("video_url") : ResponseBodyUtils.getHighQualityImage(node),
null,
null,
null,
node.optLong("video_view_count", -1),
-1,
false,
false,
feedItem.getJSONObject("edge_media_preview_like").getLong("count"),
feedItem.isNull("location") ? null : feedItem.getJSONObject("location").optString("name"),
feedItem.isNull("location") ? null :
(feedItem.getJSONObject("location").optString("id") + "/" +
feedItem.getJSONObject("location").optString("slug")));
locationName,
locationId);
sliderItems[j].setSliderDisplayUrl(node.getString("display_url"));
}
@ -187,7 +222,9 @@ public final class FeedFetcher extends AsyncTask<Void, Void, FeedModel[]> {
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ASYNC_FEED_FETCHER, "doInBackground");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
if (BuildConfig.DEBUG) {
Log.e(TAG, "", e);
}
}
return result;

View File

@ -14,7 +14,7 @@ import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NetworkUtils;
import awaisomereport.LogCollector.LogFile;
import static awais.instagrabber.utils.Utils.logCollector;
@ -39,7 +39,7 @@ public final class FeedStoriesFetcher extends AsyncTask<Void, Void, FeedStoryMod
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONArray feedStoriesReel = new JSONObject(Utils.readFromConnection(conn))
final JSONArray feedStoriesReel = new JSONObject(NetworkUtils.readFromConnection(conn))
.getJSONObject("data")
.getJSONObject(Constants.EXTRAS_USER)
.getJSONObject("feed_reels_tray")

View File

@ -13,7 +13,7 @@ import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.FollowModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NetworkUtils;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector;
@ -55,8 +55,8 @@ public final class FollowFetcher extends AsyncTask<Void, Void, FollowModel[]> {
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject data = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("data")
.getJSONObject(Constants.EXTRAS_USER).getJSONObject(isFollowers ? "edge_followed_by" : "edge_follow");
final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
.getJSONObject(Constants.EXTRAS_USER).getJSONObject(isFollowers ? "edge_followed_by" : "edge_follow");
final String endCursor;
final boolean hasNextPage;

View File

@ -9,24 +9,24 @@ import java.net.HttpURLConnection;
import java.net.URL;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.TextUtils;
public class GetActivityAsyncTask extends AsyncTask<Void, Void, GetActivityAsyncTask.NotificationCounts> {
public class GetActivityAsyncTask extends AsyncTask<String, Void, GetActivityAsyncTask.NotificationCounts> {
private static final String TAG = "GetActivityAsyncTask";
private String uid;
private String cookie;
private OnTaskCompleteListener onTaskCompleteListener;
public GetActivityAsyncTask(final String uid, final String cookie, final OnTaskCompleteListener onTaskCompleteListener) {
this.uid = uid;
this.cookie = cookie;
public GetActivityAsyncTask(final OnTaskCompleteListener onTaskCompleteListener) {
this.onTaskCompleteListener = onTaskCompleteListener;
}
protected NotificationCounts doInBackground(Void... voids) {
if (Utils.isEmpty(cookie)) {
return null;
}
protected NotificationCounts doInBackground(final String... cookiesArray) {
if (cookiesArray == null) return null;
final String cookie = cookiesArray[0];
if (TextUtils.isEmpty(cookie)) return null;
final String uid = CookieUtils.getUserIdFromCookie(cookie);
final String url = "https://www.instagram.com/graphql/query/?query_hash=0f318e8cfff9cc9ef09f88479ff571fb"
+ "&variables={\"id\":\"" + uid + "\"}";
HttpURLConnection urlConnection = null;
@ -39,8 +39,12 @@ public class GetActivityAsyncTask extends AsyncTask<Void, Void, GetActivityAsync
if (urlConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return null;
}
final JSONObject data = new JSONObject(Utils.readFromConnection(urlConnection)).getJSONObject("data")
.getJSONObject("user").getJSONObject("edge_activity_count").getJSONArray("edges").getJSONObject(0)
final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(urlConnection))
.getJSONObject("data")
.getJSONObject("user")
.getJSONObject("edge_activity_count")
.getJSONArray("edges")
.getJSONObject(0)
.getJSONObject("node");
return new NotificationCounts(
data.getInt("relationships"),
@ -61,9 +65,7 @@ public class GetActivityAsyncTask extends AsyncTask<Void, Void, GetActivityAsync
@Override
protected void onPostExecute(final NotificationCounts result) {
if (onTaskCompleteListener == null) {
return;
}
if (onTaskCompleteListener == null) return;
onTaskCompleteListener.onTaskComplete(result);
}
@ -105,6 +107,17 @@ public class GetActivityAsyncTask extends AsyncTask<Void, Void, GetActivityAsync
public int getLikesCount() {
return likesCount;
}
@Override
public String toString() {
return "NotificationCounts{" +
"relationshipsCount=" + relationshipsCount +
", userTagsCount=" + userTagsCount +
", commentsCount=" + commentsCount +
", commentLikesCount=" + commentLikesCount +
", likesCount=" + likesCount +
'}';
}
}
public interface OnTaskCompleteListener {

View File

@ -15,7 +15,7 @@ import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.HashtagModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NetworkUtils;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector;
@ -40,7 +40,7 @@ public final class HashtagFetcher extends AsyncTask<Void, Void, HashtagModel> {
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject user = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("graphql").getJSONObject(Constants.EXTRAS_HASHTAG);
final JSONObject user = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql").getJSONObject(Constants.EXTRAS_HASHTAG);
final JSONObject timelineMedia = user.getJSONObject("edge_hashtag_to_media");
if (timelineMedia.has("edges")) {

View File

@ -8,27 +8,29 @@ import org.json.JSONObject;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.HighlightModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NetworkUtils;
public final class HighlightsFetcher extends AsyncTask<Void, Void, HighlightModel[]> {
public final class HighlightsFetcher extends AsyncTask<Void, Void, List<HighlightModel>> {
private final String id;
private final boolean storiesig;
private final FetchListener<HighlightModel[]> fetchListener;
private final FetchListener<List<HighlightModel>> fetchListener;
public HighlightsFetcher(final String id, final boolean storiesig, final FetchListener<HighlightModel[]> fetchListener) {
public HighlightsFetcher(final String id, final boolean storiesig, final FetchListener<List<HighlightModel>> fetchListener) {
this.id = id;
this.storiesig = storiesig;
this.fetchListener = fetchListener;
}
@Override
protected HighlightModel[] doInBackground(final Void... voids) {
HighlightModel[] result = null;
protected List<HighlightModel> doInBackground(final Void... voids) {
List<HighlightModel> result = null;
String url = "https://" + (storiesig ? "storiesig" : "i.instagram") + ".com/api/v1/highlights/" + id + "/highlights_tray/";
try {
@ -39,20 +41,21 @@ public final class HighlightsFetcher extends AsyncTask<Void, Void, HighlightMode
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONArray highlightsReel = new JSONObject(Utils.readFromConnection(conn)).getJSONArray("tray");
final JSONArray highlightsReel = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONArray("tray");
final int length = highlightsReel.length();
final HighlightModel[] highlightModels = new HighlightModel[length];
final String[] highlightIds = new String[length];
final List<HighlightModel> highlightModels = new ArrayList<>();
// final String[] highlightIds = new String[length];
for (int i = 0; i < length; ++i) {
final JSONObject highlightNode = highlightsReel.getJSONObject(i);
highlightModels[i] = new HighlightModel(
highlightModels.add(new HighlightModel(
highlightNode.getString("title"),
highlightNode.getString(Constants.EXTRAS_ID),
highlightNode.getJSONObject("cover_media").getJSONObject("cropped_image_version").getString("url")
);
highlightNode.getJSONObject("cover_media")
.getJSONObject("cropped_image_version")
.getString("url")
));
}
conn.disconnect();
result = highlightModels;
}
@ -66,7 +69,7 @@ public final class HighlightsFetcher extends AsyncTask<Void, Void, HighlightMode
}
@Override
protected void onPostExecute(final HighlightModel[] result) {
protected void onPostExecute(final List<HighlightModel> result) {
if (fetchListener != null) fetchListener.onResult(result);
}
}

View File

@ -18,11 +18,11 @@ import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import awais.instagrabber.models.ImageUploadOptions;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.NumberUtils;
public class ImageUploader extends AsyncTask<ImageUploadOptions, Void, ImageUploader.ImageUploadResponse> {
private static final String TAG = "ImageUploader";
@ -52,7 +52,7 @@ public class ImageUploader extends AsyncTask<ImageUploadOptions, Void, ImageUplo
final String contentLength = String.valueOf(bytes.length);
final Map<String, String> headers = new HashMap<>();
final String uploadId = String.valueOf(new Date().getTime());
final long random = LOWER + new Random().nextLong() * (UPPER - LOWER + 1);
final long random = NumberUtils.random(LOWER, UPPER + 1);
final String name = String.format("%s_0_%s", uploadId, random);
final String waterfallId = options.getWaterfallId() != null ? options.getWaterfallId() : UUID.randomUUID().toString();
headers.put("X-Entity-Type", "image/jpeg");
@ -69,7 +69,7 @@ public class ImageUploader extends AsyncTask<ImageUploadOptions, Void, ImageUplo
connection.setRequestMethod("POST");
connection.setUseCaches(false);
connection.setDoOutput(true);
Utils.setConnectionHeaders(connection, headers);
NetworkUtils.setConnectionHeaders(connection, headers);
out = new BufferedOutputStream(connection.getOutputStream());
out.write(bytes);
out.flush();

View File

@ -5,7 +5,6 @@ import android.util.Log;
import androidx.annotation.Nullable;
import org.json.JSONArray;
import org.json.JSONObject;
import java.math.BigDecimal;
@ -16,18 +15,20 @@ import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.LocationModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NetworkUtils;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector;
public final class LocationFetcher extends AsyncTask<Void, Void, LocationModel> {
private final FetchListener<LocationModel> fetchListener;
private final String idSlug;
private static final String TAG = "LocationFetcher";
public LocationFetcher(String idSlug, FetchListener<LocationModel> fetchListener) {
private final FetchListener<LocationModel> fetchListener;
private final String id;
public LocationFetcher(final String id, final FetchListener<LocationModel> fetchListener) {
// idSlug = id + "/" + slug UPDATE: slug can be ignored tbh
this.idSlug = idSlug;
this.id = id;
this.fetchListener = fetchListener;
}
@ -37,27 +38,29 @@ public final class LocationFetcher extends AsyncTask<Void, Void, LocationModel>
LocationModel result = null;
try {
final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/explore/locations/" + idSlug + "/?__a=1").openConnection();
final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/explore/locations/" + id + "/?__a=1")
.openConnection();
conn.setUseCaches(true);
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject user = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("graphql").getJSONObject(Constants.EXTRAS_LOCATION);
final JSONObject timelineMedia = user.getJSONObject("edge_location_to_media");
if (timelineMedia.has("edges")) {
final JSONArray edges = timelineMedia.getJSONArray("edges");
}
final JSONObject location = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql")
.getJSONObject(Constants.EXTRAS_LOCATION);
final JSONObject timelineMedia = location.getJSONObject("edge_location_to_media");
// if (timelineMedia.has("edges")) {
// final JSONArray edges = timelineMedia.getJSONArray("edges");
// }
result = new LocationModel(
user.getString(Constants.EXTRAS_ID) + "/" + user.getString("slug"),
user.getString("name"),
user.getString("blurb"),
user.getString("website"),
user.getString("profile_pic_url"),
location.getString(Constants.EXTRAS_ID),
location.getString("slug"),
location.getString("name"),
location.getString("blurb"),
location.getString("website"),
location.getString("profile_pic_url"),
timelineMedia.getLong("count"),
BigDecimal.valueOf(user.optDouble("lat", 0d)).toString(),
BigDecimal.valueOf(user.optDouble("lng", 0d)).toString()
BigDecimal.valueOf(location.optDouble("lat", 0d)).toString(),
BigDecimal.valueOf(location.optDouble("lng", 0d)).toString()
);
}
@ -65,9 +68,10 @@ public final class LocationFetcher extends AsyncTask<Void, Void, LocationModel>
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ASYNC_LOCATION_FETCHER, "doInBackground");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
if (BuildConfig.DEBUG) {
Log.e(TAG, "", e);
}
}
return result;
}

View File

@ -8,31 +8,36 @@ import org.json.JSONObject;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.NotificationModel;
import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NetworkUtils;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class NotificationsFetcher extends AsyncTask<Void, Void, NotificationModel[]> {
private final FetchListener<NotificationModel[]> fetchListener;
public final class NotificationsFetcher extends AsyncTask<Void, Void, List<NotificationModel>> {
private static final String TAG = "NotificationsFetcher";
public NotificationsFetcher(final FetchListener<NotificationModel[]> fetchListener) {
private final FetchListener<List<NotificationModel>> fetchListener;
public NotificationsFetcher(final FetchListener<List<NotificationModel>> fetchListener) {
this.fetchListener = fetchListener;
}
@Override
protected NotificationModel[] doInBackground(final Void... voids) {
NotificationModel[] result = null;
protected List<NotificationModel> doInBackground(final Void... voids) {
List<NotificationModel> result = new ArrayList<>();
final String url = "https://www.instagram.com/accounts/activity/?__a=1";
Utils.setupCookies(settingsHelper.getString(Constants.COOKIE));
CookieUtils.setupCookies(settingsHelper.getString(Constants.COOKIE));
try {
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
@ -42,56 +47,62 @@ public final class NotificationsFetcher extends AsyncTask<Void, Void, Notificati
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
JSONObject page = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("graphql").getJSONObject("user"),
ewaf = page.getJSONObject("activity_feed").optJSONObject("edge_web_activity_feed"),
efr = page.optJSONObject("edge_follow_requests"),
data;
final JSONObject page = new JSONObject(NetworkUtils.readFromConnection(conn))
.getJSONObject("graphql")
.getJSONObject("user");
final JSONObject ewaf = page.getJSONObject("activity_feed")
.optJSONObject("edge_web_activity_feed");
final JSONObject efr = page.optJSONObject("edge_follow_requests");
JSONObject data;
JSONArray media;
int totalLength = 0, mediaLen = 0, reqLen = 0;
NotificationModel[] models = null, req = null;
if ((media = ewaf.optJSONArray("edges")) != null && media.length() > 0 &&
(data = media.optJSONObject(0).optJSONObject("node")) != null) {
mediaLen = media.length();
models = new NotificationModel[mediaLen];
for (int i = 0; i < mediaLen; ++i) {
if (ewaf != null
&& (media = ewaf.optJSONArray("edges")) != null
&& media.length() > 0
&& media.optJSONObject(0).optJSONObject("node") != null) {
for (int i = 0; i < media.length(); ++i) {
data = media.optJSONObject(i).optJSONObject("node");
if (Utils.getNotifType(data.getString("__typename")) == null) continue;
models[i] = new NotificationModel(data.getString(Constants.EXTRAS_ID),
if (data == null) continue;
final String type = data.getString("__typename");
final NotificationType notificationType = NotificationType.valueOfType(type);
if (notificationType == null) continue;
final JSONObject user = data.getJSONObject("user");
result.add(new NotificationModel(
data.getString(Constants.EXTRAS_ID),
data.optString("text"), // comments or mentions
data.getLong("timestamp"),
data.getJSONObject("user").getString("username"),
data.getJSONObject("user").getString("profile_pic_url"),
user.getString("id"),
user.getString("username"),
user.getString("profile_pic_url"),
!data.isNull("media") ? data.getJSONObject("media").getString("shortcode") : null,
!data.isNull("media") ? data.getJSONObject("media").getString("thumbnail_src") : null,
Utils.getNotifType(data.getString("__typename")));
!data.isNull("media") ? data.getJSONObject("media").getString("thumbnail_src") : null, notificationType));
}
}
if (efr != null && (media = efr.optJSONArray("edges")) != null && media.length() > 0 &&
(data = media.optJSONObject(0).optJSONObject("node")) != null) {
reqLen = media.length();
req = new NotificationModel[reqLen];
for (int i = 0; i < reqLen; ++i) {
if (efr != null
&& (media = efr.optJSONArray("edges")) != null
&& media.length() > 0
&& media.optJSONObject(0).optJSONObject("node") != null) {
for (int i = 0; i < media.length(); ++i) {
data = media.optJSONObject(i).optJSONObject("node");
req[i] = new NotificationModel(data.getString(Constants.EXTRAS_ID),
data.optString("full_name"), 0L, data.getString("username"),
data.getString("profile_pic_url"), null, null, NotificationType.REQUEST);
if (data == null) continue;
result.add(new NotificationModel(
data.getString(Constants.EXTRAS_ID),
data.optString("full_name"),
0L,
data.getString(Constants.EXTRAS_ID),
data.getString("username"),
data.getString("profile_pic_url"),
null,
null, NotificationType.REQUEST));
}
}
result = new NotificationModel[mediaLen + reqLen];
if (req != null) System.arraycopy(req, 0, result, 0, reqLen);
if (models != null) System.arraycopy(models, 0, result, reqLen, mediaLen);
}
conn.disconnect();
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ASYNC_NOTIFICATION_FETCHER, "doInBackground");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
}
return result;
}
@ -101,7 +112,7 @@ public final class NotificationsFetcher extends AsyncTask<Void, Void, Notificati
}
@Override
protected void onPostExecute(final NotificationModel[] result) {
protected void onPostExecute(final List<NotificationModel> result) {
if (fetchListener != null) fetchListener.onResult(result);
}
}

View File

@ -11,11 +11,16 @@ import java.io.File;
import java.net.HttpURLConnection;
import java.net.URL;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.ViewerPostModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
@ -25,6 +30,8 @@ import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
import static awais.instagrabber.utils.Utils.logCollector;
public final class PostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]> {
private static final String TAG = "PostFetcher";
private final String shortCode;
private final FetchListener<ViewerPostModel[]> fetchListener;
@ -36,7 +43,7 @@ public final class PostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
@Override
protected ViewerPostModel[] doInBackground(final Void... voids) {
ViewerPostModel[] result = null;
Utils.setupCookies(Utils.settingsHelper.getString(Constants.COOKIE)); // <- direct download
CookieUtils.setupCookies(Utils.settingsHelper.getString(Constants.COOKIE)); // <- direct download
try {
final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/p/" + shortCode + "/?__a=1").openConnection();
conn.setUseCaches(false);
@ -44,19 +51,43 @@ public final class PostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject media = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("graphql")
.getJSONObject("shortcode_media");
final String username = media.has("owner") ? media.getJSONObject("owner").getString(Constants.EXTRAS_USERNAME) : null;
final JSONObject media = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql")
.getJSONObject("shortcode_media");
ProfileModel profileModel = null;
if (media.has("owner")) {
final JSONObject owner = media.getJSONObject("owner");
profileModel = new ProfileModel(
owner.optBoolean("is_private"),
owner.optBoolean("is_private"),
owner.optBoolean("is_verified"),
owner.optString("id"),
owner.optString("username"),
owner.optString("full_name"),
null,
null,
owner.optString("profile_pic_url"),
owner.optString("profile_pic_url"),
owner.optInt("edge_owner_to_timeline_media"),
owner.optInt("edge_followed_by"),
-1,
owner.optBoolean("followed_by_viewer"),
owner.optBoolean("restricted_by_viewer"),
owner.optBoolean("blocked_by_viewer"),
owner.optBoolean("requested_by_viewer")
);
}
final String username = profileModel == null ? "" : profileModel.getUsername();
// to check if file exists
final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" +
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : ""));
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/" + username) : ""));
File customDir = null;
if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
final String customPath = Utils.settingsHelper.getString(FOLDER_PATH +
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : ""));
if (!Utils.isEmpty(customPath)) customDir = new File(customPath);
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER)
? ("/" + username)
: ""));
if (!TextUtils.isEmpty(customPath)) customDir = new File(customPath);
}
final long timestamp = media.getLong("taken_at_timestamp");
@ -75,34 +106,36 @@ public final class PostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
else {
final JSONArray captions = mediaToCaption.optJSONArray("edges");
postCaption = captions != null && captions.length() > 0 ?
captions.getJSONObject(0).getJSONObject("node").optString("text") : null;
captions.getJSONObject(0).getJSONObject("node").optString("text") : null;
}
JSONObject commentObject = media.optJSONObject("edge_media_to_parent_comment");
final long commentsCount = commentObject != null ? commentObject.optLong("count") : 0;
String endCursor = null;
if (commentObject != null && (commentObject = commentObject.optJSONObject("page_info")) != null)
if (commentObject != null && (commentObject = commentObject.optJSONObject("page_info")) != null) {
endCursor = commentObject.optString("end_cursor");
}
if (mediaItemType != MediaItemType.MEDIA_TYPE_SLIDER) {
final ViewerPostModel postModel = new ViewerPostModel(mediaItemType,
final ViewerPostModel postModel = new ViewerPostModel(
mediaItemType,
media.getString(Constants.EXTRAS_ID),
isVideo ? media.getString("video_url") : Utils.getHighQualityImage(media),
isVideo ? media.getString("video_url") : ResponseBodyUtils.getHighQualityImage(media),
shortCode,
Utils.isEmpty(postCaption) ? null : postCaption,
username,
TextUtils.isEmpty(postCaption) ? null : postCaption,
profileModel,
isVideo && media.has("video_view_count") ? media.getLong("video_view_count") : -1,
timestamp, media.getBoolean("viewer_has_liked"), media.getBoolean("viewer_has_saved"),
media.getJSONObject("edge_media_preview_like").getLong("count"),
media.isNull("location") ? null : media.getJSONObject("location").optString("name"),
media.isNull("location") ? null :
(media.getJSONObject("location").optString("id") + "/" +
(media.getJSONObject("location").optString("id") + "/" +
media.getJSONObject("location").optString("slug")));
postModel.setCommentsCount(commentsCount);
Utils.checkExistence(downloadDir, customDir, false, postModel);
DownloadUtils.checkExistence(downloadDir, customDir, false, postModel);
result = new ViewerPostModel[]{postModel};
@ -114,22 +147,23 @@ public final class PostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
final JSONObject node = children.getJSONObject(i).getJSONObject("node");
final boolean isChildVideo = node.getBoolean("is_video");
postModels[i] = new ViewerPostModel(isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
postModels[i] = new ViewerPostModel(
isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
media.getString(Constants.EXTRAS_ID),
isChildVideo ? node.getString("video_url") : Utils.getHighQualityImage(node),
isChildVideo ? node.getString("video_url") : ResponseBodyUtils.getHighQualityImage(node),
node.getString(Constants.EXTRAS_SHORTCODE),
postCaption,
username,
profileModel,
isChildVideo && node.has("video_view_count") ? node.getLong("video_view_count") : -1,
timestamp, media.getBoolean("viewer_has_liked"), media.getBoolean("viewer_has_saved"),
media.getJSONObject("edge_media_preview_like").getLong("count"),
media.isNull("location") ? null : media.getJSONObject("location").optString("name"),
media.isNull("location") ? null :
(media.getJSONObject("location").optString("id") + "/" +
media.getJSONObject("location").optString("slug")));
(media.getJSONObject("location").optString("id") + "/" +
media.getJSONObject("location").optString("slug")));
postModels[i].setSliderDisplayUrl(node.getString("display_url"));
Utils.checkExistence(downloadDir, customDir, true, postModels[i]);
DownloadUtils.checkExistence(downloadDir, customDir, true, postModels[i]);
}
postModels[0].setCommentsCount(commentsCount);
@ -140,9 +174,10 @@ public final class PostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
conn.disconnect();
} catch (Exception e) {
if (logCollector != null)
if (logCollector != null) {
logCollector.appendException(e, LogCollector.LogFile.ASYNC_POST_FETCHER, "doInBackground");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
}
Log.e(TAG, "Error fetching post", e);
}
return result;
}

View File

@ -15,7 +15,11 @@ import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.enums.PostItemType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
@ -25,19 +29,19 @@ import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
import static awais.instagrabber.utils.Utils.logCollector;
public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
private static final String TAG = "PostsFetcher";
private final PostItemType type;
private final String endCursor;
private final String id;
private final FetchListener<PostModel[]> fetchListener;
private String username = null;
public PostsFetcher(final String id, final FetchListener<PostModel[]> fetchListener) {
this.id = id;
this.endCursor = "";
this.fetchListener = fetchListener;
}
public PostsFetcher(final String id, final String endCursor, final FetchListener<PostModel[]> fetchListener) {
public PostsFetcher(final String id,
final PostItemType type,
final String endCursor,
final FetchListener<PostModel[]> fetchListener) {
this.id = id;
this.type = type;
this.endCursor = endCursor == null ? "" : endCursor;
this.fetchListener = fetchListener;
}
@ -49,27 +53,32 @@ public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
@Override
protected PostModel[] doInBackground(final Void... voids) {
final boolean isHashTag = id.charAt(0) == '#';
final boolean isSaved = id.charAt(0) == '$';
final boolean isTagged = id.charAt(0) == '%';
final boolean isLocation = id.contains("/");
// final boolean isHashTag = id.charAt(0) == '#';
// final boolean isSaved = id.charAt(0) == '$';
// final boolean isTagged = id.charAt(0) == '%';
// final boolean isLocation = id.contains("/");
final String url;
if (isHashTag)
url = "https://www.instagram.com/graphql/query/?query_hash=9b498c08113f1e09617a1703c22b2f32&variables=" +
"{\"tag_name\":\"" + id.substring(1).toLowerCase() + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
else if (isLocation)
url = "https://www.instagram.com/graphql/query/?query_hash=36bd0f2bf5911908de389b8ceaa3be6d&variables=" +
"{\"id\":\""+ id.split("/")[0] +"\",\"first\":150,\"after\":\"" + endCursor + "\"}";
else if (isSaved)
url = "https://www.instagram.com/graphql/query/?query_hash=8c86fed24fa03a8a2eea2a70a80c7b6b&variables=" +
"{\"id\":\""+ id.substring(1) +"\",\"first\":150,\"after\":\"" + endCursor + "\"}";
else if (isTagged)
url = "https://www.instagram.com/graphql/query/?query_hash=ff260833edf142911047af6024eb634a&variables=" +
"{\"id\":\""+ id.substring(1) +"\",\"first\":150,\"after\":\"" + endCursor + "\"}";
else
url = "https://www.instagram.com/graphql/query/?query_id=17880160963012870&id=" + id + "&first=50&after=" + endCursor;
switch (type) {
case HASHTAG:
url = "https://www.instagram.com/graphql/query/?query_hash=9b498c08113f1e09617a1703c22b2f32&variables=" +
"{\"tag_name\":\"" + id.toLowerCase() + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
break;
case LOCATION:
url = "https://www.instagram.com/graphql/query/?query_hash=36bd0f2bf5911908de389b8ceaa3be6d&variables=" +
"{\"id\":\"" + id + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
break;
case SAVED:
url = "https://www.instagram.com/graphql/query/?query_hash=8c86fed24fa03a8a2eea2a70a80c7b6b&variables=" +
"{\"id\":\"" + id + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
break;
case TAGGED:
url = "https://www.instagram.com/graphql/query/?query_hash=31fe64d9463cbbe58319dced405c6206&variables=" +
"{\"id\":\"" + id + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
break;
default:
url = "https://www.instagram.com/graphql/query/?query_id=17880160963012870&id=" + id + "&first=50&after=" + endCursor;
}
PostModel[] result = null;
try {
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
@ -79,21 +88,30 @@ public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
// to check if file exists
final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" +
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : ""));
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/" + username) : ""));
File customDir = null;
if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
final String customPath = Utils.settingsHelper.getString(FOLDER_PATH +
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : ""));
if (!Utils.isEmpty(customPath)) customDir = new File(customPath);
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER)
? ("/" + username)
: ""));
if (!TextUtils.isEmpty(customPath)) customDir = new File(customPath);
}
final JSONObject mediaPosts = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("data")
.getJSONObject(isHashTag ? Constants.EXTRAS_HASHTAG :
(isLocation ? Constants.EXTRAS_LOCATION : Constants.EXTRAS_USER))
.getJSONObject(isHashTag ? "edge_hashtag_to_media" :
(isLocation ? "edge_location_to_media" :
(isSaved ? "edge_saved_media" :
(isTagged ? "edge_user_to_photos_of_you" : "edge_owner_to_timeline_media"))));
final boolean isHashtag = type == PostItemType.HASHTAG;
final boolean isLocation = type == PostItemType.LOCATION;
final boolean isSaved = type == PostItemType.SAVED;
final boolean isTagged = type == PostItemType.TAGGED;
final JSONObject mediaPosts = new JSONObject(NetworkUtils.readFromConnection(conn))
.getJSONObject("data")
.getJSONObject(isHashtag
? Constants.EXTRAS_HASHTAG
: (isLocation ? Constants.EXTRAS_LOCATION
: Constants.EXTRAS_USER))
.getJSONObject(isHashtag ? "edge_hashtag_to_media" :
isLocation ? "edge_location_to_media" : isSaved ? "edge_saved_media"
: isTagged ? "edge_user_to_photos_of_you"
: "edge_owner_to_timeline_media");
final String endCursor;
final boolean hasNextPage;
@ -122,16 +140,16 @@ public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
else itemType = MediaItemType.MEDIA_TYPE_IMAGE;
models[i] = new PostModel(itemType, mediaNode.getString(Constants.EXTRAS_ID),
mediaNode.getString("display_url"), mediaNode.getString("thumbnail_src"),
mediaNode.getString(Constants.EXTRAS_SHORTCODE),
captions.length() > 0 ? captions.getJSONObject(0).getJSONObject("node").getString("text") : null,
mediaNode.getLong("taken_at_timestamp"), mediaNode.optBoolean("viewer_has_liked"),
mediaNode.optBoolean("viewer_has_saved"), mediaNode.getJSONObject("edge_liked_by").getLong("count"));
mediaNode.getString("display_url"), mediaNode.getString("thumbnail_src"),
mediaNode.getString(Constants.EXTRAS_SHORTCODE),
captions.length() > 0 ? captions.getJSONObject(0).getJSONObject("node").getString("text") : null,
mediaNode.getLong("taken_at_timestamp"), mediaNode.optBoolean("viewer_has_liked"),
mediaNode.optBoolean("viewer_has_saved"), mediaNode.getJSONObject("edge_liked_by").getLong("count"));
Utils.checkExistence(downloadDir, customDir, isSlider, models[i]);
DownloadUtils.checkExistence(downloadDir, customDir, isSlider, models[i]);
}
if (models.length > 0 && models[models.length - 1] != null)
if (models.length != 0 && models[models.length - 1] != null)
models[models.length - 1].setPageCursor(hasNextPage, endCursor);
result = models;
@ -141,7 +159,7 @@ public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
} catch (Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ASYNC_MAIN_POSTS_FETCHER, "doInBackground");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
}
return result;

View File

@ -15,7 +15,8 @@ import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.TextUtils;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector;
@ -40,7 +41,7 @@ public final class ProfileFetcher extends AsyncTask<Void, Void, ProfileModel> {
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject user = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("graphql").getJSONObject(Constants.EXTRAS_USER);
final JSONObject user = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql").getJSONObject(Constants.EXTRAS_USER);
boolean isPrivate = user.getBoolean("is_private");
boolean reallyPrivate = isPrivate;
@ -51,7 +52,7 @@ public final class ProfileFetcher extends AsyncTask<Void, Void, ProfileModel> {
}
String url = user.optString("external_url");
if (Utils.isEmpty(url)) url = null;
if (TextUtils.isEmpty(url)) url = null;
result = new ProfileModel(isPrivate,
reallyPrivate,

View File

@ -15,6 +15,8 @@ import java.net.URL;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
@ -45,26 +47,26 @@ public final class ProfilePictureFetcher extends AsyncTask<Void, Void, String> {
conn.setRequestMethod("GET");
conn.setRequestProperty("User-Agent", Constants.USER_AGENT);
final String result = conn.getResponseCode() == HttpURLConnection.HTTP_OK ? Utils.readFromConnection(conn) : null;
final String result = conn.getResponseCode() == HttpURLConnection.HTTP_OK ? NetworkUtils.readFromConnection(conn) : null;
conn.disconnect();
if (!Utils.isEmpty(result)) {
if (!TextUtils.isEmpty(result)) {
JSONObject data = new JSONObject(result).getJSONObject("user");
if (data.has("hd_profile_pic_url_info"))
out = data.getJSONObject("hd_profile_pic_url_info").optString("url");
}
if (Utils.isEmpty(out) && Utils.settingsHelper.getBoolean(Constants.INSTADP)) {
if (TextUtils.isEmpty(out) && Utils.settingsHelper.getBoolean(Constants.INSTADP)) {
final HttpURLConnection backup =
(HttpURLConnection) new URL("https://instadp.com/fullsize/" + userName).openConnection();
backup.setUseCaches(false);
backup.setRequestMethod("GET");
backup.setRequestProperty("User-Agent", Constants.A_USER_AGENT);
final String instadp = backup.getResponseCode() == HttpURLConnection.HTTP_OK ? Utils.readFromConnection(backup) : null;
final String instadp = backup.getResponseCode() == HttpURLConnection.HTTP_OK ? NetworkUtils.readFromConnection(backup) : null;
backup.disconnect();
if (!Utils.isEmpty(instadp)) {
if (!TextUtils.isEmpty(instadp)) {
final Document doc = Jsoup.parse(instadp);
boolean fallback = false;
@ -86,7 +88,7 @@ public final class ProfilePictureFetcher extends AsyncTask<Void, Void, String> {
}
}
}
if (Utils.isEmpty(out)) out = picUrl;
if (TextUtils.isEmpty(out)) out = picUrl;
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ASYNC_PROFILE_PICTURE_FETCHER, "doInBackground");

View File

@ -0,0 +1,89 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONObject;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.UUID;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.stickers.QuizModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class QuizAction extends AsyncTask<Integer, Void, Integer> {
private static final String TAG = "QuizAction";
private final StoryModel storyModel;
private final QuizModel quizModel;
private final String cookie;
private final OnTaskCompleteListener onTaskCompleteListener;
public QuizAction(final StoryModel storyModel,
final QuizModel quizModel,
final String cookie,
final OnTaskCompleteListener onTaskCompleteListener) {
this.storyModel = storyModel;
this.quizModel = quizModel;
this.cookie = cookie;
this.onTaskCompleteListener = onTaskCompleteListener;
}
protected Integer doInBackground(Integer... rawChoice) {
int choice = rawChoice[0];
final String url = "https://i.instagram.com/api/v1/media/" + storyModel.getStoryMediaId().split("_")[0] + "/" + quizModel.getId() + "/story_quiz_answer/";
HttpURLConnection urlConnection = null;
try {
JSONObject ogBody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString()
+ "\",\"mutation_token\":\"" + UUID.randomUUID().toString()
+ "\",\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+ "\",\"_uid\":\"" + CookieUtils.getUserIdFromCookie(cookie)
+ "\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+ "\"}");
ogBody.put("answer", String.valueOf(choice));
String urlParameters = Utils.sign(ogBody.toString());
urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
if (urlParameters != null) {
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();
Log.d(TAG, "quiz: " + url + " " + cookie + " " + urlParameters);
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
return choice;
}
} catch (Throwable ex) {
Log.e(TAG, "quiz: " + ex);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return -1;
}
@Override
protected void onPostExecute(final Integer choice) {
if (onTaskCompleteListener == null || choice == null) return;
onTaskCompleteListener.onTaskComplete(choice);
}
public interface OnTaskCompleteListener {
void onTaskComplete(final int choice);
}
}

View File

@ -0,0 +1,88 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONObject;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.UUID;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.stickers.QuestionModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class RespondAction extends AsyncTask<String, Void, Boolean> {
private final StoryModel storyModel;
private final QuestionModel questionModel;
private final String cookie;
private final OnTaskCompleteListener onTaskCompleteListener;
public RespondAction(final StoryModel storyModel,
final QuestionModel questionModel,
final String cookie,
final OnTaskCompleteListener onTaskCompleteListener) {
this.storyModel = storyModel;
this.questionModel = questionModel;
this.cookie = cookie;
this.onTaskCompleteListener = onTaskCompleteListener;
}
protected Boolean doInBackground(String... rawChoice) {
final String url = "https://i.instagram.com/api/v1/media/"
+ storyModel.getStoryMediaId().split("_")[0] + "/" + questionModel.getId() + "/story_question_response/";
HttpURLConnection urlConnection = null;
try {
final JSONObject ogbody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString()
+ "\",\"mutation_token\":\"" + UUID.randomUUID().toString()
+ "\",\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+ "\",\"_uid\":\"" + CookieUtils.getUserIdFromCookie(cookie)
+ "\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+ "\"}");
String choice = rawChoice[0].replaceAll("\"", ("\\\""));
ogbody.put("response", choice);
String urlParameters = Utils.sign(ogbody.toString());
urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
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();
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
return true;
}
} catch (Throwable ex) {
Log.e("austin_debug", "respond: " + ex);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return null;
}
@Override
protected void onPostExecute(final Boolean ok) {
if (onTaskCompleteListener == null) return;
onTaskCompleteListener.onTaskComplete(ok);
}
public interface OnTaskCompleteListener {
void onTaskComplete(final boolean result);
}
}

View File

@ -0,0 +1,51 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.utils.NetworkUtils;
public class SeenAction extends AsyncTask<Void, Void, Void> {
private static final String TAG = "SeenAction";
private final String cookie;
private final StoryModel storyModel;
public SeenAction(final String cookie, final StoryModel storyModel) {
this.cookie = cookie;
this.storyModel = storyModel;
}
protected Void doInBackground(Void... voids) {
final String url = "https://www.instagram.com/stories/reel/seen";
try {
final String urlParameters = "reelMediaId=" + storyModel.getStoryMediaId().split("_")[0]
+ "&reelMediaOwnerId=" + storyModel.getUserId()
+ "&reelId=" + storyModel.getUserId()
+ "&reelMediaTakenAt=" + storyModel.getTimestamp()
+ "&viewSeenAt=" + storyModel.getTimestamp();
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("x-csrftoken", cookie.split("csrftoken=")[1].split(";")[0]);
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();
urlConnection.connect();
Log.d(TAG, urlConnection.getResponseCode() + " " + NetworkUtils.readFromConnection(urlConnection));
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e(TAG, "Error", ex);
}
return null;
}
}

View File

@ -17,8 +17,8 @@ import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.SuggestionModel;
import awais.instagrabber.models.enums.SuggestionType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.UrlEncoder;
import awais.instagrabber.utils.Utils;
public final class SuggestionsFetcher extends AsyncTask<String, String, SuggestionModel[]> {
private final FetchListener<SuggestionModel[]> fetchListener;
@ -43,7 +43,7 @@ public final class SuggestionsFetcher extends AsyncTask<String, String, Suggesti
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final String defaultHashTagPic = "https://www.instagram.com/static/images/hashtag/search-hashtag-default-avatar.png/1d8417c9a4f5.png";
final JSONObject jsonObject = new JSONObject(Utils.readFromConnection(conn));
final JSONObject jsonObject = new JSONObject(NetworkUtils.readFromConnection(conn));
conn.disconnect();
final JSONArray usersArray = jsonObject.getJSONArray("users");
@ -75,9 +75,9 @@ public final class SuggestionsFetcher extends AsyncTask<String, String, Suggesti
// name
suggestionModels.add(new SuggestionModel(false,
place.getJSONObject("location").getString("pk")+"/"+place.getString("slug"),
place.getJSONObject("location").getString("pk"), // +"/"+place.getString("slug"),
place.getString("title"),
place.optString("profile_pic_url", null),
place.optString("profile_pic_url"),
SuggestionType.TYPE_LOCATION,
placesArrayJSONObject.optInt("position", suggestionModels.size() - 1)));
}

View File

@ -13,7 +13,7 @@ import java.net.URL;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NetworkUtils;
public final class UsernameFetcher extends AsyncTask<Void, Void, String> {
private final FetchListener<String> fetchListener;
@ -36,7 +36,7 @@ public final class UsernameFetcher extends AsyncTask<Void, Void, String> {
final JSONObject user;
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK &&
(user = new JSONObject(Utils.readFromConnection(conn)).optJSONObject(Constants.EXTRAS_USER)) != null)
(user = new JSONObject(NetworkUtils.readFromConnection(conn)).optJSONObject(Constants.EXTRAS_USER)) != null)
result = user.getString(Constants.EXTRAS_USERNAME);
conn.disconnect();

View File

@ -0,0 +1,69 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.stickers.PollModel;
import awais.instagrabber.utils.Constants;
public class VoteAction extends AsyncTask<Integer, Void, Integer> {
private static final String TAG = "VoteAction";
private final StoryModel storyModel;
private final PollModel pollModel;
private final String cookie;
private final OnTaskCompleteListener onTaskCompleteListener;
public VoteAction(final StoryModel storyModel,
final PollModel pollModel,
final String cookie,
final OnTaskCompleteListener onTaskCompleteListener) {
this.storyModel = storyModel;
this.pollModel = pollModel;
this.cookie = cookie;
this.onTaskCompleteListener = onTaskCompleteListener;
}
protected Integer doInBackground(Integer... rawChoice) {
int choice = rawChoice[0];
final String url = "https://www.instagram.com/media/" + storyModel.getStoryMediaId().split("_")[0] + "/" + pollModel.getId() + "/story_poll_vote/";
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]);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", "6");
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes("vote=" + choice);
wr.flush();
wr.close();
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
return choice;
}
urlConnection.disconnect();
} catch (Exception ex) {
Log.e(TAG, "Error", ex);
}
return -1;
}
@Override
protected void onPostExecute(final Integer result) {
if (result == null || onTaskCompleteListener == null) return;
onTaskCompleteListener.onTaskComplete(result);
}
public interface OnTaskCompleteListener {
void onTaskComplete(final int choice);
}
}

View File

@ -18,7 +18,9 @@ import awais.instagrabber.models.direct_messages.InboxThreadModel;
import awais.instagrabber.models.enums.UserInboxDirection;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
import static awais.instagrabber.utils.Utils.logCollector;
import static awaisomereport.LogCollector.LogFile;
@ -48,10 +50,10 @@ public final class DirectMessageInboxThreadFetcher extends AsyncTask<Void, Void,
final Map<String, String> queryParamsMap = new HashMap<>();
queryParamsMap.put("visual_message_return_type", "unseen");
if (direction != null) queryParamsMap.put("direction", direction.getValue());
if (!Utils.isEmpty(endCursor)) {
if (!TextUtils.isEmpty(endCursor)) {
queryParamsMap.put("cursor", endCursor);
}
final String queryString = Utils.getQueryString(queryParamsMap);
final String queryString = NetworkUtils.getQueryString(queryParamsMap);
final String url = "https://i.instagram.com/api/v1/direct_v2/threads/" + id + "/?" + queryString;
try {
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
@ -60,8 +62,8 @@ public final class DirectMessageInboxThreadFetcher extends AsyncTask<Void, Void,
conn.setUseCaches(false);
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject data = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("thread");
result = Utils.createInboxThreadModel(data, true);
final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("thread");
result = ResponseBodyUtils.createInboxThreadModel(data, true);
}
conn.disconnect();

View File

@ -19,6 +19,7 @@ import java.util.Map;
import java.util.UUID;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
@ -43,8 +44,8 @@ public class DirectThreadBroadcaster extends AsyncTask<DirectThreadBroadcaster.B
final String cookie = settingsHelper.getString(Constants.COOKIE);
final String cc = UUID.randomUUID().toString();
final Map<String, String> form = new HashMap<>();
form.put("_csrftoken", cookie.split("csrftoken=")[1].split(";")[0]);
form.put("_uid", Utils.getUserIdFromCookie(cookie));
form.put("_csrftoken", CookieUtils.getCsrfTokenFromCookie(cookie));
form.put("_uid", CookieUtils.getUserIdFromCookie(cookie));
form.put("__uuid", settingsHelper.getString(Constants.DEVICE_UUID));
form.put("client_context", cc);
form.put("mutation_token", cc);

View File

@ -8,6 +8,9 @@ import androidx.annotation.Nullable;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
@ -17,17 +20,21 @@ import awais.instagrabber.models.direct_messages.InboxModel;
import awais.instagrabber.models.direct_messages.InboxThreadModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
import static awais.instagrabber.utils.Utils.logCollector;
import static awaisomereport.LogCollector.LogFile;
public final class InboxFetcher extends AsyncTask<Void, Void, InboxModel> {
private static final String TAG = "InboxFetcher";
private final String endCursor;
private final FetchListener<InboxModel> fetchListener;
public InboxFetcher(final String endCursor, final FetchListener<InboxModel> fetchListener) {
this.endCursor = Utils.isEmpty(endCursor) ? "" : "?cursor=" + endCursor;
this.endCursor = TextUtils.isEmpty(endCursor) ? "" : "?cursor=" + endCursor;
this.fetchListener = fetchListener;
}
@ -43,46 +50,59 @@ public final class InboxFetcher extends AsyncTask<Void, Void, InboxModel> {
conn.setRequestProperty("Accept-Language", LocaleUtils.getCurrentLocale().getLanguage() + ",en-US;q=0.8");
conn.setUseCaches(false);
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
JSONObject data = new JSONObject(Utils.readFromConnection(conn));
// try (FileWriter fileWriter = new FileWriter(new File("/sdcard/test.json"))) {
// fileWriter.write(data.toString(2));
// }
final long seqId = data.optLong("seq_id");
final int pendingRequestsCount = data.optInt("pending_requests_total");
final boolean hasPendingTopRequests = data.optBoolean("has_pending_top_requests");
data = data.getJSONObject("inbox");
final boolean blendedInboxEnabled = data.optBoolean("blended_inbox_enabled");
final boolean hasOlder = data.optBoolean("has_older");
final int unseenCount = data.optInt("unseen_count");
final long unseenCountTimestamp = data.optLong("unseen_count_ts");
final String oldestCursor = data.optString("oldest_cursor");
InboxThreadModel[] inboxThreadModels = null;
final JSONArray threadsArray = data.optJSONArray("threads");
if (threadsArray != null) {
final int threadsLen = threadsArray.length();
inboxThreadModels = new InboxThreadModel[threadsLen];
for (int i = 0; i < threadsLen; ++i)
inboxThreadModels[i] = Utils.createInboxThreadModel(threadsArray.getJSONObject(i), false);
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
final InputStream responseInputStream = conn.getErrorStream();
final BufferedReader r = new BufferedReader(new InputStreamReader(responseInputStream));
final StringBuilder builder = new StringBuilder();
for (String line = r.readLine(); line != null; line = r.readLine()) {
if (builder.length() != 0) {
builder.append("\n");
}
builder.append(line);
}
result = new InboxModel(hasOlder, hasPendingTopRequests,
blendedInboxEnabled, unseenCount, pendingRequestsCount,
seqId, unseenCountTimestamp, oldestCursor, inboxThreadModels);
Log.e(TAG, "Error response: " + conn.getResponseCode() + ", " + builder.toString());
r.close();
conn.disconnect();
return null;
}
JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn));
// try (FileWriter fileWriter = new FileWriter(new File("/sdcard/test.json"))) {
// fileWriter.write(data.toString(2));
// }
final long seqId = data.optLong("seq_id");
final int pendingRequestsCount = data.optInt("pending_requests_total");
final boolean hasPendingTopRequests = data.optBoolean("has_pending_top_requests");
data = data.getJSONObject("inbox");
final boolean blendedInboxEnabled = data.optBoolean("blended_inbox_enabled");
final boolean hasOlder = data.optBoolean("has_older");
final int unseenCount = data.optInt("unseen_count");
final long unseenCountTimestamp = data.optLong("unseen_count_ts");
final String oldestCursor = data.optString("oldest_cursor");
InboxThreadModel[] inboxThreadModels = null;
final JSONArray threadsArray = data.optJSONArray("threads");
if (threadsArray != null) {
final int threadsLen = threadsArray.length();
inboxThreadModels = new InboxThreadModel[threadsLen];
for (int i = 0; i < threadsLen; ++i)
inboxThreadModels[i] = ResponseBodyUtils.createInboxThreadModel(threadsArray.getJSONObject(i), false);
}
result = new InboxModel(hasOlder, hasPendingTopRequests,
blendedInboxEnabled, unseenCount, pendingRequestsCount,
seqId, unseenCountTimestamp, oldestCursor, inboxThreadModels);
conn.disconnect();
} catch (final Exception e) {
result = null;
if (logCollector != null)
logCollector.appendException(e, LogFile.ASYNC_DMS, "doInBackground");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
}
return result;

View File

@ -16,6 +16,10 @@ import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
@ -50,7 +54,7 @@ public final class iLikedFetcher extends AsyncTask<Void, Void, PostModel[]> {
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject body = new JSONObject(Utils.readFromConnection(conn));
final JSONObject body = new JSONObject(NetworkUtils.readFromConnection(conn));
final String endCursor;
final boolean hasNextPage;
@ -77,12 +81,12 @@ public final class iLikedFetcher extends AsyncTask<Void, Void, PostModel[]> {
else itemType = MediaItemType.MEDIA_TYPE_IMAGE;
models[i] = new PostModel(itemType, mediaNode.getString(Constants.EXTRAS_ID),
isSlider
? Utils.getHighQualityImage(mediaNode.getJSONArray("carousel_media").getJSONObject(0))
: Utils.getHighQualityImage(mediaNode),
isSlider
? Utils.getLowQualityImage(mediaNode.getJSONArray("carousel_media").getJSONObject(0))
: Utils.getLowQualityImage(mediaNode),
isSlider
? ResponseBodyUtils.getHighQualityImage(mediaNode.getJSONArray("carousel_media").getJSONObject(0))
: ResponseBodyUtils.getHighQualityImage(mediaNode),
isSlider
? ResponseBodyUtils.getLowQualityImage(mediaNode.getJSONArray("carousel_media").getJSONObject(0))
: ResponseBodyUtils.getLowQualityImage(mediaNode),
mediaNode.getString("code"),
mediaNode.isNull("caption") ? null : mediaNode.getJSONObject("caption").optString("text"),
mediaNode.getLong("taken_at"), true,
@ -95,9 +99,9 @@ public final class iLikedFetcher extends AsyncTask<Void, Void, PostModel[]> {
if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
final String customPath = Utils.settingsHelper.getString(FOLDER_PATH +
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : ""));
if (!Utils.isEmpty(customPath)) customDir = new File(customPath);
if (!TextUtils.isEmpty(customPath)) customDir = new File(customPath);
}
Utils.checkExistence(downloadDir, customDir, isSlider, models[i]);
DownloadUtils.checkExistence(downloadDir, customDir, isSlider, models[i]);
}
if (models[models.length - 1] != null)

View File

@ -13,9 +13,14 @@ import java.net.URL;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.ViewerPostModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
@ -25,6 +30,8 @@ import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
import static awais.instagrabber.utils.Utils.logCollector;
public final class iPostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]> {
private static final String TAG = "iPostFetcher";
private final String id;
private final FetchListener<ViewerPostModel[]> fetchListener;
@ -43,19 +50,55 @@ public final class iPostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject media = new JSONObject(Utils.readFromConnection(conn)).getJSONArray("items").getJSONObject(0);
final String username = media.has("user") ? media.getJSONObject("user").getString(Constants.EXTRAS_USERNAME) : null;
final JSONObject media = new JSONObject(NetworkUtils.readFromConnection(conn))
.getJSONArray("items")
.getJSONObject(0);
ProfileModel profileModel = null;
if (media.has("user")) {
final JSONObject user = media.getJSONObject("user");
final JSONObject friendshipStatus = user.optJSONObject("friendship_status");
boolean following = false;
boolean isRestricted = false;
boolean outgoingRequest = false;
if (friendshipStatus != null) {
following = friendshipStatus.optBoolean("following");
isRestricted = friendshipStatus.optBoolean("is_restricted");
outgoingRequest = friendshipStatus.optBoolean("outgoing_request");
}
profileModel = new ProfileModel(
user.optBoolean("is_private"),
user.optBoolean("is_private"),
user.optBoolean("is_verified"),
user.optString("pk"),
user.getString(Constants.EXTRAS_USERNAME),
user.optString("fullname"),
null,
null,
user.getString("profile_pic_url"),
null,
-1,
-1,
-1,
following,
isRestricted,
false,
outgoingRequest
);
}
if (profileModel == null) {
return new ViewerPostModel[]{};
}
// to check if file exists
final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" +
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : ""));
final boolean shouldDownloadToUserFolder = Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER);
final File downloadDir = new File(
Environment.getExternalStorageDirectory(),
"Download" + (shouldDownloadToUserFolder ? "/" + profileModel.getUsername() : ""));
File customDir = null;
if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
final String customPath = Utils.settingsHelper.getString(FOLDER_PATH +
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : ""));
if (!Utils.isEmpty(customPath)) customDir = new File(customPath);
final String customPath = Utils.settingsHelper.getString(FOLDER_PATH)
+ (shouldDownloadToUserFolder ? "/" + profileModel.getUsername() : "");
if (!TextUtils.isEmpty(customPath)) customDir = new File(customPath);
}
final long timestamp = media.getLong("taken_at");
@ -75,26 +118,37 @@ public final class iPostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
final long commentsCount = media.optLong("comment_count");
final JSONObject location = media.optJSONObject("location");
String locationId = null;
String locationName = null;
if (location != null) {
locationName = location.optString("name");
if (location.has("id")) {
locationId = location.getString("id");
} else if (location.has("pk")) {
locationId = location.getString("pk");
}
}
// final String locationString = location.optString("id") + "/" + location.optString("slug");
if (mediaItemType != MediaItemType.MEDIA_TYPE_SLIDER) {
final ViewerPostModel postModel = new ViewerPostModel(mediaItemType,
final ViewerPostModel postModel = new ViewerPostModel(
mediaItemType,
media.getString(Constants.EXTRAS_ID),
isVideo
? Utils.getHighQualityPost(media.optJSONArray("video_versions"), true, true, false)
: Utils.getHighQualityImage(media),
isVideo ? ResponseBodyUtils.getHighQualityPost(media.optJSONArray("video_versions"), true, true, false)
: ResponseBodyUtils.getHighQualityImage(media),
media.getString("code"),
Utils.isEmpty(postCaption) ? null : postCaption,
username,
TextUtils.isEmpty(postCaption) ? null : postCaption,
profileModel,
isVideo && media.has("view_count") ? media.getLong("view_count") : -1,
timestamp, media.optBoolean("has_liked"), media.optBoolean("has_viewer_saved"),
timestamp, media.optBoolean("has_liked"),
media.optBoolean("has_viewer_saved"),
media.getLong("like_count"),
media.isNull("location") ? null : media.getJSONObject("location").optString("name"),
media.isNull("location") ? null :
(media.getJSONObject("location").optString("id") + "/" +
media.getJSONObject("location").optString("slug")));
locationName,
locationId);
postModel.setCommentsCount(commentsCount);
Utils.checkExistence(downloadDir, customDir, false, postModel);
DownloadUtils.checkExistence(downloadDir, customDir, false, postModel);
result = new ViewerPostModel[]{postModel};
@ -106,24 +160,23 @@ public final class iPostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
final JSONObject node = children.getJSONObject(i);
final boolean isChildVideo = node.has("video_duration");
postModels[i] = new ViewerPostModel(isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
postModels[i] = new ViewerPostModel(
isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO
: MediaItemType.MEDIA_TYPE_IMAGE,
media.getString(Constants.EXTRAS_ID),
isChildVideo
? Utils.getHighQualityPost(node.optJSONArray("video_versions"), true, true, false)
: Utils.getHighQualityImage(node),
isChildVideo ? ResponseBodyUtils.getHighQualityPost(node.optJSONArray("video_versions"), true, true, false)
: ResponseBodyUtils.getHighQualityImage(node),
media.getString("code"),
postCaption,
username,
profileModel,
-1,
timestamp, media.optBoolean("has_liked"), media.optBoolean("has_viewer_saved"),
timestamp, media.optBoolean("has_liked"),
media.optBoolean("has_viewer_saved"),
media.getLong("like_count"),
media.isNull("location") ? null : media.getJSONObject("location").optString("name"),
media.isNull("location") ? null :
(media.getJSONObject("location").optString("id") + "/" +
media.getJSONObject("location").optString("slug")));
postModels[i].setSliderDisplayUrl(Utils.getHighQualityImage(node));
Utils.checkExistence(downloadDir, customDir, true, postModels[i]);
locationName,
locationId);
postModels[i].setSliderDisplayUrl(ResponseBodyUtils.getHighQualityImage(node));
DownloadUtils.checkExistence(downloadDir, customDir, true, postModels[i]);
}
postModels[0].setCommentsCount(commentsCount);
@ -135,7 +188,9 @@ public final class iPostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
} catch (Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ASYNC_POST_FETCHER, "doInBackground (i)");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
if (BuildConfig.DEBUG) {
Log.e(TAG, "", e);
}
}
return result;
}

View File

@ -19,7 +19,8 @@ import awais.instagrabber.models.stickers.QuizModel;
import awais.instagrabber.models.stickers.SwipeUpModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector;
@ -27,11 +28,18 @@ import static awais.instagrabber.utils.Utils.logCollector;
public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[]> {
private final String id;
private String username;
private final boolean isLoc, isHashtag, storiesig, highlight;
private final boolean isLoc;
private final boolean isHashtag;
private final boolean storiesig;
private final boolean highlight;
private final FetchListener<StoryModel[]> fetchListener;
public iStoryStatusFetcher(final String id, final String username, final boolean isLoc,
final boolean isHashtag, final boolean storiesig, final boolean highlight,
public iStoryStatusFetcher(final String id,
final String username,
final boolean isLoc,
final boolean isHashtag,
final boolean storiesig,
final boolean highlight,
final FetchListener<StoryModel[]> fetchListener) {
this.id = id;
this.username = username;
@ -45,9 +53,35 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
@Override
protected StoryModel[] doInBackground(final Void... voids) {
StoryModel[] result = null;
final String url = "https://" + (storiesig ? "storiesig" : "i.instagram") + ".com/api/v1/"
+ (isLoc ? "locations/" : (isHashtag ? "tags/" : (highlight ? "feed/reels_media?user_ids=" : "feed/user/")))
+ id.replace(":", "%3A") + (highlight ? "" : (storiesig ? "/reel_media/" : "/story/"));
final String userId = id.replace(":", "%3A");
final StringBuilder builder = new StringBuilder();
builder.append("https://");
if (storiesig) {
builder.append("storiesig");
} else {
builder.append("i.instagram");
}
builder.append(".com/api/v1/");
if (isLoc) {
builder.append("locations/");
}
if (isHashtag) {
builder.append("tags/");
}
if (highlight) {
builder.append("feed/reels_media?user_ids=");
} else {
builder.append("feed/user/");
}
builder.append(userId);
if (!highlight) {
if (storiesig) {
builder.append("/reel_media/");
} else {
builder.append("/story/");
}
}
final String url = builder.toString();
try {
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setInstanceFollowRedirects(true);
@ -57,11 +91,13 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
JSONObject data = new JSONObject(Utils.readFromConnection(conn));
if (!storiesig && !highlight) data = data.optJSONObject((isLoc || isHashtag) ? "story" : "reel");
JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn));
if (!storiesig && !highlight)
data = data.optJSONObject((isLoc || isHashtag) ? "story" : "reel");
else if (highlight) data = data.getJSONObject("reels").optJSONObject(id);
if (username == null && !isLoc && !isHashtag) username = data.getJSONObject("user").getString("username");
if (username == null && !isLoc && !isHashtag)
username = data.getJSONObject("user").getString("username");
JSONArray media;
if (data != null && (media = data.optJSONArray("items")) != null
@ -83,7 +119,7 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
final JSONArray videoResources = data.optJSONArray("video_versions");
if (isVideo && videoResources != null)
models[i].setVideoUrl(Utils.getHighQualityPost(videoResources, true, true, false));
models[i].setVideoUrl(ResponseBodyUtils.getHighQualityPost(videoResources, true, true, false));
if (data.has("story_feed_media")) {
models[i].setTappableShortCode(data.getJSONArray("story_feed_media").getJSONObject(0).optString("media_id"));
@ -110,8 +146,8 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
JSONObject tappableObject = data.getJSONArray("story_questions").getJSONObject(0).optJSONObject("question_sticker");
if (tappableObject != null && !tappableObject.getString("question_type").equals("music"))
models[i].setQuestion(new QuestionModel(
String.valueOf(tappableObject.getLong("question_id")),
tappableObject.getString("question")
String.valueOf(tappableObject.getLong("question_id")),
tappableObject.getString("question")
));
}
@ -123,7 +159,7 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
for (int q = 0; q < choices.length; ++q) {
JSONObject tempchoice = tappableObject.getJSONArray("tallies").getJSONObject(q);
choices[q] = (q == tappableObject.getInt("correct_answer") ? "*** " : "")
+tempchoice.getString("text");
+ tempchoice.getString("text");
counts[q] = tempchoice.getLong("count");
}
models[i].setQuiz(new QuizModel(
@ -155,20 +191,20 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
+ (locations == null ? 0 : locations.length())];
if (hashtags != null) {
for (int h = 0; h < hashtags.length(); ++h) {
mentions[h] = "#"+hashtags.getJSONObject(h).getJSONObject("hashtag").getString("name");
mentions[h] = "#" + hashtags.getJSONObject(h).getJSONObject("hashtag").getString("name");
}
}
if (atmarks != null) {
for (int h = 0; h < atmarks.length(); ++h) {
mentions[h + (hashtags == null ? 0 : hashtags.length())] =
"@"+atmarks.getJSONObject(h).getJSONObject("user").getString("username");
"@" + atmarks.getJSONObject(h).getJSONObject("user").getString("username");
}
}
if (locations != null) {
for (int h = 0; h < locations.length(); ++h) {
mentions[h + (hashtags == null ? 0 : hashtags.length()) + (atmarks == null ? 0 : atmarks.length())] =
locations.getJSONObject(h).getJSONObject("location").getLong("pk")
+"/ ("+locations.getJSONObject(h).getJSONObject("location").getString("short_name")+")";
+ "/ (" + locations.getJSONObject(h).getJSONObject("location").getString("short_name") + ")";
}
}
if (mentions.length != 0) models[i].setMentions(mentions);

View File

@ -14,7 +14,7 @@ import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.DiscoverTopicModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.NetworkUtils;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector;
@ -39,7 +39,7 @@ public final class iTopicFetcher extends AsyncTask<Void, Void, DiscoverTopicMode
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject body = new JSONObject(Utils.readFromConnection(conn));
final JSONObject body = new JSONObject(NetworkUtils.readFromConnection(conn));
final JSONArray edges = body.getJSONArray("clusters");
String[] names = new String[edges.length()], ids = new String[edges.length()];

View File

@ -1,22 +1,23 @@
package awais.instagrabber.customviews;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.annotation.Nullable;
public final class CircularImageView extends AppCompatImageView {
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchy;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.GenericDraweeHierarchyInflater;
import com.facebook.drawee.generic.RoundingParams;
import com.facebook.drawee.view.SimpleDraweeView;
public final class CircularImageView extends SimpleDraweeView {
private final int borderSize = 8;
private int color = Color.TRANSPARENT;
private final Paint paint = new Paint();
@ -24,82 +25,115 @@ public final class CircularImageView extends AppCompatImageView {
private BitmapShader shader;
private Bitmap bitmap;
public CircularImageView(Context context, GenericDraweeHierarchy hierarchy) {
super(context);
setHierarchy(hierarchy);
setup();
}
public CircularImageView(final Context context) {
super(context);
inflateHierarchy(context, null);
setup();
}
public CircularImageView(final Context context, final AttributeSet attrs) {
super(context, attrs);
inflateHierarchy(context, attrs);
setup();
}
public CircularImageView(final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflateHierarchy(context, attrs);
setup();
}
protected void inflateHierarchy(Context context, @Nullable AttributeSet attrs) {
Resources resources = context.getResources();
final RoundingParams roundingParams = RoundingParams.asCircle();
GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(resources)
.setRoundingParams(roundingParams)
.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER);
GenericDraweeHierarchyInflater.updateBuilder(builder, context, attrs);
setAspectRatio(builder.getDesiredAspectRatio());
setHierarchy(builder.build());
}
private void setup() {
paint.setAntiAlias(true);
paintBorder.setColor(color);
paintBorder.setAntiAlias(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setOutlineProvider(new ViewOutlineProvider() {
private int viewHeight;
private int viewWidth;
@Override
public void getOutline(final View view, final Outline outline) {
if (viewHeight == 0) viewHeight = getHeight();
if (viewWidth == 0) viewWidth = getWidth();
outline.setRoundRect(borderSize, borderSize, viewWidth - borderSize, viewHeight - borderSize, viewHeight >> 1);
}
});
}
// paint.setAntiAlias(true);
// paintBorder.setColor(color);
// paintBorder.setAntiAlias(true);
//
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// setOutlineProvider(new ViewOutlineProvider() {
// private int viewHeight;
// private int viewWidth;
//
// @Override
// public void getOutline(final View view, final Outline outline) {
// if (viewHeight == 0) viewHeight = getHeight();
// if (viewWidth == 0) viewWidth = getWidth();
// outline.setRoundRect(borderSize, borderSize, viewWidth - borderSize, viewHeight - borderSize, viewHeight >> 1);
// }
// });
// }
// final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(getResources())
// .setRoundingParams(RoundingParams.)
// .build();
// setHierarchy(hierarchy);
// invalidate();
}
@Override
public void onDraw(final Canvas canvas) {
final BitmapDrawable bitmapDrawable = (BitmapDrawable) getDrawable();
if (bitmapDrawable != null) {
final Bitmap prevBitmap = bitmap;
bitmap = bitmapDrawable.getBitmap();
final boolean changed = prevBitmap != bitmap;
if (bitmap != null) {
final int width = getWidth();
final int height = getHeight();
if (shader == null || changed) {
shader = new BitmapShader(Bitmap.createScaledBitmap(bitmap, width, height, true), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(shader);
}
if (changed) color = 0;
paintBorder.setColor(color);
final int circleCenter = (width - borderSize) / 2;
final int position = circleCenter + (borderSize / 2);
canvas.drawCircle(position, position, position - 4.0f, paintBorder);
canvas.drawCircle(position, position, circleCenter - 4.0f, paint);
}
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setLayerType(LAYER_TYPE_HARDWARE, null);
}
@Override
protected void onDetachedFromWindow() {
setLayerType(LAYER_TYPE_NONE, null);
super.onDetachedFromWindow();
}
// @Override
// public void onDraw(final Canvas canvas) {
// final BitmapDrawable bitmapDrawable = (BitmapDrawable) getDrawable();
// if (bitmapDrawable != null) {
// final Bitmap prevBitmap = bitmap;
// bitmap = bitmapDrawable.getBitmap();
// final boolean changed = prevBitmap != bitmap;
// if (bitmap != null) {
// final int width = getWidth();
// final int height = getHeight();
//
// if (shader == null || changed) {
// shader = new BitmapShader(Bitmap.createScaledBitmap(bitmap, width, height, true), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
// paint.setShader(shader);
// }
//
// if (changed) color = 0;
// paintBorder.setColor(color);
//
// final int circleCenter = (width - borderSize) / 2;
// final int position = circleCenter + (borderSize / 2);
// canvas.drawCircle(position, position, position - 4.0f, paintBorder);
// canvas.drawCircle(position, position, circleCenter - 4.0f, paint);
// }
// }
// }
//
// @Override
// protected void onAttachedToWindow() {
// super.onAttachedToWindow();
// setLayerType(LAYER_TYPE_HARDWARE, null);
// }
//
// @Override
// protected void onDetachedFromWindow() {
// setLayerType(LAYER_TYPE_NONE, null);
// super.onDetachedFromWindow();
// }
public void setStoriesBorder() {
this.color = Color.GREEN;
invalidate();
// invalidate();
// final RoundingParams roundingParams = RoundingParams.fromCornersRadius(5f);
//
RoundingParams roundingParams = getHierarchy().getRoundingParams();
if (roundingParams == null) {
roundingParams = RoundingParams.asCircle().setRoundingMethod(RoundingParams.RoundingMethod.BITMAP_ONLY);
}
roundingParams.setBorder(color, 5.0f);
getHierarchy().setRoundingParams(roundingParams);
}
}

View File

@ -0,0 +1,71 @@
package awais.instagrabber.customviews;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
public class PrimaryActionModeCallback implements ActionMode.Callback {
private ActionMode mode;
private int menuRes;
private final Callbacks callbacks;
public PrimaryActionModeCallback(final int menuRes, final Callbacks callbacks) {
this.menuRes = menuRes;
this.callbacks = callbacks;
}
@Override
public boolean onCreateActionMode(final ActionMode mode, final Menu menu) {
this.mode = mode;
mode.getMenuInflater().inflate(menuRes, menu);
if (callbacks != null) {
callbacks.onCreate(mode, menu);
}
return true;
}
@Override
public boolean onPrepareActionMode(final ActionMode mode, final Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
if (callbacks != null) {
return callbacks.onActionItemClicked(mode, item);
}
return false;
}
@Override
public void onDestroyActionMode(final ActionMode mode) {
if (callbacks != null) {
callbacks.onDestroy(mode);
}
this.mode = null;
}
public abstract static class CallbacksHelper implements Callbacks {
public void onCreate(final ActionMode mode, final Menu menu) {
}
@Override
public void onDestroy(final ActionMode mode) {
}
@Override
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
return false;
}
}
public interface Callbacks {
void onCreate(final ActionMode mode, final Menu menu);
void onDestroy(final ActionMode mode);
boolean onActionItemClicked(final ActionMode mode, final MenuItem item);
}
}

View File

@ -3,6 +3,7 @@ package awais.instagrabber.customviews;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.RectF;
import android.os.Handler;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
@ -14,6 +15,7 @@ import android.text.style.ClickableSpan;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.widget.TextView;
import androidx.annotation.NonNull;
@ -23,14 +25,23 @@ import androidx.appcompat.widget.AppCompatTextView;
import awais.instagrabber.R;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.TextUtils;
public final class RamboTextView extends AppCompatTextView {
private static final String TAG = "RamboTextView";
private static final int highlightBackgroundSpanKey = R.id.tvComment;
private static final RectF touchedLineBounds = new RectF();
private ClickableSpan clickableSpanUnderTouchOnActionDown;
private MentionClickListener mentionClickListener;
private boolean isUrlHighlighted, isExpandable, isExpanded;
private boolean isUrlHighlighted;
private boolean isExpandable;
private boolean isExpanded;
private OnLongClickListener longClickListener;
private final Handler handler = new Handler();
private final Runnable longPressRunnable = () -> {
if (longClickListener != null) longClickListener.onLongClick(this);
};
public RamboTextView(final Context context) {
super(context);
@ -56,6 +67,12 @@ public final class RamboTextView extends AppCompatTextView {
this.isExpanded = isExpanded;
}
@Override
public void setOnLongClickListener(@Nullable final OnLongClickListener l) {
if (l == null) return;
this.longClickListener = l;
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(final MotionEvent event) {
@ -71,33 +88,37 @@ public final class RamboTextView extends AppCompatTextView {
final boolean isURLSpan = clickableSpanUnderTouch instanceof URLSpan;
// feed view caption hacks
if (isExpandable && !touchStartedOverAClickableSpan)
return !isExpanded | super.onTouchEvent(event); // short operator, because we want two shits to work
// if (isExpandable && !touchStartedOverAClickableSpan)
// return !isExpanded | super.onTouchEvent(event); // short operator, because we want two shits to work
final Object tag = getTag();
final FeedModel feedModel = tag instanceof FeedModel ? (FeedModel) tag : null;
switch (action) {
case MotionEvent.ACTION_DOWN:
final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
handler.postDelayed(longPressRunnable, longPressTimeout);
if (feedModel != null) feedModel.setMentionClicked(false);
if (clickableSpanUnderTouch != null) highlightUrl(clickableSpanUnderTouch, spanText);
return isURLSpan ? super.onTouchEvent(event) : touchStartedOverAClickableSpan;
if (clickableSpanUnderTouch != null) {
highlightUrl(clickableSpanUnderTouch, spanText);
}
return super.onTouchEvent(event);
case MotionEvent.ACTION_UP:
handler.removeCallbacks(longPressRunnable);
if (touchStartedOverAClickableSpan && clickableSpanUnderTouch == clickableSpanUnderTouchOnActionDown) {
dispatchUrlClick(spanText, clickableSpanUnderTouch);
if (feedModel != null) feedModel.setMentionClicked(true);
}
cleanupOnTouchUp(spanText);
return isURLSpan ? super.onTouchEvent(event) : touchStartedOverAClickableSpan;
return super.onTouchEvent(event);
case MotionEvent.ACTION_MOVE:
// handler.removeCallbacks(longPressRunnable);
if (feedModel != null) feedModel.setMentionClicked(false);
if (clickableSpanUnderTouch != null) highlightUrl(clickableSpanUnderTouch, spanText);
else removeUrlHighlightColor(spanText);
return isURLSpan ? super.onTouchEvent(event) : touchStartedOverAClickableSpan;
return super.onTouchEvent(event);
case MotionEvent.ACTION_CANCEL:
handler.removeCallbacks(longPressRunnable);
if (feedModel != null) feedModel.setMentionClicked(false);
cleanupOnTouchUp(spanText);
return super.onTouchEvent(event);
@ -117,11 +138,11 @@ public final class RamboTextView extends AppCompatTextView {
CharSequence subSequence = s.subSequence(start, s.getSpanEnd(clickableSpan));
// for feed ellipsize
final int indexOfEllipsize = Utils.indexOfChar(subSequence, '…', 0);
final int indexOfEllipsize = TextUtils.indexOfChar(subSequence, '…', 0);
if (indexOfEllipsize != -1)
subSequence = subSequence.subSequence(0, indexOfEllipsize - 1);
mentionClickListener.onClick(this, subSequence.toString(), ishHashtag);
mentionClickListener.onClick(this, subSequence.toString(), ishHashtag, false);
}
}
@ -155,7 +176,9 @@ public final class RamboTextView extends AppCompatTextView {
}
@Nullable
private static ClickableSpan findClickableSpanUnderTouch(@NonNull final TextView textView, final Spannable text, @NonNull final MotionEvent event) {
private static ClickableSpan findClickableSpanUnderTouch(@NonNull final TextView textView,
final Spanned text,
@NonNull final MotionEvent event) {
final int touchX = (int) (event.getX() - textView.getTotalPaddingLeft() + textView.getScrollX());
final int touchY = (int) (event.getY() - textView.getTotalPaddingTop() + textView.getScrollY());
@ -176,4 +199,8 @@ public final class RamboTextView extends AppCompatTextView {
return null;
}
public boolean isCaptionExpanded() {
return isExpanded;
}
}

View File

@ -0,0 +1,170 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package awais.instagrabber.customviews.drawee;
import android.graphics.Matrix;
import android.graphics.PointF;
import androidx.annotation.Nullable;
import com.facebook.common.logging.FLog;
/**
* Abstract class for ZoomableController that adds animation capabilities to
* DefaultZoomableController.
*/
public abstract class AbstractAnimatedZoomableController extends DefaultZoomableController {
private boolean mIsAnimating;
private final float[] mStartValues = new float[9];
private final float[] mStopValues = new float[9];
private final float[] mCurrentValues = new float[9];
private final Matrix mNewTransform = new Matrix();
private final Matrix mWorkingTransform = new Matrix();
public AbstractAnimatedZoomableController(TransformGestureDetector transformGestureDetector) {
super(transformGestureDetector);
}
@Override
public void reset() {
FLog.v(getLogTag(), "reset");
stopAnimation();
mWorkingTransform.reset();
mNewTransform.reset();
super.reset();
}
/**
* Returns true if the zoomable transform is identity matrix, and the controller is idle.
*/
@Override
public boolean isIdentity() {
return !isAnimating() && super.isIdentity();
}
/**
* Zooms to the desired scale and positions the image so that the given image point corresponds to
* the given view point.
*
* <p>If this method is called while an animation or gesture is already in progress, the current
* animation or gesture will be stopped first.
*
* @param scale desired scale, will be limited to {min, max} scale factor
* @param imagePoint 2D point in image's relative coordinate system (i.e. 0 <= x, y <= 1)
* @param viewPoint 2D point in view's absolute coordinate system
*/
@Override
public void zoomToPoint(float scale, PointF imagePoint, PointF viewPoint) {
zoomToPoint(scale, imagePoint, viewPoint, LIMIT_ALL, 0, null);
}
/**
* Zooms to the desired scale and positions the image so that the given image point corresponds to
* the given view point.
*
* <p>If this method is called while an animation or gesture is already in progress, the current
* animation or gesture will be stopped first.
*
* @param scale desired scale, will be limited to {min, max} scale factor
* @param imagePoint 2D point in image's relative coordinate system (i.e. 0 <= x, y <= 1)
* @param viewPoint 2D point in view's absolute coordinate system
* @param limitFlags whether to limit translation and/or scale.
* @param durationMs length of animation of the zoom, or 0 if no animation desired
* @param onAnimationComplete code to run when the animation completes. Ignored if durationMs=0
*/
public void zoomToPoint(
float scale,
PointF imagePoint,
PointF viewPoint,
@LimitFlag int limitFlags,
long durationMs,
@Nullable Runnable onAnimationComplete) {
FLog.v(getLogTag(), "zoomToPoint: duration %d ms", durationMs);
calculateZoomToPointTransform(mNewTransform, scale, imagePoint, viewPoint, limitFlags);
setTransform(mNewTransform, durationMs, onAnimationComplete);
}
/**
* Sets a new zoomable transformation and animates to it if desired.
*
* <p>If this method is called while an animation or gesture is already in progress, the current
* animation or gesture will be stopped first.
*
* @param newTransform new transform to make active
* @param durationMs duration of the animation, or 0 to not animate
* @param onAnimationComplete code to run when the animation completes. Ignored if durationMs=0
*/
public void setTransform(
Matrix newTransform, long durationMs, @Nullable Runnable onAnimationComplete) {
FLog.v(getLogTag(), "setTransform: duration %d ms", durationMs);
if (durationMs <= 0) {
setTransformImmediate(newTransform);
} else {
setTransformAnimated(newTransform, durationMs, onAnimationComplete);
}
}
private void setTransformImmediate(final Matrix newTransform) {
FLog.v(getLogTag(), "setTransformImmediate");
stopAnimation();
mWorkingTransform.set(newTransform);
super.setTransform(newTransform);
getDetector().restartGesture();
}
protected boolean isAnimating() {
return mIsAnimating;
}
protected void setAnimating(boolean isAnimating) {
mIsAnimating = isAnimating;
}
protected float[] getStartValues() {
return mStartValues;
}
protected float[] getStopValues() {
return mStopValues;
}
protected Matrix getWorkingTransform() {
return mWorkingTransform;
}
@Override
public void onGestureBegin(TransformGestureDetector detector) {
FLog.v(getLogTag(), "onGestureBegin");
stopAnimation();
super.onGestureBegin(detector);
}
@Override
public void onGestureUpdate(TransformGestureDetector detector) {
FLog.v(getLogTag(), "onGestureUpdate %s", isAnimating() ? "(ignored)" : "");
if (isAnimating()) {
return;
}
super.onGestureUpdate(detector);
}
protected void calculateInterpolation(Matrix outMatrix, float fraction) {
for (int i = 0; i < 9; i++) {
mCurrentValues[i] = (1 - fraction) * mStartValues[i] + fraction * mStopValues[i];
}
outMatrix.setValues(mCurrentValues);
}
public abstract void setTransformAnimated(
final Matrix newTransform, long durationMs, @Nullable final Runnable onAnimationComplete);
protected abstract void stopAnimation();
protected abstract Class<?> getLogTag();
}

View File

@ -0,0 +1,105 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package awais.instagrabber.customviews.drawee;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.graphics.Matrix;
import android.view.animation.DecelerateInterpolator;
import androidx.annotation.Nullable;
import com.facebook.common.internal.Preconditions;
import com.facebook.common.logging.FLog;
/**
* ZoomableController that adds animation capabilities to DefaultZoomableController using standard
* Android animation classes
*/
public class AnimatedZoomableController extends AbstractAnimatedZoomableController {
private static final Class<?> TAG = AnimatedZoomableController.class;
private final ValueAnimator mValueAnimator;
public static AnimatedZoomableController newInstance() {
return new AnimatedZoomableController(TransformGestureDetector.newInstance());
}
@SuppressLint("NewApi")
public AnimatedZoomableController(TransformGestureDetector transformGestureDetector) {
super(transformGestureDetector);
mValueAnimator = ValueAnimator.ofFloat(0, 1);
mValueAnimator.setInterpolator(new DecelerateInterpolator());
}
@SuppressLint("NewApi")
@Override
public void setTransformAnimated(
final Matrix newTransform, long durationMs, @Nullable final Runnable onAnimationComplete) {
FLog.v(getLogTag(), "setTransformAnimated: duration %d ms", durationMs);
stopAnimation();
Preconditions.checkArgument(durationMs > 0);
Preconditions.checkState(!isAnimating());
setAnimating(true);
mValueAnimator.setDuration(durationMs);
getTransform().getValues(getStartValues());
newTransform.getValues(getStopValues());
mValueAnimator.addUpdateListener(
new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
calculateInterpolation(getWorkingTransform(), (float) valueAnimator.getAnimatedValue());
AnimatedZoomableController.super.setTransform(getWorkingTransform());
}
});
mValueAnimator.addListener(
new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
FLog.v(getLogTag(), "setTransformAnimated: animation cancelled");
onAnimationStopped();
}
@Override
public void onAnimationEnd(Animator animation) {
FLog.v(getLogTag(), "setTransformAnimated: animation finished");
onAnimationStopped();
}
private void onAnimationStopped() {
if (onAnimationComplete != null) {
onAnimationComplete.run();
}
setAnimating(false);
getDetector().restartGesture();
}
});
mValueAnimator.start();
}
@SuppressLint("NewApi")
@Override
public void stopAnimation() {
if (!isAnimating()) {
return;
}
FLog.v(getLogTag(), "stopAnimation");
mValueAnimator.cancel();
mValueAnimator.removeAllUpdateListeners();
mValueAnimator.removeAllListeners();
}
@Override
protected Class<?> getLogTag() {
return TAG;
}
}

View File

@ -0,0 +1,720 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package awais.instagrabber.customviews.drawee;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.RectF;
import android.view.MotionEvent;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.facebook.common.logging.FLog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Zoomable controller that calculates transformation based on touch events.
*/
public class DefaultZoomableController
implements ZoomableController, TransformGestureDetector.Listener {
/**
* Interface for handling call backs when the image bounds are set.
*/
public interface ImageBoundsListener {
void onImageBoundsSet(RectF imageBounds);
}
@IntDef(
flag = true,
value = {LIMIT_NONE, LIMIT_TRANSLATION_X, LIMIT_TRANSLATION_Y, LIMIT_SCALE, LIMIT_ALL})
@Retention(RetentionPolicy.SOURCE)
public @interface LimitFlag {}
public static final int LIMIT_NONE = 0;
public static final int LIMIT_TRANSLATION_X = 1;
public static final int LIMIT_TRANSLATION_Y = 2;
public static final int LIMIT_SCALE = 4;
public static final int LIMIT_ALL = LIMIT_TRANSLATION_X | LIMIT_TRANSLATION_Y | LIMIT_SCALE;
private static final float EPS = 1e-3f;
private static final Class<?> TAG = DefaultZoomableController.class;
private static final RectF IDENTITY_RECT = new RectF(0, 0, 1, 1);
private TransformGestureDetector mGestureDetector;
private @Nullable
ImageBoundsListener mImageBoundsListener;
private @Nullable
Listener mListener = null;
private boolean mIsEnabled = false;
private boolean mIsRotationEnabled = false;
private boolean mIsScaleEnabled = true;
private boolean mIsTranslationEnabled = true;
private boolean mIsGestureZoomEnabled = true;
private float mMinScaleFactor = 1.0f;
private float mMaxScaleFactor = 2.0f;
// View bounds, in view-absolute coordinates
private final RectF mViewBounds = new RectF();
// Non-transformed image bounds, in view-absolute coordinates
private final RectF mImageBounds = new RectF();
// Transformed image bounds, in view-absolute coordinates
private final RectF mTransformedImageBounds = new RectF();
private final Matrix mPreviousTransform = new Matrix();
private final Matrix mActiveTransform = new Matrix();
private final Matrix mActiveTransformInverse = new Matrix();
private final float[] mTempValues = new float[9];
private final RectF mTempRect = new RectF();
private boolean mWasTransformCorrected;
public static DefaultZoomableController newInstance() {
return new DefaultZoomableController(TransformGestureDetector.newInstance());
}
public DefaultZoomableController(TransformGestureDetector gestureDetector) {
mGestureDetector = gestureDetector;
mGestureDetector.setListener(this);
}
/**
* Rests the controller.
*/
public void reset() {
FLog.v(TAG, "reset");
mGestureDetector.reset();
mPreviousTransform.reset();
mActiveTransform.reset();
onTransformChanged();
}
/**
* Sets the zoomable listener.
*/
@Override
public void setListener(Listener listener) {
mListener = listener;
}
/**
* Sets whether the controller is enabled or not.
*/
@Override
public void setEnabled(boolean enabled) {
mIsEnabled = enabled;
if (!enabled) {
reset();
}
}
/**
* Gets whether the controller is enabled or not.
*/
@Override
public boolean isEnabled() {
return mIsEnabled;
}
/**
* Sets whether the rotation gesture is enabled or not.
*/
public void setRotationEnabled(boolean enabled) {
mIsRotationEnabled = enabled;
}
/**
* Gets whether the rotation gesture is enabled or not.
*/
public boolean isRotationEnabled() {
return mIsRotationEnabled;
}
/**
* Sets whether the scale gesture is enabled or not.
*/
public void setScaleEnabled(boolean enabled) {
mIsScaleEnabled = enabled;
}
/**
* Gets whether the scale gesture is enabled or not.
*/
public boolean isScaleEnabled() {
return mIsScaleEnabled;
}
/**
* Sets whether the translation gesture is enabled or not.
*/
public void setTranslationEnabled(boolean enabled) {
mIsTranslationEnabled = enabled;
}
/**
* Gets whether the translations gesture is enabled or not.
*/
public boolean isTranslationEnabled() {
return mIsTranslationEnabled;
}
/**
* Sets the minimum scale factor allowed.
*
* <p>Hierarchy's scaling (if any) is not taken into account.
*/
public void setMinScaleFactor(float minScaleFactor) {
mMinScaleFactor = minScaleFactor;
}
/**
* Gets the minimum scale factor allowed.
*/
public float getMinScaleFactor() {
return mMinScaleFactor;
}
/**
* Sets the maximum scale factor allowed.
*
* <p>Hierarchy's scaling (if any) is not taken into account.
*/
public void setMaxScaleFactor(float maxScaleFactor) {
mMaxScaleFactor = maxScaleFactor;
}
/**
* Gets the maximum scale factor allowed.
*/
public float getMaxScaleFactor() {
return mMaxScaleFactor;
}
/**
* Sets whether gesture zooms are enabled or not.
*/
public void setGestureZoomEnabled(boolean isGestureZoomEnabled) {
mIsGestureZoomEnabled = isGestureZoomEnabled;
}
/**
* Gets whether gesture zooms are enabled or not.
*/
public boolean isGestureZoomEnabled() {
return mIsGestureZoomEnabled;
}
/**
* Gets the current scale factor.
*/
@Override
public float getScaleFactor() {
return getMatrixScaleFactor(mActiveTransform);
}
/**
* Sets the image bounds, in view-absolute coordinates.
*/
@Override
public void setImageBounds(RectF imageBounds) {
if (!imageBounds.equals(mImageBounds)) {
mImageBounds.set(imageBounds);
onTransformChanged();
if (mImageBoundsListener != null) {
mImageBoundsListener.onImageBoundsSet(mImageBounds);
}
}
}
/**
* Gets the non-transformed image bounds, in view-absolute coordinates.
*/
public RectF getImageBounds() {
return mImageBounds;
}
/**
* Gets the transformed image bounds, in view-absolute coordinates
*/
private RectF getTransformedImageBounds() {
return mTransformedImageBounds;
}
/**
* Sets the view bounds.
*/
@Override
public void setViewBounds(RectF viewBounds) {
mViewBounds.set(viewBounds);
}
/**
* Gets the view bounds.
*/
public RectF getViewBounds() {
return mViewBounds;
}
/**
* Sets the image bounds listener.
*/
public void setImageBoundsListener(@Nullable ImageBoundsListener imageBoundsListener) {
mImageBoundsListener = imageBoundsListener;
}
/**
* Gets the image bounds listener.
*/
public @Nullable
ImageBoundsListener getImageBoundsListener() {
return mImageBoundsListener;
}
/**
* Returns true if the zoomable transform is identity matrix.
*/
@Override
public boolean isIdentity() {
return isMatrixIdentity(mActiveTransform, 1e-3f);
}
/**
* Returns true if the transform was corrected during the last update.
*
* <p>We should rename this method to `wasTransformedWithoutCorrection` and just return the
* internal flag directly. However, this requires interface change and negation of meaning.
*/
@Override
public boolean wasTransformCorrected() {
return mWasTransformCorrected;
}
/**
* Gets the matrix that transforms image-absolute coordinates to view-absolute coordinates. The
* zoomable transformation is taken into account.
*
* <p>Internal matrix is exposed for performance reasons and is not to be modified by the callers.
*/
@Override
public Matrix getTransform() {
return mActiveTransform;
}
/**
* Gets the matrix that transforms image-relative coordinates to view-absolute coordinates. The
* zoomable transformation is taken into account.
*/
public void getImageRelativeToViewAbsoluteTransform(Matrix outMatrix) {
outMatrix.setRectToRect(IDENTITY_RECT, mTransformedImageBounds, Matrix.ScaleToFit.FILL);
}
/**
* Maps point from view-absolute to image-relative coordinates. This takes into account the
* zoomable transformation.
*/
public PointF mapViewToImage(PointF viewPoint) {
float[] points = mTempValues;
points[0] = viewPoint.x;
points[1] = viewPoint.y;
mActiveTransform.invert(mActiveTransformInverse);
mActiveTransformInverse.mapPoints(points, 0, points, 0, 1);
mapAbsoluteToRelative(points, points, 1);
return new PointF(points[0], points[1]);
}
/**
* Maps point from image-relative to view-absolute coordinates. This takes into account the
* zoomable transformation.
*/
public PointF mapImageToView(PointF imagePoint) {
float[] points = mTempValues;
points[0] = imagePoint.x;
points[1] = imagePoint.y;
mapRelativeToAbsolute(points, points, 1);
mActiveTransform.mapPoints(points, 0, points, 0, 1);
return new PointF(points[0], points[1]);
}
/**
* Maps array of 2D points from view-absolute to image-relative coordinates. This does NOT take
* into account the zoomable transformation. Points are represented by a float array of [x0, y0,
* x1, y1, ...].
*
* @param destPoints destination array (may be the same as source array)
* @param srcPoints source array
* @param numPoints number of points to map
*/
private void mapAbsoluteToRelative(float[] destPoints, float[] srcPoints, int numPoints) {
for (int i = 0; i < numPoints; i++) {
destPoints[i * 2 + 0] = (srcPoints[i * 2 + 0] - mImageBounds.left) / mImageBounds.width();
destPoints[i * 2 + 1] = (srcPoints[i * 2 + 1] - mImageBounds.top) / mImageBounds.height();
}
}
/**
* Maps array of 2D points from image-relative to view-absolute coordinates. This does NOT take
* into account the zoomable transformation. Points are represented by float array of [x0, y0, x1,
* y1, ...].
*
* @param destPoints destination array (may be the same as source array)
* @param srcPoints source array
* @param numPoints number of points to map
*/
private void mapRelativeToAbsolute(float[] destPoints, float[] srcPoints, int numPoints) {
for (int i = 0; i < numPoints; i++) {
destPoints[i * 2 + 0] = srcPoints[i * 2 + 0] * mImageBounds.width() + mImageBounds.left;
destPoints[i * 2 + 1] = srcPoints[i * 2 + 1] * mImageBounds.height() + mImageBounds.top;
}
}
/**
* Zooms to the desired scale and positions the image so that the given image point corresponds to
* the given view point.
*
* @param scale desired scale, will be limited to {min, max} scale factor
* @param imagePoint 2D point in image's relative coordinate system (i.e. 0 <= x, y <= 1)
* @param viewPoint 2D point in view's absolute coordinate system
*/
public void zoomToPoint(float scale, PointF imagePoint, PointF viewPoint) {
FLog.v(TAG, "zoomToPoint");
calculateZoomToPointTransform(mActiveTransform, scale, imagePoint, viewPoint, LIMIT_ALL);
onTransformChanged();
}
/**
* Calculates the zoom transformation that would zoom to the desired scale and position the image
* so that the given image point corresponds to the given view point.
*
* @param outTransform the matrix to store the result to
* @param scale desired scale, will be limited to {min, max} scale factor
* @param imagePoint 2D point in image's relative coordinate system (i.e. 0 <= x, y <= 1)
* @param viewPoint 2D point in view's absolute coordinate system
* @param limitFlags whether to limit translation and/or scale.
* @return whether or not the transform has been corrected due to limitation
*/
protected boolean calculateZoomToPointTransform(
Matrix outTransform,
float scale,
PointF imagePoint,
PointF viewPoint,
@LimitFlag int limitFlags) {
float[] viewAbsolute = mTempValues;
viewAbsolute[0] = imagePoint.x;
viewAbsolute[1] = imagePoint.y;
mapRelativeToAbsolute(viewAbsolute, viewAbsolute, 1);
float distanceX = viewPoint.x - viewAbsolute[0];
float distanceY = viewPoint.y - viewAbsolute[1];
boolean transformCorrected = false;
outTransform.setScale(scale, scale, viewAbsolute[0], viewAbsolute[1]);
transformCorrected |= limitScale(outTransform, viewAbsolute[0], viewAbsolute[1], limitFlags);
outTransform.postTranslate(distanceX, distanceY);
transformCorrected |= limitTranslation(outTransform, limitFlags);
return transformCorrected;
}
/**
* Sets a new zoom transformation.
*/
public void setTransform(Matrix newTransform) {
FLog.v(TAG, "setTransform");
mActiveTransform.set(newTransform);
onTransformChanged();
}
/**
* Gets the gesture detector.
*/
protected TransformGestureDetector getDetector() {
return mGestureDetector;
}
/**
* Notifies controller of the received touch event.
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
FLog.v(TAG, "onTouchEvent: action: ", event.getAction());
if (mIsEnabled && mIsGestureZoomEnabled) {
return mGestureDetector.onTouchEvent(event);
}
return false;
}
/* TransformGestureDetector.Listener methods */
@Override
public void onGestureBegin(TransformGestureDetector detector) {
FLog.v(TAG, "onGestureBegin");
mPreviousTransform.set(mActiveTransform);
onTransformBegin();
// We only received a touch down event so far, and so we don't know yet in which direction a
// future move event will follow. Therefore, if we can't scroll in all directions, we have to
// assume the worst case where the user tries to scroll out of edge, which would cause
// transformation to be corrected.
mWasTransformCorrected = !canScrollInAllDirection();
}
@Override
public void onGestureUpdate(TransformGestureDetector detector) {
FLog.v(TAG, "onGestureUpdate");
boolean transformCorrected = calculateGestureTransform(mActiveTransform, LIMIT_ALL);
onTransformChanged();
if (transformCorrected) {
mGestureDetector.restartGesture();
}
// A transformation happened, but was it without correction?
mWasTransformCorrected = transformCorrected;
}
@Override
public void onGestureEnd(TransformGestureDetector detector) {
FLog.v(TAG, "onGestureEnd");
onTransformEnd();
}
/**
* Calculates the zoom transformation based on the current gesture.
*
* @param outTransform the matrix to store the result to
* @param limitTypes whether to limit translation and/or scale.
* @return whether or not the transform has been corrected due to limitation
*/
protected boolean calculateGestureTransform(Matrix outTransform, @LimitFlag int limitTypes) {
TransformGestureDetector detector = mGestureDetector;
boolean transformCorrected = false;
outTransform.set(mPreviousTransform);
if (mIsRotationEnabled) {
float angle = detector.getRotation() * (float) (180 / Math.PI);
outTransform.postRotate(angle, detector.getPivotX(), detector.getPivotY());
}
if (mIsScaleEnabled) {
float scale = detector.getScale();
outTransform.postScale(scale, scale, detector.getPivotX(), detector.getPivotY());
}
transformCorrected |=
limitScale(outTransform, detector.getPivotX(), detector.getPivotY(), limitTypes);
if (mIsTranslationEnabled) {
outTransform.postTranslate(detector.getTranslationX(), detector.getTranslationY());
}
transformCorrected |= limitTranslation(outTransform, limitTypes);
return transformCorrected;
}
private void onTransformBegin() {
if (mListener != null && isEnabled()) {
mListener.onTransformBegin(mActiveTransform);
}
}
private void onTransformChanged() {
mActiveTransform.mapRect(mTransformedImageBounds, mImageBounds);
if (mListener != null && isEnabled()) {
mListener.onTransformChanged(mActiveTransform);
}
}
private void onTransformEnd() {
if (mListener != null && isEnabled()) {
mListener.onTransformEnd(mActiveTransform);
}
}
/**
* Keeps the scaling factor within the specified limits.
*
* @param pivotX x coordinate of the pivot point
* @param pivotY y coordinate of the pivot point
* @param limitTypes whether to limit scale.
* @return whether limiting has been applied or not
*/
private boolean limitScale(
Matrix transform, float pivotX, float pivotY, @LimitFlag int limitTypes) {
if (!shouldLimit(limitTypes, LIMIT_SCALE)) {
return false;
}
float currentScale = getMatrixScaleFactor(transform);
float targetScale = limit(currentScale, mMinScaleFactor, mMaxScaleFactor);
if (targetScale != currentScale) {
float scale = targetScale / currentScale;
transform.postScale(scale, scale, pivotX, pivotY);
return true;
}
return false;
}
/**
* Limits the translation so that there are no empty spaces on the sides if possible.
*
* <p>The image is attempted to be centered within the view bounds if the transformed image is
* smaller. There will be no empty spaces within the view bounds if the transformed image is
* bigger. This applies to each dimension (horizontal and vertical) independently.
*
* @param limitTypes whether to limit translation along the specific axis.
* @return whether limiting has been applied or not
*/
private boolean limitTranslation(Matrix transform, @LimitFlag int limitTypes) {
if (!shouldLimit(limitTypes, LIMIT_TRANSLATION_X | LIMIT_TRANSLATION_Y)) {
return false;
}
RectF b = mTempRect;
b.set(mImageBounds);
transform.mapRect(b);
float offsetLeft =
shouldLimit(limitTypes, LIMIT_TRANSLATION_X)
? getOffset(
b.left, b.right, mViewBounds.left, mViewBounds.right, mImageBounds.centerX())
: 0;
float offsetTop =
shouldLimit(limitTypes, LIMIT_TRANSLATION_Y)
? getOffset(
b.top, b.bottom, mViewBounds.top, mViewBounds.bottom, mImageBounds.centerY())
: 0;
if (offsetLeft != 0 || offsetTop != 0) {
transform.postTranslate(offsetLeft, offsetTop);
return true;
}
return false;
}
/**
* Checks whether the specified limit flag is present in the limits provided.
*
* <p>If the flag contains multiple flags together using a bitwise OR, this only checks that at
* least one of the flags is included.
*
* @param limits the limits to apply
* @param flag the limit flag(s) to check for
* @return true if the flag (or one of the flags) is included in the limits
*/
private static boolean shouldLimit(@LimitFlag int limits, @LimitFlag int flag) {
return (limits & flag) != LIMIT_NONE;
}
/**
* Returns the offset necessary to make sure that: - the image is centered within the limit if the
* image is smaller than the limit - there is no empty space on left/right if the image is bigger
* than the limit
*/
private float getOffset(
float imageStart, float imageEnd, float limitStart, float limitEnd, float limitCenter) {
float imageWidth = imageEnd - imageStart, limitWidth = limitEnd - limitStart;
float limitInnerWidth = Math.min(limitCenter - limitStart, limitEnd - limitCenter) * 2;
// center if smaller than limitInnerWidth
if (imageWidth < limitInnerWidth) {
return limitCenter - (imageEnd + imageStart) / 2;
}
// to the edge if in between and limitCenter is not (limitLeft + limitRight) / 2
if (imageWidth < limitWidth) {
if (limitCenter < (limitStart + limitEnd) / 2) {
return limitStart - imageStart;
} else {
return limitEnd - imageEnd;
}
}
// to the edge if larger than limitWidth and empty space visible
if (imageStart > limitStart) {
return limitStart - imageStart;
}
if (imageEnd < limitEnd) {
return limitEnd - imageEnd;
}
return 0;
}
/**
* Limits the value to the given min and max range.
*/
private float limit(float value, float min, float max) {
return Math.min(Math.max(min, value), max);
}
/**
* Gets the scale factor for the given matrix. This method assumes the equal scaling factor for X
* and Y axis.
*/
private float getMatrixScaleFactor(Matrix transform) {
transform.getValues(mTempValues);
return mTempValues[Matrix.MSCALE_X];
}
/**
* Same as {@code Matrix.isIdentity()}, but with tolerance {@code eps}.
*/
private boolean isMatrixIdentity(Matrix transform, float eps) {
// Checks whether the given matrix is close enough to the identity matrix:
// 1 0 0
// 0 1 0
// 0 0 1
// Or equivalently to the zero matrix, after subtracting 1.0f from the diagonal elements:
// 0 0 0
// 0 0 0
// 0 0 0
transform.getValues(mTempValues);
mTempValues[0] -= 1.0f; // m00
mTempValues[4] -= 1.0f; // m11
mTempValues[8] -= 1.0f; // m22
for (int i = 0; i < 9; i++) {
if (Math.abs(mTempValues[i]) > eps) {
return false;
}
}
return true;
}
/**
* Returns whether the scroll can happen in all directions. I.e. the image is not on any edge.
*/
private boolean canScrollInAllDirection() {
return mTransformedImageBounds.left < mViewBounds.left - EPS
&& mTransformedImageBounds.top < mViewBounds.top - EPS
&& mTransformedImageBounds.right > mViewBounds.right + EPS
&& mTransformedImageBounds.bottom > mViewBounds.bottom + EPS;
}
@Override
public int computeHorizontalScrollRange() {
return (int) mTransformedImageBounds.width();
}
@Override
public int computeHorizontalScrollOffset() {
return (int) (mViewBounds.left - mTransformedImageBounds.left);
}
@Override
public int computeHorizontalScrollExtent() {
return (int) mViewBounds.width();
}
@Override
public int computeVerticalScrollRange() {
return (int) mTransformedImageBounds.height();
}
@Override
public int computeVerticalScrollOffset() {
return (int) (mViewBounds.top - mTransformedImageBounds.top);
}
@Override
public int computeVerticalScrollExtent() {
return (int) mViewBounds.height();
}
public Listener getListener() {
return mListener;
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package awais.instagrabber.customviews.drawee;
import android.graphics.PointF;
import android.view.GestureDetector;
import android.view.MotionEvent;
/**
* Tap gesture listener for double tap to zoom / unzoom and double-tap-and-drag to zoom.
*
* @see ZoomableDraweeView#setTapListener(GestureDetector.SimpleOnGestureListener)
*/
public class DoubleTapGestureListener extends GestureDetector.SimpleOnGestureListener {
private static final int DURATION_MS = 300;
private static final int DOUBLE_TAP_SCROLL_THRESHOLD = 20;
private final ZoomableDraweeView mDraweeView;
private final PointF mDoubleTapViewPoint = new PointF();
private final PointF mDoubleTapImagePoint = new PointF();
private float mDoubleTapScale = 1;
private boolean mDoubleTapScroll = false;
public DoubleTapGestureListener(ZoomableDraweeView zoomableDraweeView) {
mDraweeView = zoomableDraweeView;
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
AbstractAnimatedZoomableController zc =
(AbstractAnimatedZoomableController) mDraweeView.getZoomableController();
PointF vp = new PointF(e.getX(), e.getY());
PointF ip = zc.mapViewToImage(vp);
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mDoubleTapViewPoint.set(vp);
mDoubleTapImagePoint.set(ip);
mDoubleTapScale = zc.getScaleFactor();
break;
case MotionEvent.ACTION_MOVE:
mDoubleTapScroll = mDoubleTapScroll || shouldStartDoubleTapScroll(vp);
if (mDoubleTapScroll) {
float scale = calcScale(vp);
zc.zoomToPoint(scale, mDoubleTapImagePoint, mDoubleTapViewPoint);
}
break;
case MotionEvent.ACTION_UP:
if (mDoubleTapScroll) {
float scale = calcScale(vp);
zc.zoomToPoint(scale, mDoubleTapImagePoint, mDoubleTapViewPoint);
} else {
final float maxScale = zc.getMaxScaleFactor();
final float minScale = zc.getMinScaleFactor();
if (zc.getScaleFactor() < (maxScale + minScale) / 2) {
zc.zoomToPoint(
maxScale, ip, vp, DefaultZoomableController.LIMIT_ALL, DURATION_MS, null);
} else {
zc.zoomToPoint(
minScale, ip, vp, DefaultZoomableController.LIMIT_ALL, DURATION_MS, null);
}
}
mDoubleTapScroll = false;
break;
}
return true;
}
private boolean shouldStartDoubleTapScroll(PointF viewPoint) {
double dist =
Math.hypot(viewPoint.x - mDoubleTapViewPoint.x, viewPoint.y - mDoubleTapViewPoint.y);
return dist > DOUBLE_TAP_SCROLL_THRESHOLD;
}
private float calcScale(PointF currentViewPoint) {
float dy = (currentViewPoint.y - mDoubleTapViewPoint.y);
float t = 1 + Math.abs(dy) * 0.001f;
return (dy < 0) ? mDoubleTapScale / t : mDoubleTapScale * t;
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package awais.instagrabber.customviews.drawee;
import android.view.GestureDetector;
import android.view.MotionEvent;
/**
* Wrapper for SimpleOnGestureListener as GestureDetector does not allow changing its listener.
*/
public class GestureListenerWrapper extends GestureDetector.SimpleOnGestureListener {
private GestureDetector.SimpleOnGestureListener mDelegate;
public GestureListenerWrapper() {
mDelegate = new GestureDetector.SimpleOnGestureListener();
}
public void setListener(GestureDetector.SimpleOnGestureListener listener) {
mDelegate = listener;
}
@Override
public void onLongPress(MotionEvent e) {
mDelegate.onLongPress(e);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return mDelegate.onScroll(e1, e2, distanceX, distanceY);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return mDelegate.onFling(e1, e2, velocityX, velocityY);
}
@Override
public void onShowPress(MotionEvent e) {
mDelegate.onShowPress(e);
}
@Override
public boolean onDown(MotionEvent e) {
return mDelegate.onDown(e);
}
@Override
public boolean onDoubleTap(MotionEvent e) {
return mDelegate.onDoubleTap(e);
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
return mDelegate.onDoubleTapEvent(e);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return mDelegate.onSingleTapConfirmed(e);
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return mDelegate.onSingleTapUp(e);
}
}

Some files were not shown because too many files have changed in this diff Show More