diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 00000000..61a9130c
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index ac6b0aec..23a89bbb 100755
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -15,6 +15,7 @@
+
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index 247f71d9..6a48fecb 100755
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -31,5 +31,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3ea378e6..6e6363fe 100755
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -40,7 +40,7 @@
-
+
diff --git a/.idea/runConfigurations/app.xml b/.idea/runConfigurations/app.xml
index fe1ee5d0..6276ac27 100755
--- a/.idea/runConfigurations/app.xml
+++ b/.idea/runConfigurations/app.xml
@@ -1,11 +1,12 @@
-
+
+
@@ -41,11 +42,16 @@
+
+
+
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 3fa4bc3f..26ef9d1c 100755
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -10,8 +10,8 @@ android {
minSdkVersion 21
targetSdkVersion 29
- versionCode 51
- versionName '19.0-a2'
+ versionCode 52
+ versionName '19.0'
multiDexEnabled true
@@ -20,6 +20,9 @@ android {
}
compileOptions {
+ // Flag to enable support for the new language APIs
+ coreLibraryDesugaringEnabled true
+
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
@@ -36,32 +39,51 @@ android {
}
}
-dependencies {
- 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.3.0-alpha02'
- implementation 'com.google.android.exoplayer:exoplayer:2.11.1'
+configurations.all {
+ resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+}
+
+dependencies {
+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
+
+ def appcompat_version = "1.2.0"
+ def nav_version = '2.3.1'
+
+ implementation 'com.google.android.material:material:1.3.0-alpha03'
+
+ implementation 'com.google.android.exoplayer:exoplayer-core:2.12.0'
+ implementation 'com.google.android.exoplayer:exoplayer-ui:2.12.0'
implementation "androidx.appcompat:appcompat:$appcompat_version"
implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
- implementation "androidx.recyclerview:recyclerview:1.2.0-alpha05"
+ implementation "androidx.recyclerview:recyclerview:1.2.0-alpha06"
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"
+ implementation "androidx.constraintlayout:constraintlayout:2.0.4"
+ implementation "androidx.preference:preference:1.1.1"
+ implementation "androidx.work:work-runtime:2.4.0"
+ implementation 'androidx.palette:palette:1.0.0'
-// implementation 'com.github.hendrawd:StorageUtil:1.1.0'
+ implementation 'com.google.guava:guava:27.0.1-android'
+
+ // implementation 'com.github.hendrawd:StorageUtil:1.1.0'
+ implementation 'com.github.ammargitham:AutoLinkTextViewV2:master-SNAPSHOT'
implementation 'org.jsoup:jsoup:1.13.1'
implementation 'com.facebook.fresco:fresco:2.3.0'
+ implementation 'com.facebook.fresco:animated-gif: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'
+ implementation 'com.github.dragon66:icafe:master-SNAPSHOT'
+ implementation 'javax.media:jai_imageio:1.1.1'
+
+ debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5'
+
+ testImplementation 'org.junit.jupiter:junit-jupiter:5.7.0'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d0847f2f..3d33cda0 100755
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -71,22 +71,7 @@
android:taskAffinity="awais.instagrabber.errorreport"
android:theme="@android:style/Theme.DeviceDefault.Dialog" />
-
+ android:theme="@style/Theme.AppCompat.Translucent">
@@ -138,6 +123,12 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java b/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java
index 827db5ea..5f87ed0e 100644
--- a/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java
+++ b/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java
@@ -32,12 +32,16 @@ public final class InstaGrabberApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
+ // final Set requestListeners = new HashSet<>();
+ // requestListeners.add(new RequestLoggingListener());
final ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig
.newBuilder(this)
// .setMainDiskCacheConfig(diskCacheConfig)
+ // .setRequestListeners(requestListeners)
.setDownsampleEnabled(true)
.build();
Fresco.initialize(this, imagePipelineConfig);
+ // FLog.setMinimumLoggingLevel(FLog.VERBOSE);
if (BuildConfig.DEBUG) {
try {
diff --git a/app/src/main/java/awais/instagrabber/activities/DirectDownload.java b/app/src/main/java/awais/instagrabber/activities/DirectDownload.java
new file mode 100644
index 00000000..4d675b3e
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/activities/DirectDownload.java
@@ -0,0 +1,149 @@
+package awais.instagrabber.activities;
+
+import android.Manifest;
+import android.app.Notification;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.app.NotificationCompat;
+import androidx.core.app.NotificationManagerCompat;
+import androidx.core.content.ContextCompat;
+
+import awais.instagrabber.R;
+import awais.instagrabber.asyncs.PostFetcher;
+import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.models.FeedModel;
+import awais.instagrabber.models.IntentModel;
+import awais.instagrabber.models.enums.IntentModelType;
+import awais.instagrabber.utils.Constants;
+import awais.instagrabber.utils.DownloadUtils;
+import awais.instagrabber.utils.IntentUtils;
+import awais.instagrabber.utils.TextUtils;
+
+public final class DirectDownload extends AppCompatActivity {
+ private static final int NOTIFICATION_ID = 1900000000;
+ private static final int STORAGE_PERM_REQUEST_CODE = 8020;
+
+ private boolean contextFound = false;
+ private Intent intent;
+ private Context context;
+ private NotificationManagerCompat notificationManager;
+
+ @Override
+ protected void onCreate(@Nullable final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_direct);
+ }
+
+ @Override
+ public void onWindowAttributesChanged(final WindowManager.LayoutParams params) {
+ super.onWindowAttributesChanged(params);
+ if (!contextFound) {
+ intent = getIntent();
+ context = getApplicationContext();
+ if (intent != null && context != null) {
+ contextFound = true;
+ checkPermissions();
+ }
+ }
+ }
+
+ @Override
+ public Resources getResources() {
+ if (!contextFound) {
+ intent = getIntent();
+ context = getApplicationContext();
+ if (intent != null && context != null) {
+ contextFound = true;
+ checkPermissions();
+ }
+ }
+ return super.getResources();
+ }
+
+ private synchronized void checkPermissions() {
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
+ doDownload();
+ return;
+ }
+ ActivityCompat.requestPermissions(this, DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
+ final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
+ if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
+ doDownload();
+ }
+ }
+
+ private synchronized void doDownload() {
+ notificationManager = NotificationManagerCompat.from(getApplicationContext());
+ final Intent intent = getIntent();
+ final String action = intent.getAction();
+ if (TextUtils.isEmpty(action) || Intent.ACTION_MAIN.equals(action)) {
+ finish();
+ return;
+ }
+ boolean error = true;
+
+ String data = null;
+ final Bundle extras = intent.getExtras();
+ if (extras != null) {
+ final Object extraData = extras.get(Intent.EXTRA_TEXT);
+ if (extraData != null) {
+ error = false;
+ data = extraData.toString();
+ }
+ }
+ if (error) {
+ final Uri intentData = intent.getData();
+ if (intentData != null) data = intentData.toString();
+ }
+ if (data == null || TextUtils.isEmpty(data)) {
+ finish();
+ return;
+ }
+ final IntentModel model = IntentUtils.parseUrl(data);
+ if (model == null || model.getType() != IntentModelType.POST) {
+ finish();
+ return;
+ }
+ final String text = model.getText();
+ new PostFetcher(text, new FetchListener() {
+ @Override
+ public void doBefore() {
+ if (notificationManager == null) return;
+ final Notification fetchingPostNotification = new NotificationCompat.Builder(getApplicationContext(), Constants.DOWNLOAD_CHANNEL_ID)
+ .setCategory(NotificationCompat.CATEGORY_STATUS)
+ .setSmallIcon(R.drawable.ic_download)
+ .setAutoCancel(false)
+ .setPriority(NotificationCompat.PRIORITY_MIN)
+ .setContentText(getString(R.string.direct_download_loading))
+ .build();
+ notificationManager.notify(NOTIFICATION_ID, fetchingPostNotification);
+ }
+
+ @Override
+ public void onResult(final FeedModel result) {
+ if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
+ if (result == null) {
+ finish();
+ return;
+ }
+ DownloadUtils.download(getApplicationContext(), result);
+ finish();
+ }
+ }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/activities/Login.java b/app/src/main/java/awais/instagrabber/activities/Login.java
index a7f06b67..37083794 100755
--- a/app/src/main/java/awais/instagrabber/activities/Login.java
+++ b/app/src/main/java/awais/instagrabber/activities/Login.java
@@ -5,6 +5,7 @@ import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
+import android.view.LayoutInflater;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
@@ -59,7 +60,7 @@ public final class Login extends BaseLanguageActivity implements View.OnClickLis
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- loginBinding = ActivityLoginBinding.inflate(getLayoutInflater());
+ loginBinding = ActivityLoginBinding.inflate(LayoutInflater.from(getApplicationContext()));
setContentView(loginBinding.getRoot());
initWebView();
@@ -117,12 +118,9 @@ public final class Login extends BaseLanguageActivity implements View.OnClickLis
webSettings.setDisplayZoomControls(false);
webSettings.setLoadWithOverviewMode(true);
webSettings.setUseWideViewPort(true);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- webSettings.setAllowFileAccessFromFileURLs(true);
- webSettings.setAllowUniversalAccessFromFileURLs(true);
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
- webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
+ webSettings.setAllowFileAccessFromFileURLs(true);
+ webSettings.setAllowUniversalAccessFromFileURLs(true);
+ webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
diff --git a/app/src/main/java/awais/instagrabber/activities/MainActivity.java b/app/src/main/java/awais/instagrabber/activities/MainActivity.java
index 79439bdc..cb188e8c 100644
--- a/app/src/main/java/awais/instagrabber/activities/MainActivity.java
+++ b/app/src/main/java/awais/instagrabber/activities/MainActivity.java
@@ -24,10 +24,12 @@ import android.widget.AutoCompleteTextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.app.NotificationManagerCompat;
+import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LiveData;
import androidx.navigation.NavBackStackEntry;
@@ -39,10 +41,10 @@ import androidx.navigation.ui.NavigationUI;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.bottomnavigation.BottomNavigationView;
+import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
@@ -50,9 +52,12 @@ import java.util.Map;
import awais.instagrabber.R;
import awais.instagrabber.adapters.SuggestionsAdapter;
+import awais.instagrabber.asyncs.PostFetcher;
import awais.instagrabber.asyncs.SuggestionsFetcher;
import awais.instagrabber.customviews.helpers.CustomHideBottomViewOnScrollBehavior;
import awais.instagrabber.databinding.ActivityMainBinding;
+import awais.instagrabber.fragments.PostViewV2Fragment;
+import awais.instagrabber.fragments.main.FeedFragment;
import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.IntentModel;
@@ -94,9 +99,12 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
R.id.notificationsViewer,
R.id.themePreferencesFragment,
R.id.favoritesFragment,
- R.id.backupPreferencesFragment);
+ R.id.backupPreferencesFragment,
+ R.id.directMessagesThreadFragment
+ );
private static final Map NAV_TO_MENU_ID_MAP = new HashMap<>();
- private static final List REMOVE_COLLAPSING_TOOLBAR_SCROLL_DESTINATIONS = Collections.singletonList(R.id.commentsViewerFragment);
+ private static final List REMOVE_COLLAPSING_TOOLBAR_SCROLL_DESTINATIONS = ImmutableList.of(R.id.commentsViewerFragment,
+ R.id.directMessagesThreadFragment);
private static final String FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex";
private ActivityMainBinding binding;
@@ -157,10 +165,6 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
bindActivityCheckerService();
}
getSupportFragmentManager().addOnBackStackChangedListener(this);
-
- // Log.d("austin_debug", "dir: "+Arrays.toString(StorageUtil.getStorageDirectories(getApplicationContext())));
- // final File sdcard = new File(StorageUtil.getStorageDirectories(getApplicationContext())[0]);
- // Log.d("austin_debug", "files: "+Arrays.toString(sdcard.listFiles()));
}
@Override
@@ -222,7 +226,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
@Override
public void onBackPressed() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && isTaskRoot() && isBackStackEmpty) {
+ if (isTaskRoot() && isBackStackEmpty) {
finishAfterTransition();
} else {
super.onBackPressed();
@@ -403,11 +407,14 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
}
final List mainNavList = getMainNavList(main_nav_ids);
if (setDefaultFromSettings) {
- final String defaultTabIdString = settingsHelper.getString(Constants.DEFAULT_TAB);
+ final String defaultTabResNameString = settingsHelper.getString(Constants.DEFAULT_TAB);
try {
- final int defaultNavId = TextUtils.isEmpty(defaultTabIdString)
- ? R.navigation.profile_nav_graph
- : Integer.parseInt(defaultTabIdString);
+ int navId = 0;
+ if (!TextUtils.isEmpty(defaultTabResNameString)) {
+ navId = getResources().getIdentifier(defaultTabResNameString, "navigation", getPackageName());
+ }
+ final int defaultNavId = navId <= 0 ? R.navigation.profile_nav_graph
+ : navId;
final int index = mainNavList.indexOf(defaultNavId);
if (index >= 0) firstFragmentGraphIndex = index;
setBottomNavSelectedItem(defaultNavId);
@@ -422,8 +429,18 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
R.id.main_nav_host,
getIntent(),
firstFragmentGraphIndex);
- navControllerLiveData.observe(this, this::setupNavigation);
+ navControllerLiveData.observe(this, navController -> setupNavigation(binding.toolbar, navController));
currentNavControllerLiveData = navControllerLiveData;
+ binding.bottomNavView.setOnNavigationItemReselectedListener(item -> {
+ // Log.d(TAG, "setupBottomNavigationBar: item: " + item);
+ final Fragment navHostFragment = getSupportFragmentManager().findFragmentById(R.id.main_nav_host);
+ if (navHostFragment != null) {
+ final Fragment fragment = navHostFragment.getChildFragmentManager().getPrimaryNavigationFragment();
+ if (fragment instanceof FeedFragment) {
+ ((FeedFragment) fragment).scrollToTop();
+ }
+ }
+ });
}
private void setBottomNavSelectedItem(final int navId) {
@@ -447,8 +464,11 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
return mainNavList;
}
- private void setupNavigation(final NavController navController) {
- NavigationUI.setupWithNavController(binding.toolbar, navController);
+ private void setupNavigation(final Toolbar toolbar, final NavController navController) {
+ if (navController == null) {
+ return;
+ }
+ NavigationUI.setupWithNavController(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);
@@ -572,13 +592,18 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
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);
+ final AlertDialog alertDialog = new AlertDialog.Builder(this)
+ .setCancelable(false)
+ .setView(R.layout.dialog_opening_post)
+ .create();
+ alertDialog.show();
+ new PostFetcher(shortCode, feedModel -> {
+ final PostViewV2Fragment fragment = PostViewV2Fragment
+ .builder(feedModel)
+ .build();
+ fragment.setOnShowListener(dialog -> alertDialog.dismiss());
+ fragment.show(getSupportFragmentManager(), "post_view");
+ }).execute();
}
private void showLocationView(@NonNull final IntentModel intentModel) {
@@ -626,4 +651,31 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
public BottomNavigationView getBottomNavView() {
return binding.bottomNavView;
}
+
+ // public void fitSystemWindows(final boolean fit) {
+ // binding.appBarLayout.setBackground(null);
+ // binding.appBarLayout.setFitsSystemWindows(fit);
+ // binding.collapsingToolbarLayout.setBackground(null);
+ // binding.collapsingToolbarLayout.setFitsSystemWindows(fit);
+ // final Drawable toolbarBackground = binding.toolbar.getBackground();
+ // binding.toolbar.setFitsSystemWindows(fit);
+ // binding.toolbar.setBackground(null);
+ // binding.toolbar.setClickable(false);
+ // }
+ //
+ // public int getNavHostContainerId() {
+ // return binding.mainNavHost.getId();
+ // }
+
+ public void setToolbar(final Toolbar toolbar) {
+ binding.appBarLayout.setVisibility(View.GONE);
+ setSupportActionBar(toolbar);
+ setupNavigation(toolbar, currentNavControllerLiveData.getValue());
+ }
+
+ public void resetToolbar() {
+ binding.appBarLayout.setVisibility(View.VISIBLE);
+ setSupportActionBar(binding.toolbar);
+ setupNavigation(binding.toolbar, currentNavControllerLiveData.getValue());
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java
index 6a00ff33..e0ccb44e 100755
--- a/app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java
+++ b/app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java
@@ -2,137 +2,191 @@ package awais.instagrabber.adapters;
import android.content.Context;
import android.view.LayoutInflater;
-import android.view.View;
import android.view.ViewGroup;
-import android.widget.Filter;
-import android.widget.Filterable;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.DiffUtil;
+import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
-import awais.instagrabber.R;
-import awais.instagrabber.adapters.viewholder.CommentViewHolder;
-import awais.instagrabber.interfaces.MentionClickListener;
+import awais.instagrabber.adapters.viewholder.comments.ChildCommentViewHolder;
+import awais.instagrabber.adapters.viewholder.comments.ParentCommentViewHolder;
+import awais.instagrabber.databinding.ItemCommentBinding;
+import awais.instagrabber.databinding.ItemCommentSmallBinding;
import awais.instagrabber.models.CommentModel;
-import awais.instagrabber.models.ProfileModel;
-import awais.instagrabber.utils.LocaleUtils;
-import awais.instagrabber.utils.TextUtils;
-public final class CommentsAdapter extends RecyclerView.Adapter implements Filterable {
+public final class CommentsAdapter extends ListAdapter {
+ private static final int TYPE_PARENT = 1;
+ private static final int TYPE_CHILD = 2;
- private CommentModel[] filteredCommentModels;
- private LayoutInflater layoutInflater;
+ private final Map positionTypeMap = new HashMap<>();
- 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
+ // private final Filter filter = new Filter() {
+ // @NonNull
+ // @Override
+ // protected FilterResults performFiltering(final CharSequence filter) {
+ // final FilterResults results = new FilterResults();
+ // results.values = commentModels;
+ //
+ // final int commentsLen = commentModels == null ? 0 : commentModels.size();
+ // if (commentModels != null && commentsLen > 0 && !TextUtils.isEmpty(filter)) {
+ // final String query = filter.toString().toLowerCase();
+ // final ArrayList filterList = new ArrayList<>(commentsLen);
+ //
+ // for (final CommentModel commentModel : commentModels) {
+ // final String commentText = commentModel.getText().toString().toLowerCase();
+ //
+ // if (commentText.contains(query)) filterList.add(commentModel);
+ // else {
+ // final List childCommentModels = commentModel.getChildCommentModels();
+ // if (childCommentModels != null) {
+ // for (final CommentModel childCommentModel : childCommentModels) {
+ // final String childCommentText = childCommentModel.getText().toString().toLowerCase();
+ // if (childCommentText.contains(query)) filterList.add(commentModel);
+ // }
+ // }
+ // }
+ // }
+ // filterList.trimToSize();
+ // results.values = filterList.toArray(new CommentModel[0]);
+ // }
+ //
+ // return results;
+ // }
+ //
+ // @Override
+ // protected void publishResults(final CharSequence constraint, @NonNull final FilterResults results) {
+ // if (results.values instanceof List) {
+ // //noinspection unchecked
+ // filteredCommentModels = (List) results.values;
+ // notifyDataSetChanged();
+ // }
+ // }
+ // };
+
+ private static final DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback() {
@Override
- protected FilterResults performFiltering(final CharSequence filter) {
- final FilterResults results = new FilterResults();
- results.values = commentModels;
-
- final int commentsLen = commentModels == null ? 0 : commentModels.length;
- if (commentModels != null && commentsLen > 0 && !TextUtils.isEmpty(filter)) {
- final String query = filter.toString().toLowerCase();
- final ArrayList filterList = new ArrayList<>(commentsLen);
-
- for (final CommentModel commentModel : commentModels) {
- final String commentText = commentModel.getText().toString().toLowerCase();
-
- if (commentText.contains(query)) filterList.add(commentModel);
- else {
- final CommentModel[] childCommentModels = commentModel.getChildCommentModels();
- if (childCommentModels != null) {
- for (final CommentModel childCommentModel : childCommentModels) {
- final String childCommentText = childCommentModel.getText().toString().toLowerCase();
- if (childCommentText.contains(query)) filterList.add(commentModel);
- }
- }
- }
- }
- filterList.trimToSize();
- results.values = filterList.toArray(new CommentModel[0]);
- }
-
- return results;
+ public boolean areItemsTheSame(@NonNull final CommentModel oldItem, @NonNull final CommentModel newItem) {
+ return oldItem.getId().equals(newItem.getId());
}
@Override
- protected void publishResults(final CharSequence constraint, @NonNull final FilterResults results) {
- if (results.values instanceof CommentModel[]) {
- filteredCommentModels = (CommentModel[]) results.values;
- notifyDataSetChanged();
- }
+ public boolean areContentsTheSame(@NonNull final CommentModel oldItem, @NonNull final CommentModel newItem) {
+ return oldItem.getId().equals(newItem.getId());
}
};
+ private final CommentCallback commentCallback;
+ private CommentModel selected;
+ private int selectedIndex;
- 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;
- this.mentionClickListener = mentionClickListener;
- }
-
- @Override
- public Filter getFilter() {
- return filter;
+ public CommentsAdapter(final CommentCallback commentCallback) {
+ super(DIFF_CALLBACK);
+ this.commentCallback = commentCallback;
}
@NonNull
@Override
- public CommentViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
+ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
final Context context = parent.getContext();
- 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);
- final View view = layoutInflater.inflate(isParent ? R.layout.item_comment
- : R.layout.item_comment_small,
- parent,
- false);
- return new CommentViewHolder(view,
- onClickListener,
- mentionClickListener);
+ final LayoutInflater layoutInflater = LayoutInflater.from(context);
+ if (type == TYPE_PARENT) {
+ final ItemCommentBinding binding = ItemCommentBinding.inflate(layoutInflater, parent, false);
+ return new ParentCommentViewHolder(binding);
+ }
+ final ItemCommentSmallBinding binding = ItemCommentSmallBinding.inflate(layoutInflater, parent, false);
+ return new ChildCommentViewHolder(binding);
}
@Override
- public void onBindViewHolder(@NonNull final CommentViewHolder holder, final int position) {
- final CommentModel commentModel = filteredCommentModels[position];
- if (commentModel != null) {
- holder.setCommentModel(commentModel);
+ public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
+ final CommentModel commentModel = getItem(position);
+ if (commentModel == null) return;
+ final int type = getItemViewType(position);
+ final boolean selected = this.selected != null && this.selected.getId().equals(commentModel.getId());
+ if (type == TYPE_PARENT) {
+ final ParentCommentViewHolder viewHolder = (ParentCommentViewHolder) holder;
+ viewHolder.bind(commentModel, selected, commentCallback);
+ return;
+ }
+ final ChildCommentViewHolder viewHolder = (ChildCommentViewHolder) holder;
+ viewHolder.bind(commentModel, selected, commentCallback);
+ }
- holder.setComment(commentModel.getText());
- holder.setDate(commentModel.getDateTime());
- holder.setLiked(commentModel.getLiked());
+ @Override
+ public void submitList(@Nullable final List list) {
+ final List flatList = flattenList(list);
+ super.submitList(flatList);
+ }
- final long likes = commentModel.getLikes();
- holder.setLikes(String.format(LocaleUtils.getCurrentLocale(), "%d %s", likes, quantityStrings[likes == 1 ? 0 : 1]));
+ @Override
+ public void submitList(@Nullable final List list, @Nullable final Runnable commitCallback) {
+ final List flatList = flattenList(list);
+ super.submitList(flatList, commitCallback);
+ }
- final ProfileModel profileModel = commentModel.getProfileModel();
- if (profileModel != null) {
- holder.setUsername(profileModel.getUsername());
- holder.getProfilePicView().setImageURI(profileModel.getSdProfilePic());
- }
- if (holder.isParent()) {
- final CommentModel[] childCommentModels = commentModel.getChildCommentModels();
- if (childCommentModels != null && childCommentModels.length > 0)
- holder.setChildAdapter(new CommentsAdapter(childCommentModels, false, onClickListener, mentionClickListener));
- else holder.hideChildComments();
+ private List flattenList(final List list) {
+ if (list == null) {
+ return Collections.emptyList();
+ }
+ final List flatList = new ArrayList<>();
+ int lastCommentIndex = -1;
+ for (final CommentModel parent : list) {
+ lastCommentIndex++;
+ flatList.add(parent);
+ positionTypeMap.put(lastCommentIndex, TYPE_PARENT);
+ final List children = parent.getChildCommentModels();
+ if (children != null) {
+ for (final CommentModel child : children) {
+ lastCommentIndex++;
+ flatList.add(child);
+ positionTypeMap.put(lastCommentIndex, TYPE_CHILD);
+ }
}
}
+ return flatList;
}
+
@Override
- public int getItemCount() {
- return filteredCommentModels == null ? 0 : filteredCommentModels.length;
+ public int getItemViewType(final int position) {
+ final Integer type = positionTypeMap.get(position);
+ if (type == null) {
+ return TYPE_PARENT;
+ }
+ return type;
+ }
+
+ public void setSelected(final CommentModel commentModel) {
+ this.selected = commentModel;
+ selectedIndex = getCurrentList().indexOf(commentModel);
+ notifyItemChanged(selectedIndex);
+ }
+
+ public void clearSelection() {
+ this.selected = null;
+ notifyItemChanged(selectedIndex);
+ }
+
+ public CommentModel getSelected() {
+ return selected;
+ }
+
+ public interface CommentCallback {
+ void onClick(final CommentModel comment);
+
+ void onHashtagClick(final String hashtag);
+
+ void onMentionClick(final String mention);
+
+ void onURLClick(final String url);
+
+ void onEmailClick(final String emailAddress);
}
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/DirectMessageItemsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/DirectMessageItemsAdapter.java
index 547f3cce..a344a238 100644
--- a/app/src/main/java/awais/instagrabber/adapters/DirectMessageItemsAdapter.java
+++ b/app/src/main/java/awais/instagrabber/adapters/DirectMessageItemsAdapter.java
@@ -107,6 +107,7 @@ public final class DirectMessageItemsAdapter extends ListAdapter {
-
- private static final DiffUtil.ItemCallback diffCallback = new DiffUtil.ItemCallback() {
- @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 clickListener,
- final OnItemLongClickListener longClickListener) {
- super(diffCallback, clickListener, longClickListener);
- }
-
- @NonNull
- @Override
- public DiscoverViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
- 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 = getItem(position);
- if (itemModel != null) {
- itemModel.setPosition(position);
- holder.itemView.setTag(itemModel);
- 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.ic_slider_24 : R.drawable.ic_video_24);
- holder.selectedView.setVisibility(itemModel.isSelected() ? View.VISIBLE : View.GONE);
- holder.postImage.setImageURI(itemModel.getDisplayUrl());
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/DiscoverTopicsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/DiscoverTopicsAdapter.java
new file mode 100644
index 00000000..fa173042
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/adapters/DiscoverTopicsAdapter.java
@@ -0,0 +1,53 @@
+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 awais.instagrabber.adapters.viewholder.TopicClusterViewHolder;
+import awais.instagrabber.databinding.ItemDiscoverTopicBinding;
+import awais.instagrabber.models.TopicCluster;
+
+public class DiscoverTopicsAdapter extends ListAdapter {
+ private static final DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback() {
+ @Override
+ public boolean areItemsTheSame(@NonNull final TopicCluster oldItem, @NonNull final TopicCluster newItem) {
+ return oldItem.getId().equals(newItem.getId());
+ }
+
+ @Override
+ public boolean areContentsTheSame(@NonNull final TopicCluster oldItem, @NonNull final TopicCluster newItem) {
+ return oldItem.getCoverMedia().getDisplayUrl().equals(newItem.getCoverMedia().getDisplayUrl())
+ && oldItem.getTitle().equals(newItem.getTitle());
+ }
+ };
+
+ private final OnTopicClickListener onTopicClickListener;
+
+ public DiscoverTopicsAdapter(final OnTopicClickListener onTopicClickListener) {
+ super(DIFF_CALLBACK);
+ this.onTopicClickListener = onTopicClickListener;
+ }
+
+ @NonNull
+ @Override
+ public TopicClusterViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
+ final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
+ final ItemDiscoverTopicBinding binding = ItemDiscoverTopicBinding.inflate(layoutInflater, parent, false);
+ return new TopicClusterViewHolder(binding, onTopicClickListener);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull final TopicClusterViewHolder holder, final int position) {
+ final TopicCluster topicCluster = getItem(position);
+ holder.bind(topicCluster);
+ }
+
+ public interface OnTopicClickListener {
+ void onTopicClick(TopicCluster topicCluster, View root, View cover, View title, int titleColor, int backgroundColor);
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java b/app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java
index 09623f3f..6ffc88e0 100755
--- a/app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java
+++ b/app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java
@@ -1,111 +1,111 @@
-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.DiffUtil;
-import androidx.recyclerview.widget.ListAdapter;
-
-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.FeedModel;
-import awais.instagrabber.models.enums.MediaItemType;
-import awais.instagrabber.utils.Utils;
-
-public final class FeedAdapter extends ListAdapter {
- private static final String TAG = "FeedAdapter";
- private final View.OnClickListener clickListener;
- private final MentionClickListener mentionClickListener;
- 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;
- };
-
- private static final DiffUtil.ItemCallback diffCallback = new DiffUtil.ItemCallback() {
- @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 View.OnClickListener clickListener,
- final MentionClickListener mentionClickListener) {
- super(diffCallback);
- // private final static String ellipsize = "… more";
- this.clickListener = clickListener;
- this.mentionClickListener = mentionClickListener;
- }
-
- @NonNull
- @Override
- public FeedItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
- 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);
- }
- case MEDIA_TYPE_SLIDER: {
- final ItemFeedSliderBinding binding = ItemFeedSliderBinding.inflate(layoutInflater, parent, false);
- return new FeedSliderViewHolder(binding, mentionClickListener, clickListener, longClickListener);
- }
- case MEDIA_TYPE_IMAGE:
- default: {
- final ItemFeedPhotoBinding binding = ItemFeedPhotoBinding.inflate(layoutInflater, parent, false);
- return new FeedPhotoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
- }
- }
- }
-
- @Override
- 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) {
- return getItem(position).getItemType().getId();
- }
-
- @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();
- }
-
- @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();
- }
-}
\ No newline at end of file
+// 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.DiffUtil;
+// import androidx.recyclerview.widget.ListAdapter;
+//
+// 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.FeedModel;
+// import awais.instagrabber.models.enums.MediaItemType;
+// import awais.instagrabber.utils.Utils;
+//
+// public final class FeedAdapter extends ListAdapter {
+// private static final String TAG = "FeedAdapter";
+// private final View.OnClickListener clickListener;
+// private final MentionClickListener mentionClickListener;
+// 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;
+// };
+//
+// private static final DiffUtil.ItemCallback diffCallback = new DiffUtil.ItemCallback() {
+// @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 View.OnClickListener clickListener,
+// final MentionClickListener mentionClickListener) {
+// super(diffCallback);
+// // private final static String ellipsize = "… more";
+// this.clickListener = clickListener;
+// this.mentionClickListener = mentionClickListener;
+// }
+//
+// @NonNull
+// @Override
+// public FeedItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
+// 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);
+// }
+// case MEDIA_TYPE_SLIDER: {
+// final ItemFeedSliderBinding binding = ItemFeedSliderBinding.inflate(layoutInflater, parent, false);
+// return new FeedSliderViewHolder(binding, mentionClickListener, clickListener, longClickListener);
+// }
+// case MEDIA_TYPE_IMAGE:
+// default: {
+// final ItemFeedPhotoBinding binding = ItemFeedPhotoBinding.inflate(layoutInflater, parent, false);
+// return new FeedPhotoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
+// }
+// }
+// }
+//
+// @Override
+// 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, (feedModel1, view, postImage) -> {});
+// }
+//
+// @Override
+// public int getItemViewType(final int position) {
+// return getItem(position).getItemType().getId();
+// }
+//
+// @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();
+// }
+//
+// @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();
+// }
+// }
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java b/app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java
new file mode 100644
index 00000000..f8e70c04
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java
@@ -0,0 +1,241 @@
+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.DiffUtil;
+import androidx.recyclerview.widget.ListAdapter;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import awais.instagrabber.adapters.viewholder.FeedGridItemViewHolder;
+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.databinding.ItemFeedGridBinding;
+import awais.instagrabber.databinding.ItemFeedPhotoBinding;
+import awais.instagrabber.databinding.ItemFeedSliderBinding;
+import awais.instagrabber.databinding.ItemFeedVideoBinding;
+import awais.instagrabber.models.FeedModel;
+import awais.instagrabber.models.PostsLayoutPreferences;
+import awais.instagrabber.models.enums.MediaItemType;
+
+public final class FeedAdapterV2 extends ListAdapter {
+ private static final String TAG = "FeedAdapterV2";
+
+ private final FeedItemCallback feedItemCallback;
+ private final SelectionModeCallback selectionModeCallback;
+ private final Set selectedPositions = new HashSet<>();
+ private final Set selectedFeedModels = new HashSet<>();
+
+ private PostsLayoutPreferences layoutPreferences;
+ private boolean selectionModeActive = false;
+
+
+ private static final DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback() {
+ @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());
+ }
+ };
+ private final AdapterSelectionCallback adapterSelectionCallback = new AdapterSelectionCallback() {
+ @Override
+ public boolean onPostLongClick(final int position, final FeedModel feedModel) {
+ if (!selectionModeActive) {
+ selectionModeActive = true;
+ notifyDataSetChanged();
+ if (selectionModeCallback != null) {
+ selectionModeCallback.onSelectionStart();
+ }
+ }
+ selectedPositions.add(position);
+ selectedFeedModels.add(feedModel);
+ notifyItemChanged(position);
+ if (selectionModeCallback != null) {
+ selectionModeCallback.onSelectionChange(selectedFeedModels);
+ }
+ return true;
+ }
+
+ @Override
+ public void onPostClick(final int position, final FeedModel feedModel) {
+ if (!selectionModeActive) return;
+ if (selectedPositions.contains(position)) {
+ selectedPositions.remove(position);
+ selectedFeedModels.remove(feedModel);
+ } else {
+ selectedPositions.add(position);
+ selectedFeedModels.add(feedModel);
+ }
+ notifyItemChanged(position);
+ if (selectionModeCallback != null) {
+ selectionModeCallback.onSelectionChange(selectedFeedModels);
+ }
+ if (selectedPositions.isEmpty()) {
+ selectionModeActive = false;
+ notifyDataSetChanged();
+ if (selectionModeCallback != null) {
+ selectionModeCallback.onSelectionEnd();
+ }
+ }
+ }
+ };
+
+ public FeedAdapterV2(@NonNull final PostsLayoutPreferences layoutPreferences,
+ final FeedItemCallback feedItemCallback,
+ final SelectionModeCallback selectionModeCallback) {
+ super(DIFF_CALLBACK);
+ this.layoutPreferences = layoutPreferences;
+ this.feedItemCallback = feedItemCallback;
+ this.selectionModeCallback = selectionModeCallback;
+ }
+
+ @NonNull
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
+ final Context context = parent.getContext();
+ final LayoutInflater layoutInflater = LayoutInflater.from(context);
+ switch (layoutPreferences.getType()) {
+ case LINEAR:
+ return getLinearViewHolder(parent, layoutInflater, viewType);
+ case GRID:
+ case STAGGERED_GRID:
+ default:
+ final ItemFeedGridBinding binding = ItemFeedGridBinding.inflate(layoutInflater, parent, false);
+ return new FeedGridItemViewHolder(binding);
+ }
+ }
+
+ @NonNull
+ private RecyclerView.ViewHolder getLinearViewHolder(@NonNull final ViewGroup parent,
+ final LayoutInflater layoutInflater,
+ final int viewType) {
+ switch (MediaItemType.valueOf(viewType)) {
+ case MEDIA_TYPE_VIDEO: {
+ final ItemFeedVideoBinding binding = ItemFeedVideoBinding.inflate(layoutInflater, parent, false);
+ return new FeedVideoViewHolder(binding, feedItemCallback);
+ }
+ case MEDIA_TYPE_SLIDER: {
+ final ItemFeedSliderBinding binding = ItemFeedSliderBinding.inflate(layoutInflater, parent, false);
+ return new FeedSliderViewHolder(binding, feedItemCallback);
+ }
+ case MEDIA_TYPE_IMAGE:
+ default: {
+ final ItemFeedPhotoBinding binding = ItemFeedPhotoBinding.inflate(layoutInflater, parent, false);
+ return new FeedPhotoViewHolder(binding, feedItemCallback);
+ }
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, final int position) {
+ final FeedModel feedModel = getItem(position);
+ if (feedModel == null) return;
+ switch (layoutPreferences.getType()) {
+ case LINEAR:
+ ((FeedItemViewHolder) viewHolder).bind(feedModel);
+ break;
+ case GRID:
+ case STAGGERED_GRID:
+ default:
+ ((FeedGridItemViewHolder) viewHolder).bind(position,
+ feedModel,
+ layoutPreferences,
+ feedItemCallback,
+ adapterSelectionCallback,
+ selectionModeActive,
+ selectedPositions.contains(position));
+ }
+ }
+
+ @Override
+ public int getItemViewType(final int position) {
+ return getItem(position).getItemType().getId();
+ }
+
+ public void setLayoutPreferences(@NonNull final PostsLayoutPreferences layoutPreferences) {
+ this.layoutPreferences = layoutPreferences;
+ }
+
+ public void endSelection() {
+ if (!selectionModeActive) return;
+ selectionModeActive = false;
+ selectedPositions.clear();
+ selectedFeedModels.clear();
+ notifyDataSetChanged();
+ if (selectionModeCallback != null) {
+ selectionModeCallback.onSelectionEnd();
+ }
+ }
+
+ // @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();
+ // }
+ //
+ // @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();
+ // }
+
+ public interface FeedItemCallback {
+ void onPostClick(final FeedModel feedModel,
+ final View profilePicView,
+ final View mainPostImage);
+
+ void onProfilePicClick(final FeedModel feedModel,
+ final View profilePicView);
+
+ void onNameClick(final FeedModel feedModel,
+ final View profilePicView);
+
+ void onLocationClick(final FeedModel feedModel);
+
+ void onMentionClick(final String mention);
+
+ void onHashtagClick(final String hashtag);
+
+ void onCommentsClick(final FeedModel feedModel);
+
+ void onDownloadClick(final FeedModel feedModel, final int childPosition);
+
+ void onEmailClick(final String emailId);
+
+ void onURLClick(final String url);
+
+ void onSliderClick(FeedModel feedModel, int position);
+ }
+
+ public interface AdapterSelectionCallback {
+ boolean onPostLongClick(final int position, FeedModel feedModel);
+
+ void onPostClick(final int position, FeedModel feedModel);
+ }
+
+ public interface SelectionModeCallback {
+ void onSelectionStart();
+
+ void onSelectionChange(final Set selectedFeedModels);
+
+ void onSelectionEnd();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/FeedItemCallbackAdapter.java b/app/src/main/java/awais/instagrabber/adapters/FeedItemCallbackAdapter.java
new file mode 100644
index 00000000..67ac9cb9
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/adapters/FeedItemCallbackAdapter.java
@@ -0,0 +1,40 @@
+package awais.instagrabber.adapters;
+
+import android.view.View;
+
+import awais.instagrabber.models.FeedModel;
+
+public class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback {
+ @Override
+ public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {}
+
+ @Override
+ public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {}
+
+ @Override
+ public void onNameClick(final FeedModel feedModel, final View profilePicView) {}
+
+ @Override
+ public void onLocationClick(final FeedModel feedModel) {}
+
+ @Override
+ public void onMentionClick(final String mention) {}
+
+ @Override
+ public void onHashtagClick(final String hashtag) {}
+
+ @Override
+ public void onCommentsClick(final FeedModel feedModel) {}
+
+ @Override
+ public void onDownloadClick(final FeedModel feedModel, final int childPosition) {}
+
+ @Override
+ public void onEmailClick(final String emailId) {}
+
+ @Override
+ public void onURLClick(final String url) {}
+
+ @Override
+ public void onSliderClick(final FeedModel feedModel, final int position) {}
+}
diff --git a/app/src/main/java/awais/instagrabber/adapters/MultiSelectListAdapter.java b/app/src/main/java/awais/instagrabber/adapters/MultiSelectListAdapter.java
index d196b2c9..9b8fe04b 100644
--- a/app/src/main/java/awais/instagrabber/adapters/MultiSelectListAdapter.java
+++ b/app/src/main/java/awais/instagrabber/adapters/MultiSelectListAdapter.java
@@ -12,8 +12,8 @@ public abstract class MultiSelectListAdapter {
private boolean isSelecting;
- private final OnItemClickListener internalOnItemClickListener;
- private final OnItemLongClickListener internalOnLongItemClickListener;
+ private OnItemClickListener internalOnItemClickListener;
+ private OnItemLongClickListener internalOnLongItemClickListener;
private final List selectedItems = new ArrayList<>();
diff --git a/app/src/main/java/awais/instagrabber/adapters/PostViewAdapter.java b/app/src/main/java/awais/instagrabber/adapters/PostViewAdapter.java
index 305f04cd..f49b8974 100644
--- a/app/src/main/java/awais/instagrabber/adapters/PostViewAdapter.java
+++ b/app/src/main/java/awais/instagrabber/adapters/PostViewAdapter.java
@@ -1,77 +1,75 @@
-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 {
- private final OnPostViewChildViewClickListener clickListener;
- private final OnPostCaptionLongClickListener longClickListener;
- private final MentionClickListener mentionClickListener;
-
- private static final DiffUtil.ItemCallback diffCallback = new DiffUtil.ItemCallback() {
- @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);
- }
-}
+// 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 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 {
+// private final OnPostViewChildViewClickListener clickListener;
+// private final OnPostCaptionLongClickListener longClickListener;
+// private final MentionClickListener mentionClickListener;
+//
+// private static final DiffUtil.ItemCallback diffCallback = new DiffUtil.ItemCallback() {
+// @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 oldItem.getViewerPostModels().equals(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);
+// }
+// }
diff --git a/app/src/main/java/awais/instagrabber/adapters/PostViewerChildAdapter.java b/app/src/main/java/awais/instagrabber/adapters/PostViewerChildAdapter.java
index ca3e59e0..d94f9ff3 100644
--- a/app/src/main/java/awais/instagrabber/adapters/PostViewerChildAdapter.java
+++ b/app/src/main/java/awais/instagrabber/adapters/PostViewerChildAdapter.java
@@ -15,6 +15,7 @@ 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.MediaItem;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
@@ -171,8 +172,9 @@ public class PostViewerChildAdapter extends ListAdapter {
+
+ private final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener;
+ private final boolean loadVideoOnItemClick;
+ private final SliderCallback sliderCallback;
+ private final awais.instagrabber.databinding.LayoutExoCustomControlsBinding controlsBinding;
+
+ private static final DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback() {
+ @Override
+ public boolean areItemsTheSame(@NonNull final PostChild oldItem, @NonNull final PostChild newItem) {
+ return oldItem.getPostId().equals(newItem.getPostId());
+ }
+
+ @Override
+ public boolean areContentsTheSame(@NonNull final PostChild oldItem, @NonNull final PostChild newItem) {
+ return oldItem.getPostId().equals(newItem.getPostId());
+ }
+ };
+
+ public SliderItemsAdapter(final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener,
+ final LayoutExoCustomControlsBinding controlsBinding,
+ final boolean loadVideoOnItemClick,
+ final SliderCallback sliderCallback) {
+ super(DIFF_CALLBACK);
+ this.onVerticalDragListener = onVerticalDragListener;
+ this.loadVideoOnItemClick = loadVideoOnItemClick;
+ this.sliderCallback = sliderCallback;
+ this.controlsBinding = controlsBinding;
+ }
+
+ @NonNull
+ @Override
+ public SliderItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
+ final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+ final MediaItemType mediaItemType = MediaItemType.valueOf(viewType);
+ switch (mediaItemType) {
+ case MEDIA_TYPE_VIDEO: {
+ final LayoutVideoPlayerWithThumbnailBinding binding = LayoutVideoPlayerWithThumbnailBinding.inflate(inflater, parent, false);
+ return new SliderVideoViewHolder(binding, onVerticalDragListener, controlsBinding, loadVideoOnItemClick);
+ }
+ case MEDIA_TYPE_IMAGE:
+ default:
+ final ItemSliderPhotoBinding binding = ItemSliderPhotoBinding.inflate(inflater, parent, false);
+ return new SliderPhotoViewHolder(binding, onVerticalDragListener);
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull final SliderItemViewHolder holder, final int position) {
+ final PostChild model = getItem(position);
+ holder.bind(model, position, sliderCallback);
+ }
+
+ @Override
+ public int getItemViewType(final int position) {
+ final PostChild viewerPostModel = getItem(position);
+ return viewerPostModel.getItemType().getId();
+ }
+
+ // @NonNull
+ // @Override
+ // public Object instantiateItem(@NonNull final ViewGroup container, final int position) {
+ // final Context context = container.getContext();
+ // final ViewerPostModel sliderItem = sliderItems.get(position);
+ //
+ // if (sliderItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
+ // final ViewSwitcher viewSwitcher = createViewSwitcher(context, position, sliderItem.getThumbnailUrl(), sliderItem.getDisplayUrl());
+ // 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(sliderItem.getDisplayUrl()))
+ // .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 thumbnailUrl,
+ // 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(thumbnailUrl);
+ // 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;
+ // }
+
+ public interface SliderCallback {
+ void onThumbnailLoaded(int position);
+
+ void onItemClicked(int position);
+
+ void onPlayerPlay(int position);
+
+ void onPlayerPause(int position);
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/CommentViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/CommentViewHolder.java
deleted file mode 100755
index 0a2e7687..00000000
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/CommentViewHolder.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package awais.instagrabber.adapters.viewholder;
-
-import android.text.Spannable;
-import android.view.View;
-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;
-import awais.instagrabber.interfaces.MentionClickListener;
-import awais.instagrabber.models.CommentModel;
-
-public final class CommentViewHolder extends RecyclerView.ViewHolder {
- private final MentionClickListener mentionClickListener;
- private final RecyclerView rvChildComments;
- 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) {
- super(itemView);
-
- container = itemView.findViewById(R.id.container);
- if (onClickListener != null) container.setOnClickListener(onClickListener);
-
- this.mentionClickListener = mentionClickListener;
-
- ivProfilePic = itemView.findViewById(R.id.ivProfilePic);
- tvUsername = itemView.findViewById(R.id.tvUsername);
- tvDate = itemView.findViewById(R.id.tvDate);
- tvLikes = itemView.findViewById(R.id.tvLikes);
- tvComment = itemView.findViewById(R.id.tvComment);
-
- tvUsername.setSelected(true);
- tvDate.setSelected(true);
-
- rvChildComments = itemView.findViewById(R.id.rvChildComments);
- }
-
- public final SimpleDraweeView getProfilePicView() {
- return ivProfilePic;
- }
-
- public final boolean isParent() {
- return rvChildComments != null;
- }
-
- public final void setCommentModel(final CommentModel commentModel) {
- if (container != null) container.setTag(commentModel);
- }
-
- 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 setLikes(final String likes) {
- if (tvLikes != null) tvLikes.setText(likes);
- }
-
- public final void setLiked(final boolean liked) {
- if (liked) container.setBackgroundColor(0x40FF69B4);
- }
-
- public final void setComment(final CharSequence comment) {
- if (tvComment != null) {
- tvComment.setText(comment, comment instanceof Spannable ? TextView.BufferType.SPANNABLE : TextView.BufferType.NORMAL);
- ((RamboTextView) tvComment).setMentionClickListener(mentionClickListener);
- }
- }
-
- public final void setChildAdapter(final CommentsAdapter adapter) {
- if (isParent()) {
- rvChildComments.setAdapter(adapter);
- rvChildComments.setVisibility(View.VISIBLE);
- }
- }
-
- public final void hideChildComments() {
- if (isParent()) rvChildComments.setVisibility(View.GONE);
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageInboxItemViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageInboxItemViewHolder.java
index 95abd782..f3285b1a 100644
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageInboxItemViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageInboxItemViewHolder.java
@@ -97,6 +97,7 @@ public final class DirectMessageInboxItemViewHolder extends RecyclerView.ViewHol
case MEDIA_SHARE:
case RAVEN_MEDIA:
case CLIP:
+ case FELIX_SHARE:
messageText = context.getString(R.string.direct_messages_sent_media);
break;
case ACTION_LOG:
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/FeedGridItemViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/FeedGridItemViewHolder.java
new file mode 100644
index 00000000..53a372b8
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/FeedGridItemViewHolder.java
@@ -0,0 +1,161 @@
+package awais.instagrabber.adapters.viewholder;
+
+import android.content.res.ColorStateList;
+import android.net.Uri;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.DimenRes;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.facebook.drawee.backends.pipeline.Fresco;
+import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;
+import com.facebook.imagepipeline.request.ImageRequest;
+import com.facebook.imagepipeline.request.ImageRequestBuilder;
+
+import java.util.List;
+
+import awais.instagrabber.R;
+import awais.instagrabber.adapters.FeedAdapterV2;
+import awais.instagrabber.asyncs.DownloadedCheckerAsyncTask;
+import awais.instagrabber.databinding.ItemFeedGridBinding;
+import awais.instagrabber.models.FeedModel;
+import awais.instagrabber.models.PostChild;
+import awais.instagrabber.models.PostsLayoutPreferences;
+import awais.instagrabber.utils.TextUtils;
+
+import static awais.instagrabber.models.PostsLayoutPreferences.PostsLayoutType.STAGGERED_GRID;
+
+public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
+ private final ItemFeedGridBinding binding;
+
+ public FeedGridItemViewHolder(@NonNull final ItemFeedGridBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
+
+ public void bind(final int position,
+ @NonNull final FeedModel feedModel,
+ @NonNull final PostsLayoutPreferences layoutPreferences,
+ final FeedAdapterV2.FeedItemCallback feedItemCallback,
+ final FeedAdapterV2.AdapterSelectionCallback adapterSelectionCallback,
+ final boolean selectionModeActive,
+ final boolean selected) {
+ itemView.setOnClickListener(v -> {
+ if (!selectionModeActive && feedItemCallback != null) {
+ feedItemCallback.onPostClick(feedModel, binding.profilePic, binding.postImage);
+ return;
+ }
+ if (selectionModeActive && adapterSelectionCallback != null) {
+ adapterSelectionCallback.onPostClick(position, feedModel);
+ }
+ });
+ if (adapterSelectionCallback != null) {
+ itemView.setOnLongClickListener(v -> adapterSelectionCallback.onPostLongClick(position, feedModel));
+ }
+ binding.selectedView.setVisibility(selected ? View.VISIBLE : View.GONE);
+ // for rounded borders (clip view to background shape)
+ itemView.setClipToOutline(layoutPreferences.getHasRoundedCorners());
+ if (layoutPreferences.getType() == STAGGERED_GRID) {
+ final float aspectRatio = (float) feedModel.getImageWidth() / feedModel.getImageHeight();
+ binding.postImage.setAspectRatio(aspectRatio);
+ } else {
+ binding.postImage.setAspectRatio(1);
+ }
+ if (layoutPreferences.isAvatarVisible()) {
+ binding.profilePic.setVisibility(View.VISIBLE);
+ binding.profilePic.setImageURI(feedModel.getProfileModel().getSdProfilePic());
+ final ViewGroup.LayoutParams layoutParams = binding.profilePic.getLayoutParams();
+ @DimenRes final int dimenRes;
+ switch (layoutPreferences.getProfilePicSize()) {
+ case SMALL:
+ dimenRes = R.dimen.profile_pic_size_small;
+ break;
+ case TINY:
+ dimenRes = R.dimen.profile_pic_size_tiny;
+ break;
+ default:
+ case REGULAR:
+ dimenRes = R.dimen.profile_pic_size_regular;
+ break;
+ }
+ final int dimensionPixelSize = itemView.getResources().getDimensionPixelSize(dimenRes);
+ layoutParams.width = dimensionPixelSize;
+ layoutParams.height = dimensionPixelSize;
+ binding.profilePic.requestLayout();
+ } else {
+ binding.profilePic.setVisibility(View.GONE);
+ }
+ if (layoutPreferences.isNameVisible()) {
+ binding.name.setVisibility(View.VISIBLE);
+ binding.name.setText(feedModel.getProfileModel().getUsername());
+ } else {
+ binding.name.setVisibility(View.GONE);
+ }
+ String thumbnailUrl = null;
+ final int typeIconRes;
+ switch (feedModel.getItemType()) {
+ case MEDIA_TYPE_IMAGE:
+ typeIconRes = -1;
+ thumbnailUrl = feedModel.getThumbnailUrl();
+ break;
+ case MEDIA_TYPE_VIDEO:
+ thumbnailUrl = feedModel.getThumbnailUrl();
+ typeIconRes = R.drawable.exo_icon_play;
+ break;
+ case MEDIA_TYPE_SLIDER:
+ final List sliderItems = feedModel.getSliderItems();
+ if (sliderItems != null) {
+ thumbnailUrl = sliderItems.get(0).getThumbnailUrl();
+ }
+ typeIconRes = R.drawable.ic_checkbox_multiple_blank_stroke;
+ break;
+ default:
+ typeIconRes = -1;
+ thumbnailUrl = null;
+ }
+ if (TextUtils.isEmpty(thumbnailUrl)) {
+ binding.postImage.setController(null);
+ return;
+ }
+ if (typeIconRes <= 0) {
+ binding.typeIcon.setVisibility(View.GONE);
+ } else {
+ binding.typeIcon.setVisibility(View.VISIBLE);
+ binding.typeIcon.setImageResource(typeIconRes);
+ }
+ final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(thumbnailUrl))
+ .setLocalThumbnailPreviewsEnabled(true)
+ .setProgressiveRenderingEnabled(true)
+ .build();
+ final PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder()
+ .setImageRequest(requestBuilder)
+ .setOldController(binding.postImage.getController());
+ binding.postImage.setController(builder.build());
+ final DownloadedCheckerAsyncTask task = new DownloadedCheckerAsyncTask(result -> {
+ final List checkList = result.get(feedModel.getPostId());
+ if (checkList == null || checkList.isEmpty()) {
+ return;
+ }
+ switch (feedModel.getItemType()) {
+ case MEDIA_TYPE_IMAGE:
+ case MEDIA_TYPE_VIDEO:
+ binding.downloaded.setVisibility(checkList.get(0) ? View.VISIBLE : View.GONE);
+ binding.downloaded.setImageTintList(ColorStateList.valueOf(itemView.getResources().getColor(R.color.green_A400)));
+ break;
+ case MEDIA_TYPE_SLIDER:
+ binding.downloaded.setVisibility(checkList.get(0) ? View.VISIBLE : View.GONE);
+ boolean allDownloaded = checkList.size() == feedModel.getSliderItems().size();
+ if (allDownloaded) {
+ allDownloaded = checkList.stream().allMatch(downloaded -> downloaded);
+ }
+ binding.downloaded.setImageTintList(ColorStateList.valueOf(itemView.getResources().getColor(
+ allDownloaded ? R.color.green_A400 : R.color.yellow_400)));
+ break;
+ default:
+ }
+ });
+ task.execute(feedModel);
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java
index fbd527c0..ac1ef257 100644
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java
@@ -42,6 +42,9 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder {
text = R.string.mention_notif;
subtext = model.getText();
break;
+ case TAGGED:
+ text = R.string.tagged_notif;
+ break;
case FOLLOW:
text = R.string.follow_notif;
break;
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/PostMediaViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/PostMediaViewHolder.java
index 2c81475a..d2d265a8 100755
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/PostMediaViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/PostMediaViewHolder.java
@@ -19,11 +19,11 @@ public final class PostMediaViewHolder extends RecyclerView.ViewHolder {
public void bind(final ViewerPostModel model, final int position, final View.OnClickListener clickListener) {
if (model == null) return;
- model.setPosition(position);
+ // 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());
+ binding.icon.setImageURI(model.getDisplayUrl());
}
}
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewHolder.java
index 39c1d61a..5900ebc1 100755
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewHolder.java
@@ -25,7 +25,7 @@ public final class PostViewHolder extends RecyclerView.ViewHolder {
final OnItemClickListener clickListener,
final OnItemLongClickListener longClickListener) {
if (postModel == null) return;
- postModel.setPosition(position);
+ // postModel.setPosition(position);
itemView.setOnClickListener(v -> clickListener.onItemClick(postModel, position));
itemView.setOnLongClickListener(v -> longClickListener.onItemLongClick(postModel, position));
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewerViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewerViewHolder.java
index 7bc44b71..dcb2f9fc 100644
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewerViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewerViewHolder.java
@@ -1,239 +1,240 @@
-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 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);
- }
- }
-}
+// 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.PostChild;
+// 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 List items = wrapper.getViewerPostModels();
+// if (items == null || items.size() == 0) return;
+// if (items.get(0) == null) return;
+// final PostViewerChildAdapter adapter = new PostViewerChildAdapter();
+// binding.mediaViewPager.setAdapter(adapter);
+// final PostChild firstPost = items.get(0);
+// setPostInfo(firstPost, mentionClickListener);
+// setMediaItems(items, adapter);
+// setupListeners(wrapper,
+// position,
+// clickListener,
+// longClickListener,
+// mentionClickListener,
+// firstPost.getLocation());
+// }
+//
+// private void setPostInfo(final PostChild 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.isSaved();
+// 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 List items,
+// final PostViewerChildAdapter adapter) {
+// final List 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);
+// }
+// }
+// }
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/SliderItemViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/SliderItemViewHolder.java
new file mode 100644
index 00000000..64b00fb6
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/SliderItemViewHolder.java
@@ -0,0 +1,21 @@
+package awais.instagrabber.adapters.viewholder;
+
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import awais.instagrabber.adapters.SliderItemsAdapter;
+import awais.instagrabber.models.PostChild;
+
+public abstract class SliderItemViewHolder extends RecyclerView.ViewHolder {
+ private static final String TAG = "FeedSliderItemViewHolder";
+
+ public SliderItemViewHolder(@NonNull final View itemView) {
+ super(itemView);
+ }
+
+ public abstract void bind(final PostChild model,
+ final int position,
+ final SliderItemsAdapter.SliderCallback sliderCallback);
+}
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/SliderPhotoViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/SliderPhotoViewHolder.java
new file mode 100644
index 00000000..4788edc0
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/SliderPhotoViewHolder.java
@@ -0,0 +1,122 @@
+package awais.instagrabber.adapters.viewholder;
+
+import android.graphics.drawable.Animatable;
+import android.net.Uri;
+
+import androidx.annotation.NonNull;
+
+import com.facebook.drawee.backends.pipeline.Fresco;
+import com.facebook.drawee.controller.BaseControllerListener;
+import com.facebook.imagepipeline.image.ImageInfo;
+import com.facebook.imagepipeline.request.ImageRequest;
+import com.facebook.imagepipeline.request.ImageRequestBuilder;
+
+import awais.instagrabber.adapters.SliderItemsAdapter;
+import awais.instagrabber.customviews.VerticalDragHelper;
+import awais.instagrabber.customviews.drawee.AnimatedZoomableController;
+import awais.instagrabber.databinding.ItemSliderPhotoBinding;
+import awais.instagrabber.models.PostChild;
+
+public class SliderPhotoViewHolder extends SliderItemViewHolder {
+ private static final String TAG = "FeedSliderPhotoViewHolder";
+
+ private final ItemSliderPhotoBinding binding;
+ private final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener;
+
+ public SliderPhotoViewHolder(@NonNull final ItemSliderPhotoBinding binding,
+ final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener) {
+ super(binding.getRoot());
+ this.binding = binding;
+ this.onVerticalDragListener = onVerticalDragListener;
+ }
+
+ public void bind(@NonNull final PostChild model,
+ final int position,
+ final SliderItemsAdapter.SliderCallback sliderCallback) {
+ final ImageRequest requestBuilder = ImageRequestBuilder
+ .newBuilderWithSource(Uri.parse(model.getDisplayUrl()))
+ .setLocalThumbnailPreviewsEnabled(true)
+ .build();
+ binding.getRoot()
+ .setController(Fresco.newDraweeControllerBuilder()
+ .setImageRequest(requestBuilder)
+ .setControllerListener(new BaseControllerListener() {
+ @Override
+ public void onFailure(final String id, final Throwable throwable) {
+ if (sliderCallback != null) {
+ sliderCallback.onThumbnailLoaded(position);
+ }
+ }
+
+ @Override
+ public void onFinalImageSet(final String id,
+ final ImageInfo imageInfo,
+ final Animatable animatable) {
+ if (sliderCallback != null) {
+ sliderCallback.onThumbnailLoaded(position);
+ }
+ }
+ })
+ .setLowResImageRequest(ImageRequest.fromUri(model.getThumbnailUrl()))
+ .build());
+ binding.getRoot().setOnClickListener(v -> {
+ if (sliderCallback != null) {
+ sliderCallback.onItemClicked(position);
+ }
+ });
+ final AnimatedZoomableController zoomableController = AnimatedZoomableController.newInstance();
+ zoomableController.setMaxScaleFactor(3f);
+ binding.getRoot().setZoomableController(zoomableController);
+ if (onVerticalDragListener != null) {
+ binding.getRoot().setOnVerticalDragListener(onVerticalDragListener);
+ }
+ }
+
+ // private void setDimensions(final FeedModel feedModel, final int spanCount, final boolean animate) {
+ // final ViewGroup.LayoutParams layoutParams = binding.imageViewer.getLayoutParams();
+ // final int deviceWidth = Utils.displayMetrics.widthPixels;
+ // final int spanWidth = deviceWidth / spanCount;
+ // final int spanHeight = NumberUtils.getResultingHeight(spanWidth, feedModel.getImageHeight(), feedModel.getImageWidth());
+ // final int width = spanWidth == 0 ? deviceWidth : spanWidth;
+ // final int height = spanHeight == 0 ? deviceWidth + 1 : spanHeight;
+ // if (animate) {
+ // Animation animation = AnimationUtils.expand(
+ // binding.imageViewer,
+ // layoutParams.width,
+ // layoutParams.height,
+ // width,
+ // height,
+ // new Animation.AnimationListener() {
+ // @Override
+ // public void onAnimationStart(final Animation animation) {
+ // showOrHideDetails(spanCount);
+ // }
+ //
+ // @Override
+ // public void onAnimationEnd(final Animation animation) {
+ // // showOrHideDetails(spanCount);
+ // }
+ //
+ // @Override
+ // public void onAnimationRepeat(final Animation animation) {
+ //
+ // }
+ // });
+ // binding.imageViewer.startAnimation(animation);
+ // } else {
+ // layoutParams.width = width;
+ // layoutParams.height = height;
+ // binding.imageViewer.requestLayout();
+ // }
+ // }
+ //
+ // private void showOrHideDetails(final int spanCount) {
+ // if (spanCount == 1) {
+ // binding.itemFeedTop.getRoot().setVisibility(View.VISIBLE);
+ // binding.itemFeedBottom.getRoot().setVisibility(View.VISIBLE);
+ // } else {
+ // binding.itemFeedTop.getRoot().setVisibility(View.GONE);
+ // binding.itemFeedBottom.getRoot().setVisibility(View.GONE);
+ // }
+ // }
+}
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/SliderVideoViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/SliderVideoViewHolder.java
new file mode 100644
index 00000000..325aa720
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/SliderVideoViewHolder.java
@@ -0,0 +1,185 @@
+package awais.instagrabber.adapters.viewholder;
+
+import android.annotation.SuppressLint;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+
+import awais.instagrabber.adapters.SliderItemsAdapter;
+import awais.instagrabber.customviews.VerticalDragHelper;
+import awais.instagrabber.customviews.VideoPlayerCallbackAdapter;
+import awais.instagrabber.customviews.VideoPlayerViewHelper;
+import awais.instagrabber.databinding.LayoutExoCustomControlsBinding;
+import awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding;
+import awais.instagrabber.models.PostChild;
+import awais.instagrabber.utils.Constants;
+import awais.instagrabber.utils.NumberUtils;
+import awais.instagrabber.utils.Utils;
+
+import static awais.instagrabber.utils.Utils.settingsHelper;
+
+public class SliderVideoViewHolder extends SliderItemViewHolder {
+ private static final String TAG = "SliderVideoViewHolder";
+
+ private final LayoutVideoPlayerWithThumbnailBinding binding;
+ private final LayoutExoCustomControlsBinding controlsBinding;
+ private final boolean loadVideoOnItemClick;
+ private VideoPlayerViewHelper videoPlayerViewHelper;
+
+ @SuppressLint("ClickableViewAccessibility")
+ public SliderVideoViewHolder(@NonNull final LayoutVideoPlayerWithThumbnailBinding binding,
+ final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener,
+ final LayoutExoCustomControlsBinding controlsBinding,
+ final boolean loadVideoOnItemClick) {
+ super(binding.getRoot());
+ this.binding = binding;
+ this.controlsBinding = controlsBinding;
+ this.loadVideoOnItemClick = loadVideoOnItemClick;
+ if (onVerticalDragListener != null) {
+ final VerticalDragHelper thumbnailVerticalDragHelper = new VerticalDragHelper(binding.thumbnailParent);
+ final VerticalDragHelper playerVerticalDragHelper = new VerticalDragHelper(binding.playerView);
+ thumbnailVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
+ playerVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
+ binding.thumbnailParent.setOnTouchListener((v, event) -> {
+ final boolean onDragTouch = thumbnailVerticalDragHelper.onDragTouch(event);
+ if (onDragTouch) {
+ return true;
+ }
+ return thumbnailVerticalDragHelper.onGestureTouchEvent(event);
+ });
+ binding.playerView.setOnTouchListener((v, event) -> {
+ final boolean onDragTouch = playerVerticalDragHelper.onDragTouch(event);
+ if (onDragTouch) {
+ return true;
+ }
+ return playerVerticalDragHelper.onGestureTouchEvent(event);
+ });
+ }
+ }
+
+ public void bind(@NonNull final PostChild model,
+ final int position,
+ final SliderItemsAdapter.SliderCallback sliderCallback) {
+ final float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
+ final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() {
+
+ @Override
+ public void onThumbnailClick() {
+ if (sliderCallback != null) {
+ sliderCallback.onItemClicked(position);
+ }
+ }
+
+ @Override
+ public void onThumbnailLoaded() {
+ if (sliderCallback != null) {
+ sliderCallback.onThumbnailLoaded(position);
+ }
+ }
+
+ @Override
+ public void onPlayerViewLoaded() {
+ // 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, model.getHeight(), model.getWidth());
+ layoutParams.width = requiredWidth;
+ layoutParams.height = resultingHeight;
+ binding.playerView.requestLayout();
+ // setMuteIcon(vol == 0f && Utils.sessionVolumeFull ? 1f : vol);
+ }
+
+ @Override
+ public void onPlay() {
+ if (sliderCallback != null) {
+ sliderCallback.onPlayerPlay(position);
+ }
+ }
+
+ @Override
+ public void onPause() {
+ if (sliderCallback != null) {
+ sliderCallback.onPlayerPause(position);
+ }
+ }
+ };
+ final float aspectRatio = (float) model.getWidth() / model.getHeight();
+ videoPlayerViewHelper = new VideoPlayerViewHelper(binding.getRoot().getContext(),
+ binding,
+ model.getDisplayUrl(),
+ vol,
+ aspectRatio,
+ model.getThumbnailUrl(),
+ loadVideoOnItemClick,
+ controlsBinding,
+ videoPlayerCallback);
+ // binding.itemFeedBottom.btnMute.setOnClickListener(v -> {
+ // final float newVol = videoPlayerViewHelper.toggleMute();
+ // setMuteIcon(newVol);
+ // Utils.sessionVolumeFull = newVol == 1f;
+ // });
+ binding.playerView.setOnClickListener(v -> {
+ if (sliderCallback != null) {
+ sliderCallback.onItemClicked(position);
+ }
+ });
+ }
+
+ public void pause() {
+ if (videoPlayerViewHelper == null) return;
+ videoPlayerViewHelper.pause();
+ }
+
+ public void releasePlayer() {
+ if (videoPlayerViewHelper == null) return;
+ videoPlayerViewHelper.releasePlayer();
+ }
+
+ // private void setDimensions(final FeedModel feedModel, final int spanCount, final boolean animate) {
+ // final ViewGroup.LayoutParams layoutParams = binding.imageViewer.getLayoutParams();
+ // final int deviceWidth = Utils.displayMetrics.widthPixels;
+ // final int spanWidth = deviceWidth / spanCount;
+ // final int spanHeight = NumberUtils.getResultingHeight(spanWidth, feedModel.getImageHeight(), feedModel.getImageWidth());
+ // final int width = spanWidth == 0 ? deviceWidth : spanWidth;
+ // final int height = spanHeight == 0 ? deviceWidth + 1 : spanHeight;
+ // if (animate) {
+ // Animation animation = AnimationUtils.expand(
+ // binding.imageViewer,
+ // layoutParams.width,
+ // layoutParams.height,
+ // width,
+ // height,
+ // new Animation.AnimationListener() {
+ // @Override
+ // public void onAnimationStart(final Animation animation) {
+ // showOrHideDetails(spanCount);
+ // }
+ //
+ // @Override
+ // public void onAnimationEnd(final Animation animation) {
+ // // showOrHideDetails(spanCount);
+ // }
+ //
+ // @Override
+ // public void onAnimationRepeat(final Animation animation) {
+ //
+ // }
+ // });
+ // binding.imageViewer.startAnimation(animation);
+ // } else {
+ // layoutParams.width = width;
+ // layoutParams.height = height;
+ // binding.imageViewer.requestLayout();
+ // }
+ // }
+ //
+ // private void showOrHideDetails(final int spanCount) {
+ // if (spanCount == 1) {
+ // binding.itemFeedTop.getRoot().setVisibility(View.VISIBLE);
+ // binding.itemFeedBottom.getRoot().setVisibility(View.VISIBLE);
+ // } else {
+ // binding.itemFeedTop.getRoot().setVisibility(View.GONE);
+ // binding.itemFeedBottom.getRoot().setVisibility(View.GONE);
+ // }
+ // }
+}
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/TopicClusterViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/TopicClusterViewHolder.java
new file mode 100644
index 00000000..6e7eaed3
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/TopicClusterViewHolder.java
@@ -0,0 +1,99 @@
+package awais.instagrabber.adapters.viewholder;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.drawable.GradientDrawable;
+import android.net.Uri;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.palette.graphics.Palette;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.facebook.common.executors.CallerThreadExecutor;
+import com.facebook.common.references.CloseableReference;
+import com.facebook.datasource.DataSource;
+import com.facebook.drawee.backends.pipeline.Fresco;
+import com.facebook.imagepipeline.core.ImagePipeline;
+import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
+import com.facebook.imagepipeline.image.CloseableImage;
+import com.facebook.imagepipeline.request.ImageRequest;
+import com.facebook.imagepipeline.request.ImageRequestBuilder;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import awais.instagrabber.R;
+import awais.instagrabber.adapters.DiscoverTopicsAdapter;
+import awais.instagrabber.databinding.ItemDiscoverTopicBinding;
+import awais.instagrabber.models.TopicCluster;
+
+public class TopicClusterViewHolder extends RecyclerView.ViewHolder {
+ private final ItemDiscoverTopicBinding binding;
+ private final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener;
+
+ public TopicClusterViewHolder(@NonNull final ItemDiscoverTopicBinding binding,
+ final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener) {
+ super(binding.getRoot());
+ this.binding = binding;
+ this.onTopicClickListener = onTopicClickListener;
+ }
+
+ public void bind(final TopicCluster topicCluster) {
+ if (topicCluster == null) {
+ return;
+ }
+ final AtomicInteger titleColor = new AtomicInteger(-1);
+ final AtomicInteger backgroundColor = new AtomicInteger(-1);
+ if (onTopicClickListener != null) {
+ itemView.setOnClickListener(v -> onTopicClickListener.onTopicClick(
+ topicCluster,
+ binding.getRoot(),
+ binding.cover,
+ binding.title,
+ titleColor.get(),
+ backgroundColor.get()
+ ));
+ }
+ // binding.title.setTransitionName("title-" + topicCluster.getId());
+ binding.cover.setTransitionName("cover-" + topicCluster.getId());
+ final ImageRequest imageRequest = ImageRequestBuilder
+ .newBuilderWithSource(Uri.parse(topicCluster.getCoverMedia().getDisplayUrl()))
+ .build();
+ final ImagePipeline imagePipeline = Fresco.getImagePipeline();
+ final DataSource> dataSource = imagePipeline
+ .fetchDecodedImage(imageRequest, CallerThreadExecutor.getInstance());
+ dataSource.subscribe(new BaseBitmapDataSubscriber() {
+ @Override
+ public void onNewResultImpl(@Nullable Bitmap bitmap) {
+ if (dataSource.isFinished()) {
+ dataSource.close();
+ }
+ if (bitmap != null) {
+ Palette.from(bitmap).generate(p -> {
+ final Palette.Swatch swatch = p.getDominantSwatch();
+ final Resources resources = itemView.getResources();
+ int titleTextColor = resources.getColor(R.color.white);
+ if (swatch != null) {
+ backgroundColor.set(swatch.getRgb());
+ GradientDrawable gd = new GradientDrawable(
+ GradientDrawable.Orientation.TOP_BOTTOM,
+ new int[]{Color.TRANSPARENT, backgroundColor.get()});
+ titleTextColor = swatch.getTitleTextColor();
+ binding.background.setBackground(gd);
+ }
+ titleColor.set(titleTextColor);
+ binding.title.setTextColor(titleTextColor);
+ });
+ }
+ }
+
+ @Override
+ public void onFailureImpl(@NonNull DataSource dataSource) {
+ dataSource.close();
+ }
+ }, CallerThreadExecutor.getInstance());
+ binding.cover.setImageRequest(imageRequest);
+ binding.title.setText(topicCluster.getTitle());
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ChildCommentViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ChildCommentViewHolder.java
new file mode 100644
index 00000000..bc5aecd8
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ChildCommentViewHolder.java
@@ -0,0 +1,96 @@
+package awais.instagrabber.adapters.viewholder.comments;
+
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import awais.instagrabber.R;
+import awais.instagrabber.adapters.CommentsAdapter.CommentCallback;
+import awais.instagrabber.databinding.ItemCommentSmallBinding;
+import awais.instagrabber.models.CommentModel;
+import awais.instagrabber.models.ProfileModel;
+import awais.instagrabber.utils.Utils;
+
+public final class ChildCommentViewHolder extends RecyclerView.ViewHolder {
+
+ private final ItemCommentSmallBinding binding;
+
+ public ChildCommentViewHolder(@NonNull final ItemCommentSmallBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
+
+ public void bind(final CommentModel comment,
+ final boolean selected,
+ final CommentCallback commentCallback) {
+ if (comment == null) return;
+ if (commentCallback != null) {
+ itemView.setOnClickListener(v -> commentCallback.onClick(comment));
+ }
+ if (selected) {
+ itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_selected));
+ } else {
+ itemView.setBackgroundColor(itemView.getResources().getColor(android.R.color.transparent));
+ }
+ setupCommentText(comment, commentCallback);
+ binding.tvDate.setText(comment.getDateTime());
+ setLiked(comment.getLiked());
+ setLikes((int) comment.getLikes());
+ setUser(comment);
+ }
+
+ private void setupCommentText(final CommentModel comment, final CommentCallback commentCallback) {
+ binding.tvComment.clearOnURLClickListeners();
+ binding.tvComment.clearOnHashtagClickListeners();
+ binding.tvComment.clearOnMentionClickListeners();
+ binding.tvComment.clearOnEmailClickListeners();
+ binding.tvComment.setText(comment.getText());
+ binding.tvComment.addOnHashtagListener(autoLinkItem -> {
+ final String originalText = autoLinkItem.getOriginalText();
+ if (commentCallback == null) return;
+ commentCallback.onHashtagClick(originalText);
+ });
+ binding.tvComment.addOnMentionClickListener(autoLinkItem -> {
+ final String originalText = autoLinkItem.getOriginalText();
+ if (commentCallback == null) return;
+ commentCallback.onMentionClick(originalText);
+
+ });
+ binding.tvComment.addOnEmailClickListener(autoLinkItem -> {
+ final String originalText = autoLinkItem.getOriginalText();
+ if (commentCallback == null) return;
+ commentCallback.onEmailClick(originalText);
+ });
+ binding.tvComment.addOnURLClickListener(autoLinkItem -> {
+ final String originalText = autoLinkItem.getOriginalText();
+ if (commentCallback == null) return;
+ commentCallback.onURLClick(originalText);
+ });
+ binding.tvComment.setOnLongClickListener(v -> {
+ Utils.copyText(itemView.getContext(), comment.getText());
+ return true;
+ });
+ binding.tvComment.setOnClickListener(v -> commentCallback.onClick(comment));
+ }
+
+ private void setUser(final CommentModel comment) {
+ final ProfileModel profileModel = comment.getProfileModel();
+ if (profileModel == null) return;
+ binding.tvUsername.setText(profileModel.getUsername());
+ binding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
+ binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
+ }
+
+ private void setLikes(final int likes) {
+ final String likesString = itemView.getResources().getQuantityString(R.plurals.likes_count, likes, likes);
+ binding.tvLikes.setText(likesString);
+ }
+
+ public final void setLiked(final boolean liked) {
+ if (liked) {
+ // container.setBackgroundColor(0x40FF69B4);
+ return;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ParentCommentViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ParentCommentViewHolder.java
new file mode 100644
index 00000000..2b595d9e
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ParentCommentViewHolder.java
@@ -0,0 +1,96 @@
+package awais.instagrabber.adapters.viewholder.comments;
+
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import awais.instagrabber.R;
+import awais.instagrabber.adapters.CommentsAdapter.CommentCallback;
+import awais.instagrabber.databinding.ItemCommentBinding;
+import awais.instagrabber.models.CommentModel;
+import awais.instagrabber.models.ProfileModel;
+import awais.instagrabber.utils.Utils;
+
+public final class ParentCommentViewHolder extends RecyclerView.ViewHolder {
+
+ private final ItemCommentBinding binding;
+
+ public ParentCommentViewHolder(@NonNull final ItemCommentBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
+
+ public void bind(final CommentModel comment,
+ final boolean selected,
+ final CommentCallback commentCallback) {
+ if (comment == null) return;
+ if (commentCallback != null) {
+ itemView.setOnClickListener(v -> commentCallback.onClick(comment));
+ }
+ if (selected) {
+ itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_selected));
+ } else {
+ itemView.setBackgroundColor(itemView.getResources().getColor(android.R.color.transparent));
+ }
+ setupCommentText(comment, commentCallback);
+ binding.tvDate.setText(comment.getDateTime());
+ setLiked(comment.getLiked());
+ setLikes((int) comment.getLikes());
+ setUser(comment);
+ }
+
+ private void setupCommentText(final CommentModel comment, final CommentCallback commentCallback) {
+ binding.tvComment.clearOnURLClickListeners();
+ binding.tvComment.clearOnHashtagClickListeners();
+ binding.tvComment.clearOnMentionClickListeners();
+ binding.tvComment.clearOnEmailClickListeners();
+ binding.tvComment.setText(comment.getText());
+ binding.tvComment.addOnHashtagListener(autoLinkItem -> {
+ final String originalText = autoLinkItem.getOriginalText();
+ if (commentCallback == null) return;
+ commentCallback.onHashtagClick(originalText);
+ });
+ binding.tvComment.addOnMentionClickListener(autoLinkItem -> {
+ final String originalText = autoLinkItem.getOriginalText();
+ if (commentCallback == null) return;
+ commentCallback.onMentionClick(originalText);
+
+ });
+ binding.tvComment.addOnEmailClickListener(autoLinkItem -> {
+ final String originalText = autoLinkItem.getOriginalText();
+ if (commentCallback == null) return;
+ commentCallback.onEmailClick(originalText);
+ });
+ binding.tvComment.addOnURLClickListener(autoLinkItem -> {
+ final String originalText = autoLinkItem.getOriginalText();
+ if (commentCallback == null) return;
+ commentCallback.onURLClick(originalText);
+ });
+ binding.tvComment.setOnLongClickListener(v -> {
+ Utils.copyText(itemView.getContext(), comment.getText());
+ return true;
+ });
+ binding.tvComment.setOnClickListener(v -> commentCallback.onClick(comment));
+ }
+
+ private void setUser(final CommentModel comment) {
+ final ProfileModel profileModel = comment.getProfileModel();
+ if (profileModel == null) return;
+ binding.tvUsername.setText(profileModel.getUsername());
+ binding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
+ binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
+ }
+
+ private void setLikes(final int likes) {
+ final String likesString = itemView.getResources().getQuantityString(R.plurals.likes_count, likes, likes);
+ binding.tvLikes.setText(likesString);
+ }
+
+ public final void setLiked(final boolean liked) {
+ if (liked) {
+ // container.setBackgroundColor(0x40FF69B4);
+ return;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageAnimatedMediaViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageAnimatedMediaViewHolder.java
index 8f9f3f4e..17d00739 100644
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageAnimatedMediaViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageAnimatedMediaViewHolder.java
@@ -1,33 +1,64 @@
package awais.instagrabber.adapters.viewholder.directmessages;
+import android.net.Uri;
import android.view.View;
+import android.view.ViewGroup;
import androidx.annotation.NonNull;
+import androidx.core.util.Pair;
import com.facebook.drawee.backends.pipeline.Fresco;
+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.LayoutDmAnimatedMediaBinding;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
+import awais.instagrabber.utils.NumberUtils;
+import awais.instagrabber.utils.Utils;
public class DirectMessageAnimatedMediaViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmAnimatedMediaBinding binding;
+ private final int maxHeight;
+ private final int maxWidth;
public DirectMessageAnimatedMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmAnimatedMediaBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
+ maxHeight = itemView.getResources().getDimensionPixelSize(R.dimen.dm_media_img_max_height);
+ maxWidth = (int) (Utils.displayMetrics.widthPixels - Utils.convertDpToPx(64) - getItemMargin());
setItemView(binding.getRoot());
+ removeElevation();
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
+ final DirectItemModel.DirectItemAnimatedMediaModel animatedMediaModel = directItemModel.getAnimatedMediaModel();
+ final String url = animatedMediaModel.getGifUrl();
+ final Pair widthHeight = NumberUtils.calculateWidthHeight(
+ animatedMediaModel.getHeight(),
+ animatedMediaModel.getWidth(),
+ maxHeight,
+ maxWidth
+ );
+ binding.ivAnimatedMessage.setVisibility(View.VISIBLE);
+ final ViewGroup.LayoutParams layoutParams = binding.ivAnimatedMessage.getLayoutParams();
+ final int width = widthHeight.first != null ? widthHeight.first : 0;
+ final int height = widthHeight.second != null ? widthHeight.second : 0;
+ layoutParams.width = width;
+ layoutParams.height = height;
+ binding.ivAnimatedMessage.requestLayout();
+ final ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
+ .setResizeOptions(ResizeOptions.forDimensions(width, height))
+ .build();
binding.ivAnimatedMessage.setController(Fresco.newDraweeControllerBuilder()
- .setUri(directItemModel.getAnimatedMediaModel().getGifUrl())
+ .setImageRequest(request)
.setAutoPlayAnimations(true)
.build());
- binding.ivAnimatedMessage.setVisibility(View.VISIBLE);
}
}
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageItemViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageItemViewHolder.java
index 747fd8ef..675b3f6d 100644
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageItemViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageItemViewHolder.java
@@ -71,6 +71,10 @@ public abstract class DirectMessageItemViewHolder extends RecyclerView.ViewHolde
this.binding.messageCard.addView(view);
}
+ public int getItemMargin() {
+ return itemMargin;
+ }
+
public abstract void bindItem(final DirectItemModel directItemModel);
@Nullable
@@ -88,4 +92,8 @@ public abstract class DirectMessageItemViewHolder extends RecyclerView.ViewHolde
}
return null;
}
+
+ protected void removeElevation() {
+ binding.messageCard.setCardElevation(0);
+ }
}
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageMediaViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageMediaViewHolder.java
index 8d7e0cd7..91c692c5 100644
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageMediaViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageMediaViewHolder.java
@@ -26,7 +26,7 @@ public class DirectMessageMediaViewHolder extends DirectMessageItemViewHolder {
super(baseBinding, onClickListener);
this.binding = binding;
maxHeight = itemView.getResources().getDimensionPixelSize(R.dimen.dm_media_img_max_height);
- maxWidth = (int) (Utils.displayMetrics.widthPixels * 0.8);
+ maxWidth = (int) (Utils.displayMetrics.widthPixels - Utils.convertDpToPx(64) - getItemMargin());
setItemView(binding.getRoot());
}
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedItemViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedItemViewHolder.java
index ce28419f..8e70cdeb 100644
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedItemViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedItemViewHolder.java
@@ -1,92 +1,110 @@
package awais.instagrabber.adapters.viewholder.feed;
-import android.text.SpannableString;
-import android.text.Spanned;
+import android.text.method.LinkMovementMethod;
+import android.transition.TransitionManager;
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.adapters.FeedAdapterV2;
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.models.enums.MediaItemType;
import awais.instagrabber.utils.TextUtils;
+import static android.text.TextUtils.TruncateAt.END;
+
public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
- public static final int MAX_CHARS = 255;
+ public static final int MAX_LINES_COLLAPSED = 5;
private final ItemFeedTopBinding topBinding;
private final ItemFeedBottomBinding bottomBinding;
- private final MentionClickListener mentionClickListener;
+ private final FeedAdapterV2.FeedItemCallback feedItemCallback;
public FeedItemViewHolder(@NonNull final View root,
final ItemFeedTopBinding topBinding,
final ItemFeedBottomBinding bottomBinding,
- final MentionClickListener mentionClickListener,
- final View.OnClickListener clickListener,
- final View.OnLongClickListener longClickListener) {
+ final FeedAdapterV2.FeedItemCallback feedItemCallback) {
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);
+ topBinding.title.setMovementMethod(new LinkMovementMethod());
+ this.feedItemCallback = feedItemCallback;
}
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));
- }
+ setupProfilePic(feedModel);
+ setupLocation(feedModel);
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);
- }
+ setupComments(feedModel);
+ setupCaption(feedModel);
+ if (feedModel.getItemType() != MediaItemType.MEDIA_TYPE_SLIDER) {
+ bottomBinding.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(feedModel, -1));
}
- expandCollapseTextView(bottomBinding.viewerCaption, feedModel.getPostCaption());
bindItem(feedModel);
}
- private void setLocation(final String locationName, final String locationId) {
+ private void setupComments(final FeedModel feedModel) {
+ final long commentsCount = feedModel.getCommentsCount();
+ bottomBinding.commentsCount.setText(String.valueOf(commentsCount));
+ bottomBinding.commentsCount.setOnClickListener(v -> feedItemCallback.onCommentsClick(feedModel));
+ }
+
+ private void setupProfilePic(final FeedModel feedModel) {
+ final ProfileModel profileModel = feedModel.getProfileModel();
+ if (profileModel != null) {
+ topBinding.ivProfilePic.setOnClickListener(v -> feedItemCallback.onProfilePicClick(feedModel, topBinding.ivProfilePic));
+ topBinding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
+ setupTitle(feedModel);
+ }
+ }
+
+ private void setupTitle(final FeedModel feedModel) {
+ // final int titleLen = profileModel.getUsername().length() + 1;
+ // final SpannableString spannableString = new SpannableString();
+ // spannableString.setSpan(new CommentMentionClickSpan(), 0, titleLen, 0);
+ final ProfileModel profileModel = feedModel.getProfileModel();
+ final String title = "@" + profileModel.getUsername();
+ topBinding.title.setText(title);
+ topBinding.title.setOnClickListener(v -> feedItemCallback.onNameClick(feedModel, topBinding.ivProfilePic));
+ }
+
+ private void setupCaption(final FeedModel feedModel) {
+ bottomBinding.viewerCaption.clearOnMentionClickListeners();
+ bottomBinding.viewerCaption.clearOnHashtagClickListeners();
+ bottomBinding.viewerCaption.clearOnURLClickListeners();
+ bottomBinding.viewerCaption.clearOnEmailClickListeners();
+ final CharSequence postCaption = feedModel.getPostCaption();
+ final boolean captionEmpty = TextUtils.isEmpty(postCaption);
+ bottomBinding.viewerCaption.setVisibility(captionEmpty ? View.GONE : View.VISIBLE);
+ if (captionEmpty) return;
+ bottomBinding.viewerCaption.setText(postCaption);
+ bottomBinding.viewerCaption.setMaxLines(MAX_LINES_COLLAPSED);
+ bottomBinding.viewerCaption.setEllipsize(END);
+ bottomBinding.viewerCaption.setOnClickListener(v -> bottomBinding.getRoot().post(() -> {
+ TransitionManager.beginDelayedTransition(bottomBinding.getRoot());
+ if (bottomBinding.viewerCaption.getMaxLines() == MAX_LINES_COLLAPSED) {
+ bottomBinding.viewerCaption.setMaxLines(Integer.MAX_VALUE);
+ bottomBinding.viewerCaption.setEllipsize(null);
+ return;
+ }
+ bottomBinding.viewerCaption.setMaxLines(MAX_LINES_COLLAPSED);
+ bottomBinding.viewerCaption.setEllipsize(END);
+ }));
+ bottomBinding.viewerCaption.addOnMentionClickListener(autoLinkItem -> feedItemCallback.onMentionClick(autoLinkItem.getOriginalText()));
+ bottomBinding.viewerCaption.addOnHashtagListener(autoLinkItem -> feedItemCallback.onHashtagClick(autoLinkItem.getOriginalText()));
+ bottomBinding.viewerCaption.addOnEmailClickListener(autoLinkItem -> feedItemCallback.onEmailClick(autoLinkItem.getOriginalText()));
+ bottomBinding.viewerCaption.addOnURLClickListener(autoLinkItem -> feedItemCallback.onURLClick(autoLinkItem.getOriginalText()));
+ }
+
+ private void setupLocation(final FeedModel feedModel) {
+ final String locationName = feedModel.getLocationName();
if (TextUtils.isEmpty(locationName)) {
topBinding.location.setVisibility(View.GONE);
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
@@ -98,41 +116,9 @@ public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
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));
+ topBinding.location.setOnClickListener(v -> feedItemCallback.onLocationClick(feedModel));
}
}
- /**
- * 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);
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedPhotoViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedPhotoViewHolder.java
index f89a2624..633886a4 100644
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedPhotoViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedPhotoViewHolder.java
@@ -1,8 +1,9 @@
package awais.instagrabber.adapters.viewholder.feed;
import android.net.Uri;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
import androidx.annotation.NonNull;
@@ -13,25 +14,24 @@ import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
+import awais.instagrabber.adapters.FeedAdapterV2;
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;
+ private final FeedAdapterV2.FeedItemCallback feedItemCallback;
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);
+ final FeedAdapterV2.FeedItemCallback feedItemCallback) {
+ super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
this.binding = binding;
- binding.itemFeedBottom.videoViewsContainer.setVisibility(View.GONE);
- binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
+ this.feedItemCallback = feedItemCallback;
+ binding.itemFeedBottom.tvVideoViews.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)
@@ -44,33 +44,35 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
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 thumbnailRequestBuilder = glide
- // .asBitmap()
- // .load(thumbnailUrl)
- // .diskCacheStrategy(DiskCacheStrategy.ALL);
- // glide.asBitmap()
- // .load(url)
- // .thumbnail(thumbnailRequestBuilder)
- // .diskCacheStrategy(DiskCacheStrategy.ALL)
- // .into(customTarget);
+ binding.getRoot().post(() -> {
+ setDimensions(feedModel);
+ 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.setTapListener(new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onSingleTapConfirmed(final MotionEvent e) {
+ if (feedItemCallback != null) {
+ feedItemCallback.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.imageViewer);
+ return true;
+ }
+ return false;
+ }
+ });
+ });
+ }
+ private void setDimensions(final FeedModel feedModel) {
+ final float aspectRatio = (float) feedModel.getImageWidth() / feedModel.getImageHeight();
+ binding.imageViewer.setAspectRatio(aspectRatio);
}
}
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedSliderViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedSliderViewHolder.java
index 9fa5fcf5..629d0c67 100644
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedSliderViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedSliderViewHolder.java
@@ -1,24 +1,15 @@
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.view.ViewTreeObserver;
import android.widget.ViewSwitcher;
import androidx.annotation.NonNull;
-import androidx.appcompat.widget.AppCompatImageView;
-import androidx.viewpager.widget.PagerAdapter;
-import androidx.viewpager.widget.ViewPager;
+import androidx.viewpager2.widget.ViewPager2;
-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.MediaItem;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
@@ -28,13 +19,16 @@ 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 java.util.List;
+
+import awais.instagrabber.adapters.FeedAdapterV2;
+import awais.instagrabber.adapters.SliderCallbackAdapter;
+import awais.instagrabber.adapters.SliderItemsAdapter;
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.models.PostChild;
import awais.instagrabber.utils.Constants;
+import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
@@ -44,25 +38,20 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
private static final boolean shouldAutoPlay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
private final ItemFeedSliderBinding binding;
+ private final FeedAdapterV2.FeedItemCallback feedItemCallback;
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);
+ final FeedAdapterV2.FeedItemCallback feedItemCallback) {
+ super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
this.binding = binding;
- binding.itemFeedBottom.videoViewsContainer.setVisibility(View.GONE);
- binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
+ this.feedItemCallback = feedItemCallback;
+ binding.itemFeedBottom.tvVideoViews.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);
@@ -76,112 +65,108 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
@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 List sliderItems = feedModel.getSliderItems();
+ final int sliderItemLen = sliderItems != null ? sliderItems.size() : 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;
-
+ binding.mediaList.setOffscreenPageLimit(1);
+ final SliderItemsAdapter adapter = new SliderItemsAdapter(null, null, false, new SliderCallbackAdapter() {
@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);
+ public void onItemClicked(final int position) {
+ feedItemCallback.onSliderClick(feedModel, position);
}
});
+ binding.mediaList.setAdapter(adapter);
+ binding.mediaList.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
+ @Override
+ public void onPageSelected(final int position) {
+ if (position >= sliderItemLen) return;
+ final String text = (position + 1) + "/" + sliderItemLen;
+ binding.mediaCounter.setText(text);
+ setDimensions(binding.mediaList, sliderItems.get(position));
+ binding.itemFeedBottom.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(feedModel, position));
+ }
+ });
+ setDimensions(binding.mediaList, sliderItems.get(0));
+ binding.itemFeedBottom.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(feedModel, 0));
+ adapter.submitList(sliderItems);
+ // final View.OnClickListener muteClickListener = v -> {
+ // final int currentItem = binding.mediaList.getCurrentItem();
+ // if (currentItem < 0 || currentItem >= binding.mediaList.getChildCount()) {
+ // return;
+ // }
+ // final PostChild sliderItem = sliderItems.get(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 PostChild firstItem = sliderItems.get(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);
+ }
- 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);
+ private void setDimensions(final View view, final PostChild model) {
+ final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams();
+ int requiredWidth = layoutParams.width;
+ if (requiredWidth <= 0) {
+ final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ view.getViewTreeObserver().removeOnPreDrawListener(this);
+ setLayoutParamDimens(binding.mediaList, model);
+ return true;
+ }
+ };
+ view.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
+ return;
}
- binding.itemFeedBottom.btnMute.setImageResource(Utils.sessionVolumeFull ? R.drawable.ic_volume_off_24 : R.drawable.ic_volume_up_24);
- binding.itemFeedBottom.btnMute.setOnClickListener(muteClickListener);
+ setLayoutParamDimens(binding.mediaList, model);
+ }
+
+ private void setLayoutParamDimens(final View view, final PostChild model) {
+ final int requiredWidth = view.getMeasuredWidth();
+ final ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ final int spanHeight = NumberUtils.getResultingHeight(requiredWidth, model.getHeight(), model.getWidth());
+ layoutParams.height = spanHeight == 0 ? requiredWidth + 1 : spanHeight;
+ view.requestLayout();
}
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);
+ // 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() {
@@ -200,7 +185,8 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
}
private static void loadPlayer(final Context context,
- final int position, final String displayUrl,
+ final int position,
+ final String displayUrl,
final ViewSwitcher viewSwitcher,
final DataSource.Factory factory,
final PlayerChangeListener playerChangeListener) {
@@ -223,119 +209,14 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
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));
+ final MediaItem mediaItem = MediaItem.fromUri(displayUrl);
+ final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(factory).createMediaSource(mediaItem);
player.setRepeatMode(Player.REPEAT_MODE_ALL);
- player.prepare(mediaSource);
+ player.setMediaSource(mediaSource);
+ player.prepare();
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];
- }
- }
}
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedVideoViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedVideoViewHolder.java
index 2887cbd4..e82e9893 100644
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedVideoViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedVideoViewHolder.java
@@ -1,7 +1,6 @@
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;
@@ -9,21 +8,14 @@ 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.adapters.FeedAdapterV2;
+import awais.instagrabber.customviews.VideoPlayerCallbackAdapter;
+import awais.instagrabber.customviews.VideoPlayerViewHelper;
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;
@@ -35,27 +27,26 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
private static final String TAG = "FeedVideoViewHolder";
private final ItemFeedVideoBinding binding;
+ private final FeedAdapterV2.FeedItemCallback feedItemCallback;
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);
- }
- };
+ // 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);
+ final FeedAdapterV2.FeedItemCallback feedItemCallback) {
+ super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
this.binding = binding;
- binding.itemFeedBottom.videoViewsContainer.setVisibility(View.VISIBLE);
+ this.feedItemCallback = feedItemCallback;
+ binding.itemFeedBottom.tvVideoViews.setVisibility(View.VISIBLE);
handler = new Handler(Looper.getMainLooper());
final Context context = binding.getRoot().getContext();
dataSourceFactory = new DefaultDataSourceFactory(context, "instagram");
@@ -69,90 +60,80 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
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()));
- }
+ // showOrHideDetails(false);
+ final float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
+ final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() {
- 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));
- }
+ @Override
+ public void onThumbnailClick() {
+ feedItemCallback.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.videoPost.thumbnail);
+ }
- 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;
+ @Override
+ public void onPlayerViewLoaded() {
+ // binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
+ final ViewGroup.LayoutParams layoutParams = binding.videoPost.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.videoPost.playerView.requestLayout();
+ setMuteIcon(vol == 0f && Utils.sessionVolumeFull ? 1f : vol);
+ }
+ };
+ // final DataSource.Factory factory = cacheDataSourceFactory != null ? cacheDataSourceFactory : dataSourceFactory;
+ // final ProgressiveMediaSource.Factory sourceFactory = new ProgressiveMediaSource.Factory(factory);
+ // final Uri uri = Uri.parse(feedModel.getDisplayUrl());
+ // final MediaItem mediaItem = MediaItem.fromUri(uri);
+ // final ProgressiveMediaSource mediaSource = sourceFactory.createMediaSource(mediaItem);
+ final float aspectRatio = (float) feedModel.getImageWidth() / feedModel.getImageHeight();
+ final VideoPlayerViewHelper videoPlayerViewHelper = new VideoPlayerViewHelper(binding.getRoot().getContext(),
+ binding.videoPost,
+ feedModel.getDisplayUrl(),
+ vol,
+ aspectRatio,
+ feedModel.getThumbnailUrl(),
+ false,
+ null,
+ videoPlayerCallback);
+ binding.videoPost.thumbnail.post(() -> {
+ if (feedModel.getImageHeight() > 0.8 * Utils.displayMetrics.heightPixels) {
+ final ViewGroup.LayoutParams layoutParams = binding.videoPost.thumbnail.getLayoutParams();
+ layoutParams.height = (int) (0.8 * Utils.displayMetrics.heightPixels);
+ binding.videoPost.thumbnail.requestLayout();
+ }
});
- binding.playerView.setOnClickListener(v -> finalPlayer.setPlayWhenReady(!finalPlayer.getPlayWhenReady()));
+ // binding.itemFeedBottom.btnMute.setOnClickListener(v -> {
+ // final float newVol = videoPlayerViewHelper.toggleMute();
+ // setMuteIcon(newVol);
+ // Utils.sessionVolumeFull = newVol == 1f;
+ // });
+ // binding.videoPost.playerView.setOnClickListener(v -> videoPlayerViewHelper.togglePlayback());
}
+
private void setMuteIcon(final float vol) {
- binding.itemFeedBottom.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
+ // 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);
- }
+ // 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.videoPost.root.getDisplayedChild() == 1) {
+ // binding.videoPost.root.showPrevious();
+ // }
+ // }
+ //
+ // public void startPlaying() {
+ // handler.removeCallbacks(loadRunnable);
+ // handler.postDelayed(loadRunnable, 800);
+ // }
}
diff --git a/app/src/main/java/awais/instagrabber/animations/ResizeAnimation.java b/app/src/main/java/awais/instagrabber/animations/ResizeAnimation.java
new file mode 100644
index 00000000..cec093ac
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/animations/ResizeAnimation.java
@@ -0,0 +1,45 @@
+package awais.instagrabber.animations;
+
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+public class ResizeAnimation extends Animation {
+ private static final String TAG = "ResizeAnimation";
+
+ final View view;
+ final int startHeight;
+ final int targetHeight;
+ final int startWidth;
+ final int targetWidth;
+
+ public ResizeAnimation(final View view,
+ final int startHeight,
+ final int startWidth,
+ final int targetHeight,
+ final int targetWidth) {
+ this.view = view;
+ this.startHeight = startHeight;
+ this.targetHeight = targetHeight;
+ this.startWidth = startWidth;
+ this.targetWidth = targetWidth;
+ }
+
+ @Override
+ protected void applyTransformation(final float interpolatedTime, final Transformation t) {
+ // Log.d(TAG, "applyTransformation: interpolatedTime: " + interpolatedTime);
+ view.getLayoutParams().height = (int) (startHeight + (targetHeight - startHeight) * interpolatedTime);
+ view.getLayoutParams().width = (int) (startWidth + (targetWidth - startWidth) * interpolatedTime);
+ view.requestLayout();
+ }
+
+ @Override
+ public void initialize(final int width, final int height, final int parentWidth, final int parentHeight) {
+ super.initialize(width, height, parentWidth, parentHeight);
+ }
+
+ @Override
+ public boolean willChangeBounds() {
+ return true;
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/CommentsFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/CommentsFetcher.java
index 6107a038..7c1a5c43 100755
--- a/app/src/main/java/awais/instagrabber/asyncs/CommentsFetcher.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/CommentsFetcher.java
@@ -12,7 +12,7 @@ import org.json.JSONObject;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
-import java.util.Collections;
+import java.util.List;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
@@ -25,23 +25,20 @@ import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector;
-public final class CommentsFetcher extends AsyncTask {
- private final String shortCode;
- private final FetchListener fetchListener;
+public final class CommentsFetcher extends AsyncTask> {
+ private static final String TAG = "CommentsFetcher";
- /*
- * i fucking spent the whole day on this and fixing all the fucking problems in this class.
- * DO NO FUCK WITH THIS CODE!
- * -AWAiS (The Badak) @the.badak
- */
- public CommentsFetcher(final String shortCode, final FetchListener fetchListener) {
+ private final String shortCode;
+ private final FetchListener> fetchListener;
+
+ public CommentsFetcher(final String shortCode, final FetchListener> fetchListener) {
this.shortCode = shortCode;
this.fetchListener = fetchListener;
}
@NonNull
@Override
- protected CommentModel[] doInBackground(final Void... voids) {
+ protected List doInBackground(final Void... voids) {
/*
"https://www.instagram.com/graphql/query/?query_hash=97b41c52301f77ce508f55e66d17620e&variables=" + "{\"shortcode\":\"" + shortcode + "\",\"first\":50,\"after\":\"" + endCursor + "\"}";
@@ -50,23 +47,20 @@ public final class CommentsFetcher extends AsyncTask
https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables={"comment_id":"18100041898085322","first":50,"after":""}
*/
- final ArrayList commentModels = getParentComments();
-
+ final List commentModels = getParentComments();
for (final CommentModel commentModel : commentModels) {
- final CommentModel[] childCommentModels = commentModel.getChildCommentModels();
+ final List childCommentModels = commentModel.getChildCommentModels();
if (childCommentModels != null) {
- final int childCommentsLen = childCommentModels.length;
-
- final CommentModel lastChild = childCommentModels[childCommentsLen - 1];
+ final int childCommentsLen = childCommentModels.size();
+ final CommentModel lastChild = childCommentModels.get(childCommentsLen - 1);
if (lastChild != null && lastChild.hasNextPage() && !TextUtils.isEmpty(lastChild.getEndCursor())) {
- final CommentModel[] remoteChildComments = getChildComments(commentModel.getId());
+ final List remoteChildComments = getChildComments(commentModel.getId());
commentModel.setChildCommentModels(remoteChildComments);
lastChild.setPageCursor(false, null);
}
}
}
-
- return commentModels.toArray(new CommentModel[0]);
+ return commentModels;
}
@Override
@@ -75,14 +69,13 @@ public final class CommentsFetcher extends AsyncTask
}
@Override
- protected void onPostExecute(final CommentModel[] result) {
+ protected void onPostExecute(final List result) {
if (fetchListener != null) fetchListener.onResult(result);
}
@NonNull
- private synchronized CommentModel[] getChildComments(final String commentId) {
- final ArrayList commentModels = new ArrayList<>();
-
+ private synchronized List getChildComments(final String commentId) {
+ final List commentModels = new ArrayList<>();
String endCursor = "";
while (endCursor != null) {
final String url = "https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables=" +
@@ -96,7 +89,8 @@ public final class CommentsFetcher extends AsyncTask
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) break;
else {
final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
- .getJSONObject("comment").getJSONObject("edge_threaded_comments");
+ .getJSONObject("comment")
+ .getJSONObject("edge_threaded_comments");
final JSONObject pageInfo = data.getJSONObject("page_info");
endCursor = pageInfo.getString("end_cursor");
@@ -110,43 +104,54 @@ public final class CommentsFetcher extends AsyncTask
if (childComment != null) {
final JSONObject owner = childComment.getJSONObject("owner");
- final ProfileModel profileModel = new ProfileModel(false, false, false,
- owner.getString(Constants.EXTRAS_ID),
- owner.getString(Constants.EXTRAS_USERNAME),
- null, null, null,
- owner.getString("profile_pic_url"),
- null, 0, 0, 0, false, false, false, false);
+ final ProfileModel profileModel = new ProfileModel(false,
+ false,
+ false,
+ owner.getString(Constants.EXTRAS_ID),
+ owner.getString(Constants.EXTRAS_USERNAME),
+ null,
+ null,
+ null,
+ owner.getString("profile_pic_url"),
+ null,
+ 0,
+ 0,
+ 0,
+ false,
+ false,
+ false,
+ false);
final JSONObject likedBy = childComment.optJSONObject("edge_liked_by");
commentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
- childComment.getString("text"),
- childComment.getLong("created_at"),
- likedBy != null ? likedBy.optLong("count", 0) : 0,
- childComment.getBoolean("viewer_has_liked"),
- profileModel));
+ childComment.getString("text"),
+ childComment.getLong("created_at"),
+ likedBy != null ? likedBy.optLong("count", 0) : 0,
+ childComment.getBoolean("viewer_has_liked"),
+ profileModel));
}
}
}
}
-
conn.disconnect();
} catch (final Exception e) {
if (logCollector != null)
- logCollector.appendException(e, LogCollector.LogFile.ASYNC_COMMENTS_FETCHER, "getChildComments",
- new Pair<>("commentModels.size", commentModels.size()));
- if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
+ logCollector.appendException(e,
+ LogCollector.LogFile.ASYNC_COMMENTS_FETCHER,
+ "getChildComments",
+ new Pair<>("commentModels.size", commentModels.size()));
+ if (BuildConfig.DEBUG) Log.e(TAG, "", e);
break;
}
}
- return commentModels.toArray(new CommentModel[0]);
+ return commentModels;
}
@NonNull
- private synchronized ArrayList getParentComments() {
- final ArrayList commentModelsList = new ArrayList<>();
-
+ private synchronized List getParentComments() {
+ final List commentModels = new ArrayList<>();
String endCursor = "";
while (endCursor != null) {
final String url = "https://www.instagram.com/graphql/query/?query_hash=bc3296d1ce80a24b1b6e40b1e72903f5&variables=" +
@@ -160,7 +165,9 @@ public final class CommentsFetcher extends AsyncTask
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) break;
else {
final JSONObject parentComments = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
- .getJSONObject("shortcode_media").getJSONObject("edge_media_to_parent_comment");
+ .getJSONObject("shortcode_media")
+ .getJSONObject(
+ "edge_media_to_parent_comment");
final JSONObject pageInfo = parentComments.getJSONObject("page_info");
endCursor = pageInfo.optString("end_cursor");
@@ -182,31 +189,37 @@ public final class CommentsFetcher extends AsyncTask
final JSONArray comments = parentComments.getJSONArray("edges");
final int commentsLen = comments.length();
- final CommentModel[] commentModels = new CommentModel[commentsLen];
-
for (int i = 0; i < commentsLen; ++i) {
final JSONObject comment = comments.getJSONObject(i).getJSONObject("node");
final JSONObject owner = comment.getJSONObject("owner");
- final ProfileModel profileModel = new ProfileModel(false, false,
- owner.optBoolean("is_verified"),
- owner.getString(Constants.EXTRAS_ID),
- owner.getString(Constants.EXTRAS_USERNAME),
- null, null, null,
- owner.getString("profile_pic_url"),
- null, 0, 0, 0, false, false, false, false);
+ final ProfileModel profileModel = new ProfileModel(false,
+ false,
+ owner.optBoolean("is_verified"),
+ owner.getString(Constants.EXTRAS_ID),
+ owner.getString(Constants.EXTRAS_USERNAME),
+ null,
+ null,
+ null,
+ owner.getString("profile_pic_url"),
+ null,
+ 0,
+ 0,
+ 0,
+ false,
+ false,
+ false,
+ false);
final JSONObject likedBy = comment.optJSONObject("edge_liked_by");
final String commentId = comment.getString(Constants.EXTRAS_ID);
- commentModels[i] = new CommentModel(commentId,
- comment.getString("text"),
- comment.getLong("created_at"),
- likedBy != null ? likedBy.optLong("count", 0) : 0,
- comment.getBoolean("viewer_has_liked"),
- profileModel);
-
+ final CommentModel commentModel = new CommentModel(commentId,
+ comment.getString("text"),
+ comment.getLong("created_at"),
+ likedBy != null ? likedBy.optLong("count", 0) : 0,
+ comment.getBoolean("viewer_has_liked"),
+ profileModel);
JSONObject tempJsonObject;
-
final JSONArray childCommentsArray;
final int childCommentsLen;
if ((tempJsonObject = comment.optJSONObject("edge_threaded_comments")) != null &&
@@ -223,47 +236,53 @@ public final class CommentsFetcher extends AsyncTask
hasNextPage = false;
}
- final CommentModel[] childCommentModels = new CommentModel[childCommentsLen];
+ final List childCommentModels = new ArrayList<>();
for (int j = 0; j < childCommentsLen; ++j) {
final JSONObject childComment = childCommentsArray.getJSONObject(j).getJSONObject("node");
tempJsonObject = childComment.getJSONObject("owner");
- final ProfileModel childProfileModel = new ProfileModel(false, false,
- tempJsonObject.optBoolean("is_verified"),
- tempJsonObject.getString(Constants.EXTRAS_ID),
- tempJsonObject.getString(Constants.EXTRAS_USERNAME),
- null, null, null,
- tempJsonObject.getString("profile_pic_url"),
- null, 0, 0, 0, false, false, false, false);
+ final ProfileModel childProfileModel = new ProfileModel(false,
+ false,
+ tempJsonObject.optBoolean("is_verified"),
+ tempJsonObject.getString(Constants.EXTRAS_ID),
+ tempJsonObject.getString(Constants.EXTRAS_USERNAME),
+ null,
+ null,
+ null,
+ tempJsonObject.getString("profile_pic_url"),
+ null,
+ 0,
+ 0,
+ 0,
+ false,
+ false,
+ false,
+ false);
tempJsonObject = childComment.optJSONObject("edge_liked_by");
- childCommentModels[j] = new CommentModel(childComment.getString(Constants.EXTRAS_ID),
- childComment.getString("text"),
- childComment.getLong("created_at"),
- tempJsonObject != null ? tempJsonObject.optLong("count", 0) : 0,
- childComment.getBoolean("viewer_has_liked"),
- childProfileModel);
+ childCommentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
+ childComment.getString("text"),
+ childComment.getLong("created_at"),
+ tempJsonObject != null ? tempJsonObject.optLong("count", 0) : 0,
+ childComment.getBoolean("viewer_has_liked"),
+ childProfileModel));
}
-
- childCommentModels[childCommentsLen - 1].setPageCursor(hasNextPage, childEndCursor);
-
- commentModels[i].setChildCommentModels(childCommentModels);
+ childCommentModels.get(childCommentsLen - 1).setPageCursor(hasNextPage, childEndCursor);
+ commentModel.setChildCommentModels(childCommentModels);
}
+ commentModels.add(commentModel);
}
-
- Collections.addAll(commentModelsList, commentModels);
}
conn.disconnect();
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ASYNC_COMMENTS_FETCHER, "getParentComments",
- new Pair<>("commentModelsList.size", commentModelsList.size()));
+ new Pair<>("commentModelsList.size", commentModels.size()));
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
break;
}
}
-
- return commentModelsList;
+ return commentModels;
}
}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/DiscoverFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/DiscoverFetcher.java
deleted file mode 100755
index f86958aa..00000000
--- a/app/src/main/java/awais/instagrabber/asyncs/DiscoverFetcher.java
+++ /dev/null
@@ -1,202 +0,0 @@
-package awais.instagrabber.asyncs;
-
-import android.os.AsyncTask;
-import android.os.Environment;
-import android.util.Log;
-import android.util.Pair;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-import java.io.File;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.ArrayList;
-
-import awais.instagrabber.BuildConfig;
-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;
-
-import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
-import static awais.instagrabber.utils.Constants.FOLDER_PATH;
-import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
-import static awais.instagrabber.utils.Utils.logCollector;
-import static awais.instagrabber.utils.Utils.settingsHelper;
-
-public final class DiscoverFetcher extends AsyncTask {
- private final String cluster, maxId, rankToken;
- private final FetchListener fetchListener;
- private int lastId = 0;
- private boolean isFirst, moreAvailable;
- private String nextMaxId;
-
- public DiscoverFetcher(final String cluster, final String maxId, final String rankToken,
- final FetchListener fetchListener, final boolean isFirst) {
- this.cluster = cluster == null ? "explore_all%3A0" : cluster.replace(":", "%3A");
- this.maxId = maxId == null ? "" : "&max_id=" + maxId;
- this.rankToken = rankToken;
- this.fetchListener = fetchListener;
- this.isFirst = isFirst;
- }
-
- @Nullable
- @Override
- protected final DiscoverItemModel[] doInBackground(final Void... voids) {
-
- DiscoverItemModel[] result = null;
-
- final ArrayList discoverItemModels = fetchItems(null, maxId);
- if (discoverItemModels != null) {
- result = discoverItemModels.toArray(new DiscoverItemModel[0]);
- if (result.length > 0) {
- final DiscoverItemModel lastModel = result[result.length - 1];
- if (lastModel != null && nextMaxId != null) lastModel.setMore(moreAvailable, nextMaxId);
- }
- }
-
- return result;
- }
-
- private ArrayList fetchItems(ArrayList 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;
-
- final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
-
- urlConnection.setUseCaches(false);
- urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
-
- if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
- final JSONObject discoverResponse = new JSONObject(NetworkUtils.readFromConnection(urlConnection));
-
- moreAvailable = discoverResponse.getBoolean("more_available");
- nextMaxId = discoverResponse.optString("next_max_id");
-
- final JSONArray sectionalItems = discoverResponse.getJSONArray("sectional_items");
- if (discoverItemModels == null) discoverItemModels = new ArrayList<>(sectionalItems.length() * 2);
-
- for (int i = 0; i < sectionalItems.length(); ++i) {
- final JSONObject sectionItem = sectionalItems.getJSONObject(i);
-
- final String feedType = sectionItem.getString("feed_type");
- final String layoutType = sectionItem.getString("layout_type");
-
- if (sectionItem.has("layout_content") && feedType.equals("media")) {
- final JSONObject layoutContent = sectionItem.getJSONObject("layout_content");
-
- if ("media_grid".equals(layoutType)) {
- final JSONArray medias = layoutContent.getJSONArray("medias");
- for (int j = 0; j < medias.length(); ++j)
- discoverItemModels.add(makeDiscoverModel(medias.getJSONObject(j).getJSONObject("media")));
-
- } else {
- final boolean isOneSide = "one_by_two_left".equals(layoutType);
- if (isOneSide || "two_by_two_right".equals(layoutType)) {
-
- final JSONObject layoutItem = layoutContent.getJSONObject(isOneSide ? "one_by_two_item" : "two_by_two_item");
- if (layoutItem.has("media"))
- discoverItemModels.add(makeDiscoverModel(layoutItem.getJSONObject("media")));
-
- if (layoutContent.has("fill_items")) {
- final JSONArray fillItems = layoutContent.getJSONArray("fill_items");
- for (int j = 0; j < fillItems.length(); ++j)
- discoverItemModels.add(makeDiscoverModel(fillItems.getJSONObject(j).getJSONObject("media")));
- }
- }
- }
- }
- }
-
- discoverItemModels.trimToSize();
- urlConnection.disconnect();
-
- // hack to fetch 50+ items
- if (this.isFirst) {
- final int size = discoverItemModels.size();
- if (size > 50) this.isFirst = false;
- discoverItemModels = fetchItems(discoverItemModels, "&max_id=" + (lastId++));
- }
- } else {
- urlConnection.disconnect();
- }
- } 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));
- if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
- }
-
- return discoverItemModels;
- }
-
- @NonNull
- private DiscoverItemModel makeDiscoverModel(@NonNull final JSONObject media) throws Exception {
- final JSONObject user = media.getJSONObject(Constants.EXTRAS_USER);
- final String username = user.getString(Constants.EXTRAS_USERNAME);
- // final ProfileModel userProfileModel = new ProfileModel(user.getBoolean("is_private"),
- // user.getBoolean("is_verified"),
- // String.valueOf(user.get("pk")),
- // username,
- // user.getString("full_name"),
- // null,
- // user.getString("profile_pic_url"), null,
- // 0, 0, 0);
-
- // final String comment;
- // if (!media.has("caption")) comment = null;
- // else {
- // final Object caption = media.get("caption");
- // comment = caption instanceof JSONObject ? ((JSONObject) caption).getString("text") : null;
- // }
-
- final MediaItemType mediaType = ResponseBodyUtils.getMediaItemType(media.getInt("media_type"));
-
- final ResponseBodyUtils.ThumbnailDetails thumbnailUrl = ResponseBodyUtils.getThumbnailUrl(media, mediaType);
- final DiscoverItemModel model = new DiscoverItemModel(mediaType,
- media.getString("pk"),
- media.getString("code"),
- thumbnailUrl != null ? thumbnailUrl.url : null);
-
- final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" +
- (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 (!TextUtils.isEmpty(customPath)) customDir = new File(customPath +
- (Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER)
- ? "/" + username
- : ""));
- }
-
- DownloadUtils.checkExistence(downloadDir, customDir, mediaType == MediaItemType.MEDIA_TYPE_SLIDER, model);
-
- return model;
- }
-
- @Override
- protected void onPreExecute() {
- if (fetchListener != null) fetchListener.doBefore();
- }
-
- @Override
- protected void onPostExecute(final DiscoverItemModel[] discoverItemModels) {
- if (fetchListener != null) fetchListener.onResult(discoverItemModels);
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/asyncs/DiscoverPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/DiscoverPostFetchService.java
new file mode 100644
index 00000000..a8c83dc8
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/asyncs/DiscoverPostFetchService.java
@@ -0,0 +1,56 @@
+package awais.instagrabber.asyncs;
+
+import java.util.List;
+
+import awais.instagrabber.customviews.helpers.PostFetcher;
+import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.models.FeedModel;
+import awais.instagrabber.webservices.DiscoverService;
+import awais.instagrabber.webservices.ServiceCallback;
+
+public class DiscoverPostFetchService implements PostFetcher.PostFetchService {
+ private static final String TAG = "DiscoverPostFetchService";
+ private final DiscoverService discoverService;
+ private final DiscoverService.TopicalExploreRequest topicalExploreRequest;
+ private boolean moreAvailable = false;
+
+ public DiscoverPostFetchService(final DiscoverService.TopicalExploreRequest topicalExploreRequest) {
+ this.topicalExploreRequest = topicalExploreRequest;
+ discoverService = DiscoverService.getInstance();
+ }
+
+ @Override
+ public void fetch(final FetchListener> fetchListener) {
+ discoverService.topicalExplore(topicalExploreRequest, new ServiceCallback() {
+ @Override
+ public void onSuccess(final DiscoverService.TopicalExploreResponse result) {
+ if (result == null) {
+ onFailure(new RuntimeException("result is null"));
+ return;
+ }
+ moreAvailable = result.isMoreAvailable();
+ topicalExploreRequest.setMaxId(result.getNextMaxId());
+ if (fetchListener != null) {
+ fetchListener.onResult(result.getItems());
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ if (fetchListener != null) {
+ fetchListener.onFailure(t);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void reset() {
+ topicalExploreRequest.setMaxId(-1);
+ }
+
+ @Override
+ public boolean hasNextPage() {
+ return moreAvailable;
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/DownloadAsync.java b/app/src/main/java/awais/instagrabber/asyncs/DownloadAsync.java
deleted file mode 100755
index ddea126c..00000000
--- a/app/src/main/java/awais/instagrabber/asyncs/DownloadAsync.java
+++ /dev/null
@@ -1,246 +0,0 @@
-package awais.instagrabber.asyncs;
-
-import android.app.PendingIntent;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.media.MediaMetadataRetriever;
-import android.media.MediaScannerConnection;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Environment;
-import android.util.Log;
-import android.util.Pair;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-import androidx.core.app.NotificationCompat;
-import androidx.core.app.NotificationManagerCompat;
-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.interfaces.FetchListener;
-import awais.instagrabber.utils.Constants;
-import awais.instagrabber.utils.Utils;
-
-import static awais.instagrabber.utils.Constants.NOTIF_GROUP_NAME;
-import static awais.instagrabber.utils.Utils.logCollector;
-import static awaisomereport.LogCollector.LogFile;
-
-public final class DownloadAsync extends AsyncTask {
- private static final String TAG = "DownloadAsync";
-
- private static int lastNotifId = 1;
- private final int currentNotifId;
- private final AtomicReference context;
- private final File outFile;
- private final String url;
- private final FetchListener fetchListener;
- private final Resources resources;
- private final NotificationCompat.Builder downloadNotif;
- private String shortCode, username;
- private final NotificationManagerCompat notificationManager;
-
- public DownloadAsync(@NonNull final Context context,
- final String url,
- final File outFile,
- final FetchListener fetchListener) {
- this.context = new AtomicReference<>(context);
- this.resources = context.getResources();
- this.url = url;
- this.outFile = outFile;
- this.fetchListener = fetchListener;
- this.shortCode = this.username = resources.getString(R.string.downloader_started);
- this.currentNotifId = ++lastNotifId;
- if (++lastNotifId + 1 == Integer.MAX_VALUE) lastNotifId = 1;
-
- @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());
- }
-
- public DownloadAsync setItems(final String shortCode, final String username) {
- this.shortCode = shortCode;
- this.username = username;
- if (downloadNotif != null) downloadNotif.setContentText(this.shortCode == null ? this.username : this.shortCode);
- return this;
- }
-
- @Nullable
- @Override
- protected File doInBackground(final Void... voids) {
- try {
- final URLConnection urlConnection = new URL(url).openConnection();
- final long fileSize = Build.VERSION.SDK_INT >= 24 ? urlConnection.getContentLengthLong() :
- urlConnection.getContentLength();
- float totalRead = 0;
-
- try (final BufferedInputStream bis = new BufferedInputStream(urlConnection.getInputStream());
- final FileOutputStream fos = new FileOutputStream(outFile)) {
- final byte[] buffer = new byte[0x2000];
-
- int count;
- boolean deletedIPTC = false;
- while ((count = bis.read(buffer, 0, 0x2000)) != -1) {
- totalRead = totalRead + count;
-
- if (!deletedIPTC) {
- int iptcStart = -1;
- int fbmdStart = -1;
- int fbmdBytesLen = -1;
-
- for (int i = 0; i < buffer.length; ++i) {
- if (buffer[i] == (byte) 0xFF && buffer[i + 1] == (byte) 0xED)
- iptcStart = i;
- else if (buffer[i] == (byte) 'F' && buffer[i + 1] == (byte) 'B'
- && buffer[i + 2] == (byte) 'M' && buffer[i + 3] == (byte) 'D') {
- fbmdStart = i;
- fbmdBytesLen = buffer[i - 10] << 24 | (buffer[i - 9] & 0xFF) << 16 |
- (buffer[i - 8] & 0xFF) << 8 | (buffer[i - 7] & 0xFF) |
- (buffer[i - 6] & 0xFF);
- break;
- }
- }
-
- if (iptcStart != -1 && fbmdStart != -1 && fbmdBytesLen != -1) {
- final int fbmdDataLen = (iptcStart + (fbmdStart - iptcStart) + (fbmdBytesLen - iptcStart)) - 4;
-
- fos.write(buffer, 0, iptcStart);
- fos.write(buffer, fbmdDataLen + iptcStart, count - fbmdDataLen - iptcStart);
-
- publishProgress(totalRead * 100f / fileSize);
-
- deletedIPTC = true;
- continue;
- }
- }
-
- fos.write(buffer, 0, count);
- publishProgress(totalRead * 100f / fileSize);
- }
- fos.flush();
- }
-
- return outFile;
- } 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(TAG, "", e);
- }
- return null;
- }
-
- @Override
- protected void onPreExecute() {
- if (fetchListener != null) fetchListener.doBefore();
- }
-
- @Override
- protected void onProgressUpdate(@NonNull final Float... values) {
- if (downloadNotif != null) {
- downloadNotif.setProgress(100, values[0].intValue(), false);
- notificationManager.notify(currentNotifId, downloadNotif.build());
- }
- }
-
- @Override
- protected void onPostExecute(final File result) {
- if (result != null) {
- 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()))
- );
- MediaScannerConnection.scanFile(context, new String[]{result.getAbsolutePath()}, null, null);
-
- if (notificationManager != null) {
- 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 (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(TAG, "", e);
- }
- }
-
- if (bitmap == null) {
- final MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- try {
- try {
- retriever.setDataSource(context, uri);
- } catch (final Exception e) {
- retriever.setDataSource(result.getAbsolutePath());
- }
- bitmap = retriever.getFrameAtTime();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
- try {
- retriever.close();
- } catch (final Exception e) {
- if (logCollector != null)
- logCollector.appendException(e, LogFile.ASYNC_DOWNLOADER, "onPostExecute::bitmap_2");
- }
- } catch (final Exception e) {
- if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
- if (logCollector != null)
- logCollector.appendException(e, LogFile.ASYNC_DOWNLOADER, "onPostExecute::bitmap_3");
- }
- }
-
- 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(
- 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)
- .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);
-
- notificationManager.cancel(currentNotifId);
- notificationManager.notify(currentNotifId + 1, downloadNotif.build());
- }
- }
-
- if (fetchListener != null) fetchListener.onResult(result);
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/asyncs/DownloadedCheckerAsyncTask.java b/app/src/main/java/awais/instagrabber/asyncs/DownloadedCheckerAsyncTask.java
new file mode 100644
index 00000000..49e1ae0e
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/asyncs/DownloadedCheckerAsyncTask.java
@@ -0,0 +1,42 @@
+package awais.instagrabber.asyncs;
+
+import android.os.AsyncTask;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import awais.instagrabber.models.FeedModel;
+import awais.instagrabber.utils.DownloadUtils;
+
+public final class DownloadedCheckerAsyncTask extends AsyncTask>> {
+ private static final String TAG = "DownloadedCheckerAsyncTask";
+
+ private final OnCheckResultListener listener;
+
+ public DownloadedCheckerAsyncTask(final OnCheckResultListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ protected Map> doInBackground(final FeedModel... feedModels) {
+ if (feedModels == null) {
+ return null;
+ }
+ final Map> map = new HashMap<>();
+ for (final FeedModel feedModel : feedModels) {
+ map.put(feedModel.getPostId(), DownloadUtils.checkDownloaded(feedModel));
+ }
+ return map;
+ }
+
+ @Override
+ protected void onPostExecute(final Map> result) {
+ if (listener == null) return;
+ listener.onResult(result);
+ }
+
+ public interface OnCheckResultListener {
+ void onResult(final Map> result);
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/FeedFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/FeedFetcher.java
deleted file mode 100755
index 855f7f17..00000000
--- a/app/src/main/java/awais/instagrabber/asyncs/FeedFetcher.java
+++ /dev/null
@@ -1,243 +0,0 @@
-package awais.instagrabber.asyncs;
-
-import android.os.AsyncTask;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.ArrayList;
-
-import awais.instagrabber.BuildConfig;
-import awais.instagrabber.interfaces.FetchListener;
-import awais.instagrabber.models.FeedModel;
-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.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 {
- 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 fetchListener;
-
- public FeedFetcher(final FetchListener fetchListener) {
- this.endCursor = "";
- this.fetchListener = fetchListener;
- }
-
- public FeedFetcher(final String endCursor, final FetchListener fetchListener) {
- this.endCursor = endCursor == null ? "" : endCursor;
- this.fetchListener = fetchListener;
- }
-
- @Nullable
- @Override
- protected final FeedModel[] doInBackground(final Void... voids) {
- FeedModel[] result = null;
- try {
- //
- // stories: 04334405dbdef91f2c4e207b84c204d7 && https://i.instagram.com/api/v1/feed/reels_tray/
- // https://www.instagram.com/graphql/query/?query_hash=04334405dbdef91f2c4e207b84c204d7&variables={"only_stories":true,"stories_prefetch":false,"stories_video_dash_manifest":false}
- // ///////////////////////////////////////////////
- // feed:
- // https://www.instagram.com/graphql/query/?query_hash=6b838488258d7a4820e48d209ef79eb1&variables=
- // {"cached_feed_item_ids":[],"fetch_media_item_count":12,"fetch_media_item_cursor":"","fetch_comment_count":4,"fetch_like":3,"has_stories":false,"has_threaded_comments":true}
- // only used: fetch_media_item_cursor, fetch_media_item_count: 100 (max 50), has_threaded_comments = true
- // //////////////////////////////////////////////
- // more unknowns: https://github.com/qsniyg/rssit/blob/master/rssit/generators/instagram.py
- //
-
- final String url = "https://www.instagram.com/graphql/query/?query_hash=6b838488258d7a4820e48d209ef79eb1&variables=" +
- "{\"fetch_media_item_count\":" + maxItemsToLoad + ",\"has_threaded_comments\":true,\"fetch_media_item_cursor\":\"" + endCursor + "\"}";
- final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
-
- if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
- 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;
-
- final JSONObject pageInfo = timelineFeed.getJSONObject("page_info");
- if (pageInfo.has("has_next_page")) {
- hasNextPage = pageInfo.getBoolean("has_next_page");
- endCursor = hasNextPage ? pageInfo.getString("end_cursor") : null;
- } else {
- hasNextPage = false;
- endCursor = null;
- }
-
- final JSONArray feedItems = timelineFeed.getJSONArray("edges");
-
- final int feedLen = feedItems.length();
- final ArrayList feedModelsList = new ArrayList<>(feedLen);
- 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;
-
- final boolean isVideo = feedItem.optBoolean("is_video");
- final long videoViews = feedItem.optLong("video_view_count", 0);
-
- final String displayUrl = feedItem.optString("display_url");
- if (TextUtils.isEmpty(displayUrl)) continue;
- final String resourceUrl;
-
- if (isVideo) resourceUrl = feedItem.getString("video_url");
- 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"),
- 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,
- owner.getString("profile_pic_url"),
- null,
- 0,
- 0,
- 0,
- false,
- false,
- false,
- false);
- }
-
- JSONObject tempJsonObject = feedItem.optJSONObject("edge_media_preview_comment");
- final long commentsCount = tempJsonObject != null ? tempJsonObject.optLong("count") : 0;
-
- tempJsonObject = feedItem.optJSONObject("edge_media_to_caption");
- final JSONArray captions = tempJsonObject != null ? tempJsonObject.getJSONArray("edges") : null;
-
- String captionText = null;
- if (captions != null && captions.length() > 0) {
- if ((tempJsonObject = captions.optJSONObject(0)) != null &&
- (tempJsonObject = tempJsonObject.optJSONObject("node")) != null)
- captionText = tempJsonObject.getString("text");
- }
-
- 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),
- resourceUrl,
- displayUrl,
- feedItem.getString(Constants.EXTRAS_SHORTCODE),
- captionText,
- commentsCount,
- feedItem.optLong("taken_at_timestamp", -1),
- feedItem.getBoolean("viewer_has_liked"),
- feedItem.getBoolean("viewer_has_saved"),
- feedItem.getJSONObject("edge_media_preview_like").getLong("count"),
- locationName,
- locationId);
-
- final boolean isSlider = "GraphSidecar".equals(mediaType) && feedItem.has("edge_sidecar_to_children");
-
- if (isSlider) {
- final JSONObject sidecar = feedItem.optJSONObject("edge_sidecar_to_children");
- if (sidecar != null) {
- final JSONArray children = sidecar.optJSONArray("edges");
-
- if (children != null) {
- final ViewerPostModel[] sliderItems = new ViewerPostModel[children.length()];
-
- for (int j = 0; j < sliderItems.length; ++j) {
- final JSONObject node = children.optJSONObject(j).getJSONObject("node");
- final boolean isChildVideo = node.optBoolean("is_video");
-
- sliderItems[j] = new ViewerPostModel(
- isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
- node.getString(Constants.EXTRAS_ID),
- 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"),
- locationName,
- locationId);
-
- sliderItems[j].setSliderDisplayUrl(node.getString("display_url"));
- }
-
- feedModel.setSliderItems(sliderItems);
- }
- }
- }
-
- feedModelsList.add(feedModel);
- }
-
- feedModelsList.trimToSize();
-
- final FeedModel[] feedModels = feedModelsList.toArray(new FeedModel[0]);
- final int length = feedModels.length;
- if (length >= 1 && feedModels[length - 1] != null) {
- feedModels[length - 1].setPageCursor(hasNextPage, endCursor);
- }
- result = feedModels;
- }
-
- urlConnection.disconnect();
- } catch (final Exception e) {
- if (logCollector != null)
- logCollector.appendException(e, LogCollector.LogFile.ASYNC_FEED_FETCHER, "doInBackground");
- if (BuildConfig.DEBUG) {
- Log.e(TAG, "", e);
- }
- }
-
- return result;
- }
-
- @Override
- protected void onPreExecute() {
- if (fetchListener != null) fetchListener.doBefore();
- }
-
- @Override
- protected void onPostExecute(final FeedModel[] postModels) {
- if (fetchListener != null) fetchListener.onResult(postModels);
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java
new file mode 100644
index 00000000..d2bba0ce
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java
@@ -0,0 +1,54 @@
+package awais.instagrabber.asyncs;
+
+import java.util.List;
+
+import awais.instagrabber.customviews.helpers.PostFetcher;
+import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.models.FeedModel;
+import awais.instagrabber.repositories.responses.PostsFetchResponse;
+import awais.instagrabber.webservices.FeedService;
+import awais.instagrabber.webservices.ServiceCallback;
+
+public class FeedPostFetchService implements PostFetcher.PostFetchService {
+ private static final String TAG = "FeedPostFetchService";
+ private final FeedService feedService;
+ private String nextCursor;
+ private boolean hasNextPage;
+
+ public FeedPostFetchService() {
+ feedService = FeedService.getInstance();
+ }
+
+ @Override
+ public void fetch(final FetchListener> fetchListener) {
+ feedService.fetch(25, nextCursor, new ServiceCallback() {
+ @Override
+ public void onSuccess(final PostsFetchResponse result) {
+ if (result == null) return;
+ nextCursor = result.getNextCursor();
+ hasNextPage = result.hasNextPage();
+ if (fetchListener != null) {
+ fetchListener.onResult(result.getFeedModels());
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ // Log.e(TAG, "onFailure: ", t);
+ if (fetchListener != null) {
+ fetchListener.onFailure(t);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void reset() {
+ nextCursor = null;
+ }
+
+ @Override
+ public boolean hasNextPage() {
+ return hasNextPage;
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java
new file mode 100644
index 00000000..b341eb4a
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java
@@ -0,0 +1,56 @@
+package awais.instagrabber.asyncs;
+
+import java.util.List;
+
+import awais.instagrabber.customviews.helpers.PostFetcher;
+import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.models.FeedModel;
+import awais.instagrabber.models.HashtagModel;
+import awais.instagrabber.webservices.ServiceCallback;
+import awais.instagrabber.webservices.TagsService;
+import awais.instagrabber.webservices.TagsService.TagPostsFetchResponse;
+
+public class HashtagPostFetchService implements PostFetcher.PostFetchService {
+ private final TagsService tagsService;
+ private final HashtagModel hashtagModel;
+ private String nextMaxId;
+ private boolean moreAvailable;
+
+ public HashtagPostFetchService(final HashtagModel hashtagModel) {
+ this.hashtagModel = hashtagModel;
+ tagsService = TagsService.getInstance();
+ }
+
+ @Override
+ public void fetch(final FetchListener> fetchListener) {
+ tagsService.fetchPosts(hashtagModel.getName().toLowerCase(), nextMaxId, new ServiceCallback() {
+ @Override
+ public void onSuccess(final TagPostsFetchResponse result) {
+ if (result == null) return;
+ nextMaxId = result.getNextMaxId();
+ moreAvailable = result.isMoreAvailable();
+ if (fetchListener != null) {
+ fetchListener.onResult(result.getItems());
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ // Log.e(TAG, "onFailure: ", t);
+ if (fetchListener != null) {
+ fetchListener.onFailure(t);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void reset() {
+ nextMaxId = null;
+ }
+
+ @Override
+ public boolean hasNextPage() {
+ return moreAvailable;
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java
new file mode 100644
index 00000000..124b3f3f
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java
@@ -0,0 +1,56 @@
+package awais.instagrabber.asyncs;
+
+import java.util.List;
+
+import awais.instagrabber.customviews.helpers.PostFetcher;
+import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.models.FeedModel;
+import awais.instagrabber.models.LocationModel;
+import awais.instagrabber.webservices.LocationService;
+import awais.instagrabber.webservices.LocationService.LocationPostsFetchResponse;
+import awais.instagrabber.webservices.ServiceCallback;
+
+public class LocationPostFetchService implements PostFetcher.PostFetchService {
+ private final LocationService locationService;
+ private final LocationModel locationModel;
+ private String nextMaxId;
+ private boolean moreAvailable;
+
+ public LocationPostFetchService(final LocationModel locationModel) {
+ this.locationModel = locationModel;
+ locationService = LocationService.getInstance();
+ }
+
+ @Override
+ public void fetch(final FetchListener> fetchListener) {
+ locationService.fetchPosts(locationModel.getId(), nextMaxId, new ServiceCallback() {
+ @Override
+ public void onSuccess(final LocationPostsFetchResponse result) {
+ if (result == null) return;
+ nextMaxId = result.getNextMaxId();
+ moreAvailable = result.isMoreAvailable();
+ if (fetchListener != null) {
+ fetchListener.onResult(result.getItems());
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ // Log.e(TAG, "onFailure: ", t);
+ if (fetchListener != null) {
+ fetchListener.onFailure(t);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void reset() {
+ nextMaxId = null;
+ }
+
+ @Override
+ public boolean hasNextPage() {
+ return moreAvailable;
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java
index 71b69c54..39ba0d9e 100755
--- a/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java
@@ -1,51 +1,48 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
-import android.os.Environment;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONObject;
-import java.io.File;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.models.FeedModel;
+import awais.instagrabber.models.PostChild;
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;
-import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
-import static awais.instagrabber.utils.Constants.FOLDER_PATH;
-import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
import static awais.instagrabber.utils.Utils.logCollector;
-public final class PostFetcher extends AsyncTask {
+public final class PostFetcher extends AsyncTask {
private static final String TAG = "PostFetcher";
private final String shortCode;
- private final FetchListener fetchListener;
+ private final FetchListener fetchListener;
- public PostFetcher(final String shortCode, final FetchListener fetchListener) {
+ public PostFetcher(final String shortCode, final FetchListener fetchListener) {
this.shortCode = shortCode;
this.fetchListener = fetchListener;
}
@Override
- protected ViewerPostModel[] doInBackground(final Void... voids) {
- ViewerPostModel[] result = null;
+ protected FeedModel doInBackground(final Void... voids) {
CookieUtils.setupCookies(Utils.settingsHelper.getString(Constants.COOKIE)); // <- direct download
+ HttpURLConnection conn = null;
try {
- final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/p/" + shortCode + "/?__a=1").openConnection();
+ conn = (HttpURLConnection) new URL("https://www.instagram.com/p/" + shortCode + "/?__a=1").openConnection();
conn.setUseCaches(false);
conn.connect();
@@ -53,7 +50,6 @@ public final class PostFetcher extends AsyncTask
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");
@@ -77,19 +73,6 @@ public final class PostFetcher extends AsyncTask
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) : ""));
- 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 (!TextUtils.isEmpty(customPath)) customDir = new File(customPath);
- }
-
final long timestamp = media.getLong("taken_at_timestamp");
final boolean isVideo = media.has("is_video") && media.optBoolean("is_video");
@@ -111,75 +94,67 @@ public final class PostFetcher extends AsyncTask
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) {
- endCursor = commentObject.optString("end_cursor");
- }
-
- if (mediaItemType != MediaItemType.MEDIA_TYPE_SLIDER) {
- final ViewerPostModel postModel = new ViewerPostModel(
- mediaItemType,
- media.getString(Constants.EXTRAS_ID),
- isVideo ? media.getString("video_url") : ResponseBodyUtils.getHighQualityImage(media),
- shortCode,
- 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("slug")));
-
- postModel.setCommentsCount(commentsCount);
-
- DownloadUtils.checkExistence(downloadDir, customDir, false, postModel);
-
- result = new ViewerPostModel[]{postModel};
-
- } else {
+ final FeedModel.Builder feedModelBuilder = new FeedModel.Builder()
+ .setItemType(mediaItemType)
+ .setPostId(media.getString(Constants.EXTRAS_ID))
+ .setDisplayUrl(isVideo ? media.getString("video_url")
+ : ResponseBodyUtils.getHighQualityImage(media))
+ .setThumbnailUrl(media.getString("display_url"))
+ .setImageHeight(media.getJSONObject("dimensions").getInt("height"))
+ .setImageWidth(media.getJSONObject("dimensions").getInt("width"))
+ .setShortCode(shortCode)
+ .setPostCaption(TextUtils.isEmpty(postCaption) ? null : postCaption)
+ .setProfileModel(profileModel)
+ .setViewCount(isVideo && media.has("video_view_count")
+ ? media.getLong("video_view_count")
+ : -1)
+ .setTimestamp(timestamp)
+ .setLiked(media.getBoolean("viewer_has_liked"))
+ .setBookmarked(media.getBoolean("viewer_has_saved"))
+ .setLikesCount(media.getJSONObject("edge_media_preview_like")
+ .getLong("count"))
+ .setLocationName(media.isNull("location")
+ ? null
+ : media.getJSONObject("location").optString("name"))
+ .setLocationId(media.isNull("location")
+ ? null
+ : media.getJSONObject("location").optString("id"))
+ .setCommentsCount(commentsCount);
+ if (isSlider) {
final JSONArray children = media.getJSONObject("edge_sidecar_to_children").getJSONArray("edges");
- final ViewerPostModel[] postModels = new ViewerPostModel[children.length()];
-
- for (int i = 0; i < postModels.length; ++i) {
- 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,
- media.getString(Constants.EXTRAS_ID),
- isChildVideo ? node.getString("video_url") : ResponseBodyUtils.getHighQualityImage(node),
- node.getString(Constants.EXTRAS_SHORTCODE),
- postCaption,
- 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")));
- postModels[i].setSliderDisplayUrl(node.getString("display_url"));
-
- DownloadUtils.checkExistence(downloadDir, customDir, true, postModels[i]);
+ final List postModels = new ArrayList<>();
+ for (int i = 0; i < children.length(); ++i) {
+ final JSONObject childNode = children.getJSONObject(i).getJSONObject("node");
+ final boolean isChildVideo = childNode.getBoolean("is_video");
+ postModels.add(new PostChild.Builder()
+ .setItemType(isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO
+ : MediaItemType.MEDIA_TYPE_IMAGE)
+ .setDisplayUrl(isChildVideo ? childNode.getString("video_url")
+ : childNode.getString("display_url"))
+ .setShortCode(media.getString(Constants.EXTRAS_SHORTCODE))
+ .setVideoViews(isChildVideo && childNode.has("video_view_count")
+ ? childNode.getLong("video_view_count")
+ : -1)
+ .setThumbnailUrl(childNode.getString("display_url"))
+ .setHeight(childNode.getJSONObject("dimensions").getInt("height"))
+ .setWidth(childNode.getJSONObject("dimensions").getInt("width"))
+ .build());
}
-
- postModels[0].setCommentsCount(commentsCount);
-
- result = postModels;
+ feedModelBuilder.setSliderItems(postModels);
}
+ return feedModelBuilder.build();
}
-
- conn.disconnect();
} catch (Exception e) {
if (logCollector != null) {
logCollector.appendException(e, LogCollector.LogFile.ASYNC_POST_FETCHER, "doInBackground");
}
Log.e(TAG, "Error fetching post", e);
+ } finally {
+ if (conn != null) {
+ conn.disconnect();
+ }
}
- return result;
+ return null;
}
@Override
@@ -188,7 +163,7 @@ public final class PostFetcher extends AsyncTask
}
@Override
- protected void onPostExecute(final ViewerPostModel[] postModels) {
- if (fetchListener != null) fetchListener.onResult(postModels);
+ protected void onPostExecute(final FeedModel feedModel) {
+ if (fetchListener != null) fetchListener.onResult(feedModel);
}
}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/PostsFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/PostsFetcher.java
deleted file mode 100755
index 6d7eeaa4..00000000
--- a/app/src/main/java/awais/instagrabber/asyncs/PostsFetcher.java
+++ /dev/null
@@ -1,182 +0,0 @@
-package awais.instagrabber.asyncs;
-
-import android.os.AsyncTask;
-import android.os.Environment;
-import android.util.Log;
-
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-import java.io.File;
-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.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;
-
-import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
-import static awais.instagrabber.utils.Constants.FOLDER_PATH;
-import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
-import static awais.instagrabber.utils.Utils.logCollector;
-
-public final class PostsFetcher extends AsyncTask> {
- private static final String TAG = "PostsFetcher";
- private final PostItemType type;
- private final String endCursor;
- private final String id;
- private final FetchListener> fetchListener;
- private String username = null;
-
- public PostsFetcher(final String id,
- final PostItemType type,
- final String endCursor,
- final FetchListener> fetchListener) {
- this.id = id;
- this.type = type;
- this.endCursor = endCursor == null ? "" : endCursor;
- this.fetchListener = fetchListener;
- }
-
- public PostsFetcher setUsername(final String username) {
- this.username = username;
- return this;
- }
-
- @Override
- protected List 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 String url;
- 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_hash=18a7b935ab438c4514b1f742d8fa07a7&variables=" +
- "{\"id\":\"" + id + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
- }
- List result = new ArrayList<>();
- try {
- final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
- conn.setUseCaches(false);
- conn.connect();
-
- 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) : ""));
- 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 (!TextUtils.isEmpty(customPath)) customDir = new File(customPath);
- }
-
- 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;
-
- final JSONObject pageInfo = mediaPosts.getJSONObject("page_info");
- if (pageInfo.has("has_next_page")) {
- hasNextPage = pageInfo.getBoolean("has_next_page");
- endCursor = hasNextPage ? pageInfo.getString("end_cursor") : null;
- } else {
- hasNextPage = false;
- endCursor = null;
- }
-
- final JSONArray edges = mediaPosts.getJSONArray("edges");
- for (int i = 0; i < edges.length(); ++i) {
- final JSONObject mediaNode = edges.getJSONObject(i).getJSONObject("node");
- final JSONArray captions = mediaNode.getJSONObject("edge_media_to_caption").getJSONArray("edges");
-
- final boolean isSlider = mediaNode.has("__typename") && mediaNode.getString("__typename").equals("GraphSidecar");
- final boolean isVideo = mediaNode.getBoolean("is_video");
-
- final MediaItemType itemType;
- if (isSlider) itemType = MediaItemType.MEDIA_TYPE_SLIDER;
- else if (isVideo) itemType = MediaItemType.MEDIA_TYPE_VIDEO;
- else itemType = MediaItemType.MEDIA_TYPE_IMAGE;
-
- final PostModel model = 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.isNull("edge_liked_by") ? 0 : mediaNode.getJSONObject("edge_liked_by").getLong("count")
- );
- result.add(model);
- DownloadUtils.checkExistence(downloadDir, customDir, isSlider, model);
- }
-
- if (!result.isEmpty() && result.get(result.size() - 1) != null)
- result.get(result.size() - 1).setPageCursor(hasNextPage, endCursor);
- }
- conn.disconnect();
- } catch (Exception e) {
- if (logCollector != null) {
- logCollector.appendException(e, LogCollector.LogFile.ASYNC_MAIN_POSTS_FETCHER, "doInBackground");
- }
- if (BuildConfig.DEBUG) {
- Log.e(TAG, "Error fetching posts", e);
- }
- }
- return result;
- }
-
- @Override
- protected void onPostExecute(final List postModels) {
- if (fetchListener != null) fetchListener.onResult(postModels);
- }
-}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java
new file mode 100644
index 00000000..3617cbd2
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java
@@ -0,0 +1,57 @@
+package awais.instagrabber.asyncs;
+
+import java.util.List;
+
+import awais.instagrabber.customviews.helpers.PostFetcher;
+import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.models.FeedModel;
+import awais.instagrabber.models.ProfileModel;
+import awais.instagrabber.repositories.responses.PostsFetchResponse;
+import awais.instagrabber.webservices.ProfileService;
+import awais.instagrabber.webservices.ServiceCallback;
+
+public class ProfilePostFetchService implements PostFetcher.PostFetchService {
+ private static final String TAG = "ProfilePostFetchService";
+ private final ProfileService profileService;
+ private final ProfileModel profileModel;
+ private String nextCursor;
+ private boolean hasNextPage;
+
+ public ProfilePostFetchService(final ProfileModel profileModel) {
+ this.profileModel = profileModel;
+ profileService = ProfileService.getInstance();
+ }
+
+ @Override
+ public void fetch(final FetchListener> fetchListener) {
+ profileService.fetchPosts(profileModel, 30, nextCursor, new ServiceCallback() {
+ @Override
+ public void onSuccess(final PostsFetchResponse result) {
+ if (result == null) return;
+ nextCursor = result.getNextCursor();
+ hasNextPage = result.hasNextPage();
+ if (fetchListener != null) {
+ fetchListener.onResult(result.getFeedModels());
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ // Log.e(TAG, "onFailure: ", t);
+ if (fetchListener != null) {
+ fetchListener.onFailure(t);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void reset() {
+ nextCursor = null;
+ }
+
+ @Override
+ public boolean hasNextPage() {
+ return hasNextPage;
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java
new file mode 100644
index 00000000..36c668d1
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java
@@ -0,0 +1,71 @@
+package awais.instagrabber.asyncs;
+
+import java.util.List;
+
+import awais.instagrabber.customviews.helpers.PostFetcher;
+import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.models.FeedModel;
+import awais.instagrabber.models.enums.PostItemType;
+import awais.instagrabber.webservices.ProfileService;
+import awais.instagrabber.webservices.ProfileService.SavedPostsFetchResponse;
+import awais.instagrabber.webservices.ServiceCallback;
+
+public class SavedPostFetchService implements PostFetcher.PostFetchService {
+ private final ProfileService profileService;
+ private final String profileId;
+ private final PostItemType type;
+
+ private String nextMaxId;
+ private boolean moreAvailable;
+
+ public SavedPostFetchService(final String profileId, final PostItemType type) {
+ this.profileId = profileId;
+ this.type = type;
+ profileService = ProfileService.getInstance();
+ }
+
+ @Override
+ public void fetch(final FetchListener> fetchListener) {
+ final ServiceCallback callback = new ServiceCallback() {
+ @Override
+ public void onSuccess(final SavedPostsFetchResponse result) {
+ if (result == null) return;
+ nextMaxId = result.getNextMaxId();
+ moreAvailable = result.isMoreAvailable();
+ if (fetchListener != null) {
+ fetchListener.onResult(result.getItems());
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ // Log.e(TAG, "onFailure: ", t);
+ if (fetchListener != null) {
+ fetchListener.onFailure(t);
+ }
+ }
+ };
+ switch (type) {
+ case LIKED:
+ profileService.fetchLiked(nextMaxId, callback);
+ break;
+ case TAGGED:
+ profileService.fetchTagged(profileId, nextMaxId, callback);
+ break;
+ case SAVED:
+ default:
+ profileService.fetchSaved(nextMaxId, callback);
+ break;
+ }
+ }
+
+ @Override
+ public void reset() {
+ nextMaxId = null;
+ }
+
+ @Override
+ public boolean hasNextPage() {
+ return moreAvailable;
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/i/iLikedFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/i/iLikedFetcher.java
deleted file mode 100755
index 58aec1ab..00000000
--- a/app/src/main/java/awais/instagrabber/asyncs/i/iLikedFetcher.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package awais.instagrabber.asyncs.i;
-
-import android.os.AsyncTask;
-import android.os.Environment;
-import android.util.Log;
-
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-import java.io.File;
-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.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;
-
-import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
-import static awais.instagrabber.utils.Constants.FOLDER_PATH;
-import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
-import static awais.instagrabber.utils.Utils.logCollector;
-
-public final class iLikedFetcher extends AsyncTask> {
- private static final String TAG = "iLikedFetcher";
-
- private final String endCursor;
- private final FetchListener> fetchListener;
-
- public iLikedFetcher(final FetchListener> fetchListener) {
- this.endCursor = "";
- this.fetchListener = fetchListener;
- }
-
- public iLikedFetcher(final String endCursor, final FetchListener> fetchListener) {
- this.endCursor = endCursor == null ? "" : endCursor;
- this.fetchListener = fetchListener;
- }
-
- @Override
- protected List doInBackground(final Void... voids) {
- final String url = "https://i.instagram.com/api/v1/feed/liked/?max_id=" + endCursor;
-
- List result = new ArrayList<>();
- try {
- final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
- conn.setUseCaches(false);
- conn.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
- conn.connect();
-
- if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
- final JSONObject body = new JSONObject(NetworkUtils.readFromConnection(conn));
-
- final String endCursor;
- final boolean hasNextPage;
-
- if (body.has("more_available")) {
- hasNextPage = body.optBoolean("more_available");
- endCursor = hasNextPage ? body.optString("next_max_id") : null;
- } else {
- hasNextPage = false;
- endCursor = null;
- }
-
- final JSONArray edges = body.getJSONArray("items");
- for (int i = 0; i < edges.length(); ++i) {
- final JSONObject mediaNode = edges.getJSONObject(i);
-
- final boolean isSlider = mediaNode.has("carousel_media_count");
- final boolean isVideo = mediaNode.has("video_duration");
-
- final MediaItemType itemType;
- if (isSlider) itemType = MediaItemType.MEDIA_TYPE_SLIDER;
- else if (isVideo) itemType = MediaItemType.MEDIA_TYPE_VIDEO;
- else itemType = MediaItemType.MEDIA_TYPE_IMAGE;
-
- final PostModel model = new PostModel(
- itemType,
- mediaNode.getString(Constants.EXTRAS_ID),
- 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,
- mediaNode.optBoolean("has_viewer_saved"),
- mediaNode.getLong("like_count"));
- result.add(model);
- String username = mediaNode.getJSONObject("user").getString("username");
- final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" +
- (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 (!TextUtils.isEmpty(customPath)) customDir = new File(customPath);
- }
- DownloadUtils.checkExistence(downloadDir, customDir, isSlider, model);
- }
-
- final int length = result.size();
- if (length >= 1 && result.get(length - 1) != null) {
- result.get(length - 1).setPageCursor(hasNextPage, endCursor);
- }
- }
-
- conn.disconnect();
- } catch (Exception e) {
- if (logCollector != null) {
- logCollector.appendException(e, LogCollector.LogFile.ASYNC_MAIN_POSTS_FETCHER, "doInBackground");
- }
- if (BuildConfig.DEBUG) {
- Log.e(TAG, "", e);
- }
- }
-
- return result;
- }
-
- @Override
- protected void onPostExecute(final List postModels) {
- if (fetchListener != null) fetchListener.onResult(postModels);
- }
-}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/i/iPostFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/i/iPostFetcher.java
deleted file mode 100755
index dae6f368..00000000
--- a/app/src/main/java/awais/instagrabber/asyncs/i/iPostFetcher.java
+++ /dev/null
@@ -1,207 +0,0 @@
-package awais.instagrabber.asyncs.i;
-
-import android.os.AsyncTask;
-import android.os.Environment;
-import android.util.Log;
-
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-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.DownloadUtils;
-import awais.instagrabber.utils.NetworkUtils;
-import awais.instagrabber.utils.ResponseBodyUtils;
-import awais.instagrabber.utils.TextUtils;
-import awais.instagrabber.utils.Utils;
-import awaisomereport.LogCollector;
-
-import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
-import static awais.instagrabber.utils.Constants.FOLDER_PATH;
-import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
-import static awais.instagrabber.utils.Utils.logCollector;
-
-public final class iPostFetcher extends AsyncTask {
- private static final String TAG = "iPostFetcher";
-
- private final String id;
- private final FetchListener fetchListener;
-
- public iPostFetcher(final String id, final FetchListener fetchListener) {
- this.id = id;
- this.fetchListener = fetchListener;
- }
-
- @Override
- protected ViewerPostModel[] doInBackground(final Void... voids) {
- ViewerPostModel[] result = null;
- try {
- final HttpURLConnection conn = (HttpURLConnection) new URL("https://i.instagram.com/api/v1/media/" + id + "/info").openConnection();
- conn.setUseCaches(false);
- conn.setRequestProperty("User-Agent", Constants.USER_AGENT);
- conn.connect();
-
- if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
- 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 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)
- + (shouldDownloadToUserFolder ? "/" + profileModel.getUsername() : "");
- if (!TextUtils.isEmpty(customPath)) customDir = new File(customPath);
- }
-
- final long timestamp = media.getLong("taken_at");
-
- final boolean isVideo = media.has("has_audio") && media.optBoolean("has_audio");
- final boolean isSlider = !media.isNull("carousel_media_count");
-
- final MediaItemType mediaItemType;
- if (isSlider) mediaItemType = MediaItemType.MEDIA_TYPE_SLIDER;
- else if (isVideo) mediaItemType = MediaItemType.MEDIA_TYPE_VIDEO;
- else mediaItemType = MediaItemType.MEDIA_TYPE_IMAGE;
-
- final String postCaption;
- final JSONObject mediaToCaption = media.optJSONObject("caption");
- if (mediaToCaption == null) postCaption = null;
- else postCaption = mediaToCaption.optString("text");
-
- 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,
- media.getString(Constants.EXTRAS_ID),
- isVideo ? ResponseBodyUtils.getHighQualityPost(media.optJSONArray("video_versions"), true, true, false)
- : ResponseBodyUtils.getHighQualityImage(media),
- media.getString("code"),
- 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"),
- media.getLong("like_count"),
- locationName,
- locationId);
-
- postModel.setCommentsCount(commentsCount);
-
- DownloadUtils.checkExistence(downloadDir, customDir, false, postModel);
-
- result = new ViewerPostModel[]{postModel};
-
- } else {
- final JSONArray children = media.getJSONArray("carousel_media");
- final ViewerPostModel[] postModels = new ViewerPostModel[children.length()];
-
- for (int i = 0; i < postModels.length; ++i) {
- 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,
- media.getString(Constants.EXTRAS_ID),
- isChildVideo ? ResponseBodyUtils.getHighQualityPost(node.optJSONArray("video_versions"), true, true, false)
- : ResponseBodyUtils.getHighQualityImage(node),
- media.getString("code"),
- postCaption,
- profileModel,
- -1,
- timestamp, media.optBoolean("has_liked"),
- media.optBoolean("has_viewer_saved"),
- media.getLong("like_count"),
- locationName,
- locationId);
- postModels[i].setSliderDisplayUrl(ResponseBodyUtils.getHighQualityImage(node));
- DownloadUtils.checkExistence(downloadDir, customDir, true, postModels[i]);
- }
-
- postModels[0].setCommentsCount(commentsCount);
- result = postModels;
- }
- }
-
- conn.disconnect();
- } catch (Exception e) {
- if (logCollector != null)
- logCollector.appendException(e, LogCollector.LogFile.ASYNC_POST_FETCHER, "doInBackground (i)");
- if (BuildConfig.DEBUG) {
- Log.e(TAG, "", e);
- }
- }
- return result;
- }
-
- @Override
- protected void onPreExecute() {
- if (fetchListener != null) fetchListener.doBefore();
- }
-
- @Override
- protected void onPostExecute(final ViewerPostModel[] postModels) {
- if (fetchListener != null) fetchListener.onResult(postModels);
- }
-}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/i/iTopicFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/i/iTopicFetcher.java
deleted file mode 100755
index 1fe62a4c..00000000
--- a/app/src/main/java/awais/instagrabber/asyncs/i/iTopicFetcher.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package awais.instagrabber.asyncs.i;
-
-import android.os.AsyncTask;
-import android.util.Log;
-
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-import java.net.HttpURLConnection;
-import java.net.URL;
-
-import awais.instagrabber.BuildConfig;
-import awais.instagrabber.interfaces.FetchListener;
-import awais.instagrabber.models.DiscoverTopicModel;
-import awais.instagrabber.utils.Constants;
-import awais.instagrabber.utils.LocaleUtils;
-import awais.instagrabber.utils.NetworkUtils;
-import awaisomereport.LogCollector;
-
-import static awais.instagrabber.utils.Utils.logCollector;
-
-public final class iTopicFetcher extends AsyncTask {
- private final FetchListener fetchListener;
-
- public iTopicFetcher(final FetchListener fetchListener) {
- this.fetchListener = fetchListener;
- }
-
- @Override
- protected DiscoverTopicModel doInBackground(final Void... voids) {
- final String url = "https://i.instagram.com/api/v1/discover/topical_explore/";
-
- DiscoverTopicModel result = null;
- try {
- final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
- conn.setUseCaches(false);
- conn.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
- conn.setRequestProperty("Accept-Language", LocaleUtils.getCurrentLocale().getLanguage() + ",en-US;q=0.8");
- conn.connect();
-
- if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
- 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()];
- for (int i = 0; i < names.length; ++i) {
- final JSONObject mediaNode = edges.getJSONObject(i);
- ids[i] = mediaNode.getString("id");
- names[i] = mediaNode.getString("title");
- }
-
- result = new DiscoverTopicModel(ids, names, body.getString("rank_token"));
- }
-
- conn.disconnect();
- } catch (Exception e) {
- if (logCollector != null)
- logCollector.appendException(e, LogCollector.LogFile.ASYNC_DISCOVER_TOPICS_FETCHER, "doInBackground");
- if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
- }
-
- return result;
- }
-
- @Override
- protected void onPostExecute(final DiscoverTopicModel discoverTopicModel) {
- if (fetchListener != null) fetchListener.onResult(discoverTopicModel);
- }
-}
diff --git a/app/src/main/java/awais/instagrabber/customviews/CircularImageView.java b/app/src/main/java/awais/instagrabber/customviews/CircularImageView.java
index 441c21e3..29828709 100755
--- a/app/src/main/java/awais/instagrabber/customviews/CircularImageView.java
+++ b/app/src/main/java/awais/instagrabber/customviews/CircularImageView.java
@@ -2,10 +2,7 @@ package awais.instagrabber.customviews;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
import android.graphics.Color;
-import android.graphics.Paint;
import android.util.AttributeSet;
import androidx.annotation.Nullable;
@@ -17,36 +14,27 @@ 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();
- private final Paint paintBorder = new Paint();
- private BitmapShader shader;
- private Bitmap bitmap;
+import awais.instagrabber.R;
+public class CircularImageView extends SimpleDraweeView {
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) {
@@ -58,77 +46,12 @@ public final class CircularImageView extends SimpleDraweeView {
GenericDraweeHierarchyInflater.updateBuilder(builder, context, attrs);
setAspectRatio(builder.getDesiredAspectRatio());
setHierarchy(builder.build());
+ setBackgroundResource(R.drawable.shape_oval_light);
}
- 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);
- // }
- // });
- // }
- // 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();
- // }
-
public void setStoriesBorder() {
- this.color = Color.GREEN;
- // invalidate();
- // final RoundingParams roundingParams = RoundingParams.fromCornersRadius(5f);
- //
+ // private final int borderSize = 8;
+ final int color = Color.GREEN;
RoundingParams roundingParams = getHierarchy().getRoundingParams();
if (roundingParams == null) {
roundingParams = RoundingParams.asCircle().setRoundingMethod(RoundingParams.RoundingMethod.BITMAP_ONLY);
diff --git a/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java b/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java
new file mode 100644
index 00000000..aee8930f
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java
@@ -0,0 +1,330 @@
+package awais.instagrabber.customviews;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.lifecycle.ViewModelStoreOwner;
+import androidx.recyclerview.widget.LinearSmoothScroller;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.StaggeredGridLayoutManager;
+import androidx.transition.ChangeBounds;
+import androidx.transition.Transition;
+import androidx.transition.TransitionManager;
+import androidx.work.Data;
+import androidx.work.WorkInfo;
+import androidx.work.WorkManager;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import awais.instagrabber.adapters.FeedAdapterV2;
+import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
+import awais.instagrabber.customviews.helpers.PostFetcher;
+import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtBottom;
+import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.models.FeedModel;
+import awais.instagrabber.models.PostChild;
+import awais.instagrabber.models.PostsLayoutPreferences;
+import awais.instagrabber.utils.Utils;
+import awais.instagrabber.viewmodels.FeedViewModel;
+import awais.instagrabber.workers.DownloadWorker;
+
+public class PostsRecyclerView extends RecyclerView {
+ private static final String TAG = "PostsRecyclerView";
+
+ private StaggeredGridLayoutManager layoutManager;
+ private PostsLayoutPreferences layoutPreferences;
+ private PostFetcher.PostFetchService postFetchService;
+ private Transition transition;
+ private PostFetcher postFetcher;
+ private ViewModelStoreOwner viewModelStoreOwner;
+ private FeedAdapterV2 feedAdapter;
+ private LifecycleOwner lifeCycleOwner;
+ private FeedViewModel feedViewModel;
+ private boolean initCalled = false;
+ private GridSpacingItemDecoration gridSpacingItemDecoration;
+ private RecyclerLazyLoaderAtBottom lazyLoader;
+ private FeedAdapterV2.FeedItemCallback feedItemCallback;
+ private boolean shouldScrollToTop;
+ private FeedAdapterV2.SelectionModeCallback selectionModeCallback;
+
+ private final List fetchStatusChangeListeners = new ArrayList<>();
+
+ private final FetchListener> fetchListener = new FetchListener>() {
+ @Override
+ public void onResult(final List result) {
+ final int currentPage = lazyLoader.getCurrentPage();
+ if (currentPage == 0) {
+ feedViewModel.getList().postValue(result);
+ shouldScrollToTop = true;
+ dispatchFetchStatus();
+ return;
+ }
+ final List models = feedViewModel.getList().getValue();
+ final List modelsCopy = models == null ? new ArrayList<>() : new ArrayList<>(models);
+ modelsCopy.addAll(result);
+ feedViewModel.getList().postValue(modelsCopy);
+ dispatchFetchStatus();
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "onFailure: ", t);
+ }
+ };
+
+ private final RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(getContext()) {
+ @Override
+ protected int getVerticalSnapPreference() {
+ return LinearSmoothScroller.SNAP_TO_START;
+ }
+ };
+
+ public PostsRecyclerView(@NonNull final Context context) {
+ super(context);
+ }
+
+ public PostsRecyclerView(@NonNull final Context context, @Nullable final AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public PostsRecyclerView(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public PostsRecyclerView setViewModelStoreOwner(final ViewModelStoreOwner owner) {
+ if (initCalled) {
+ throw new IllegalArgumentException("init already called!");
+ }
+ this.viewModelStoreOwner = owner;
+ return this;
+ }
+
+ public PostsRecyclerView setLifeCycleOwner(final LifecycleOwner lifeCycleOwner) {
+ if (initCalled) {
+ throw new IllegalArgumentException("init already called!");
+ }
+ this.lifeCycleOwner = lifeCycleOwner;
+ return this;
+ }
+
+ public PostsRecyclerView setPostFetchService(final PostFetcher.PostFetchService postFetchService) {
+ if (initCalled) {
+ throw new IllegalArgumentException("init already called!");
+ }
+ this.postFetchService = postFetchService;
+ return this;
+ }
+
+ public PostsRecyclerView setFeedItemCallback(@NonNull final FeedAdapterV2.FeedItemCallback feedItemCallback) {
+ this.feedItemCallback = feedItemCallback;
+ return this;
+ }
+
+ public PostsRecyclerView setSelectionModeCallback(@NonNull final FeedAdapterV2.SelectionModeCallback selectionModeCallback) {
+ this.selectionModeCallback = selectionModeCallback;
+ return this;
+ }
+
+ public PostsRecyclerView setLayoutPreferences(final PostsLayoutPreferences layoutPreferences) {
+ this.layoutPreferences = layoutPreferences;
+ if (initCalled) {
+ if (layoutPreferences == null) return this;
+ feedAdapter.setLayoutPreferences(layoutPreferences);
+ updateLayout();
+ }
+ return this;
+ }
+
+ public void init() {
+ initCalled = true;
+ if (viewModelStoreOwner == null) {
+ throw new IllegalArgumentException("ViewModelStoreOwner cannot be null");
+ } else if (lifeCycleOwner == null) {
+ throw new IllegalArgumentException("LifecycleOwner cannot be null");
+ } else if (postFetchService == null) {
+ throw new IllegalArgumentException("PostFetchService cannot be null");
+ }
+ if (layoutPreferences == null) {
+ layoutPreferences = PostsLayoutPreferences.builder().build();
+ // Utils.settingsHelper.putString(Constants.PREF_POSTS_LAYOUT, layoutPreferences.getJson());
+ }
+ gridSpacingItemDecoration = new GridSpacingItemDecoration(Utils.convertDpToPx(2));
+ initTransition();
+ initAdapter();
+ initLayoutManager();
+ initSelf();
+ initDownloadWorkerListener();
+ }
+
+ private void initTransition() {
+ transition = new ChangeBounds();
+ transition.setDuration(300);
+ }
+
+ private void initLayoutManager() {
+ layoutManager = new StaggeredGridLayoutManager(layoutPreferences.getColCount(), StaggeredGridLayoutManager.VERTICAL);
+ setLayoutManager(layoutManager);
+ }
+
+ private void initAdapter() {
+ feedAdapter = new FeedAdapterV2(layoutPreferences, feedItemCallback, selectionModeCallback);
+ feedAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
+ setAdapter(feedAdapter);
+ }
+
+ private void initSelf() {
+ feedViewModel = new ViewModelProvider(viewModelStoreOwner).get(FeedViewModel.class);
+ feedViewModel.getList().observe(lifeCycleOwner, list -> feedAdapter.submitList(list, () -> {
+ if (!shouldScrollToTop) return;
+ smoothScrollToPosition(0);
+ shouldScrollToTop = false;
+ }));
+ postFetcher = new PostFetcher(postFetchService, fetchListener);
+ if (layoutPreferences.getHasGap()) {
+ addItemDecoration(gridSpacingItemDecoration);
+ }
+ setHasFixedSize(true);
+ setNestedScrollingEnabled(true);
+ lazyLoader = new RecyclerLazyLoaderAtBottom(layoutManager, (page) -> {
+ if (postFetcher.hasMore()) {
+ postFetcher.fetch();
+ dispatchFetchStatus();
+ }
+ });
+ addOnScrollListener(lazyLoader);
+ postFetcher.fetch();
+ dispatchFetchStatus();
+ }
+
+ private void initDownloadWorkerListener() {
+ WorkManager.getInstance(getContext())
+ .getWorkInfosByTagLiveData("download")
+ .observe(lifeCycleOwner, workInfoList -> {
+ for (final WorkInfo workInfo : workInfoList) {
+ if (workInfo == null) continue;
+ final Data progress = workInfo.getProgress();
+ final float progressPercent = progress.getFloat(DownloadWorker.PROGRESS, 0);
+ if (progressPercent != 100) continue;
+ final String url = progress.getString(DownloadWorker.URL);
+ final List feedModels = feedViewModel.getList().getValue();
+ for (int i = 0; i < feedModels.size(); i++) {
+ final FeedModel feedModel = feedModels.get(i);
+ final List displayUrls = getDisplayUrl(feedModel);
+ if (displayUrls.contains(url)) {
+ feedAdapter.notifyItemChanged(i);
+ break;
+ }
+ }
+ }
+ });
+ }
+
+ private List getDisplayUrl(final FeedModel feedModel) {
+ List urls = Collections.emptyList();
+ switch (feedModel.getItemType()) {
+ case MEDIA_TYPE_IMAGE:
+ case MEDIA_TYPE_VIDEO:
+ urls = Collections.singletonList(feedModel.getDisplayUrl());
+ break;
+ case MEDIA_TYPE_SLIDER:
+ final List sliderItems = feedModel.getSliderItems();
+ if (sliderItems != null) {
+ final ImmutableList.Builder builder = ImmutableList.builder();
+ for (final PostChild child : sliderItems) {
+ builder.add(child.getDisplayUrl());
+ }
+ urls = builder.build();
+ }
+ break;
+ default:
+ }
+ return urls;
+ }
+
+ private void updateLayout() {
+ post(() -> {
+ TransitionManager.beginDelayedTransition(this, transition);
+ feedAdapter.notifyDataSetChanged();
+ if (!layoutPreferences.getHasGap()) {
+ removeItemDecoration(gridSpacingItemDecoration);
+ } else {
+ addItemDecoration(gridSpacingItemDecoration);
+ }
+ if (layoutPreferences.getType() == PostsLayoutPreferences.PostsLayoutType.LINEAR) {
+ if (layoutManager.getSpanCount() != 1) {
+ layoutManager.setSpanCount(1);
+ setAdapter(null);
+ setAdapter(feedAdapter);
+ }
+ } else {
+ boolean shouldRedraw = layoutManager.getSpanCount() == 1;
+ layoutManager.setSpanCount(layoutPreferences.getColCount());
+ if (shouldRedraw) {
+ setAdapter(null);
+ setAdapter(feedAdapter);
+ }
+ }
+ });
+ }
+
+ public void refresh() {
+ lazyLoader.resetState();
+ postFetcher.reset();
+ postFetcher.fetch();
+ dispatchFetchStatus();
+ }
+
+ public boolean isFetching() {
+ return postFetcher != null && postFetcher.isFetching();
+ }
+
+ public PostsRecyclerView addFetchStatusChangeListener(final FetchStatusChangeListener fetchStatusChangeListener) {
+ if (fetchStatusChangeListener == null) return this;
+ fetchStatusChangeListeners.add(fetchStatusChangeListener);
+ return this;
+ }
+
+ public void removeFetchStatusListener(final FetchStatusChangeListener fetchStatusChangeListener) {
+ if (fetchStatusChangeListener == null) return;
+ fetchStatusChangeListeners.remove(fetchStatusChangeListener);
+ }
+
+ private void dispatchFetchStatus() {
+ for (final FetchStatusChangeListener listener : fetchStatusChangeListeners) {
+ listener.onFetchStatusChange(isFetching());
+ }
+ }
+
+ public PostsLayoutPreferences getLayoutPreferences() {
+ return layoutPreferences;
+ }
+
+ public void endSelection() {
+ feedAdapter.endSelection();
+ }
+
+ public interface FetchStatusChangeListener {
+ void onFetchStatusChange(boolean fetching);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ lifeCycleOwner = null;
+ }
+
+ @Override
+ public void smoothScrollToPosition(final int position) {
+ smoothScroller.setTargetPosition(position);
+ layoutManager.startSmoothScroll(smoothScroller);
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/customviews/ProfilePicView.java b/app/src/main/java/awais/instagrabber/customviews/ProfilePicView.java
new file mode 100644
index 00000000..cd156d59
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/customviews/ProfilePicView.java
@@ -0,0 +1,141 @@
+package awais.instagrabber.customviews;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+
+import androidx.annotation.DimenRes;
+import androidx.annotation.NonNull;
+
+import com.facebook.drawee.generic.GenericDraweeHierarchy;
+import com.facebook.drawee.generic.RoundingParams;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import awais.instagrabber.R;
+
+public final class ProfilePicView extends CircularImageView {
+ private static final String TAG = "ProfilePicView";
+
+ private Size size;
+ private int dimensionPixelSize;
+
+ public ProfilePicView(Context context, GenericDraweeHierarchy hierarchy) {
+ super(context);
+ setHierarchy(hierarchy);
+ size = Size.REGULAR;
+ updateLayout();
+ }
+
+ public ProfilePicView(final Context context) {
+ super(context);
+ size = Size.REGULAR;
+ updateLayout();
+ }
+
+ public ProfilePicView(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ parseAttrs(context, attrs);
+ updateLayout();
+ }
+
+ public ProfilePicView(final Context context,
+ final AttributeSet attrs,
+ final int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ parseAttrs(context, attrs);
+ }
+
+ private void parseAttrs(final Context context, final AttributeSet attrs) {
+ final TypedArray a = context.getTheme().obtainStyledAttributes(
+ attrs,
+ R.styleable.ProfilePicView,
+ 0,
+ 0);
+ try {
+ final int sizeValue = a.getInt(R.styleable.ProfilePicView_size, Size.REGULAR.getValue());
+ size = Size.valueOf(sizeValue);
+ } finally {
+ a.recycle();
+ }
+ }
+
+ private void updateLayout() {
+ @DimenRes final int dimenRes;
+ switch (size) {
+ case SMALL:
+ dimenRes = R.dimen.profile_pic_size_small;
+ break;
+ case TINY:
+ dimenRes = R.dimen.profile_pic_size_tiny;
+ break;
+ case LARGE:
+ dimenRes = R.dimen.profile_pic_size_large;
+ break;
+ default:
+ case REGULAR:
+ dimenRes = R.dimen.profile_pic_size_regular;
+ break;
+ }
+ ViewGroup.LayoutParams layoutParams = getLayoutParams();
+ if (layoutParams == null) {
+ layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+ dimensionPixelSize = getResources().getDimensionPixelSize(dimenRes);
+ layoutParams.width = dimensionPixelSize;
+ layoutParams.height = dimensionPixelSize;
+
+ // invalidate();
+ // requestLayout();
+ }
+
+ public void setStoriesBorder() {
+ // private final int borderSize = 8;
+ final int color = Color.GREEN;
+ RoundingParams roundingParams = getHierarchy().getRoundingParams();
+ if (roundingParams == null) {
+ roundingParams = RoundingParams.asCircle().setRoundingMethod(RoundingParams.RoundingMethod.BITMAP_ONLY);
+ }
+ roundingParams.setBorder(color, 5.0f);
+ getHierarchy().setRoundingParams(roundingParams);
+ }
+
+ public enum Size {
+ TINY(0),
+ SMALL(1),
+ REGULAR(2),
+ LARGE(3);
+
+ private final int value;
+ private static Map map = new HashMap<>();
+
+ static {
+ for (Size size : Size.values()) {
+ map.put(size.value, size);
+ }
+ }
+
+ Size(final int value) {
+ this.value = value;
+ }
+
+ @NonNull
+ public static Size valueOf(final int value) {
+ final Size size = map.get(value);
+ return size != null ? size : Size.REGULAR;
+ }
+
+ public int getValue() {
+ return value;
+ }
+ }
+
+ @Override
+ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ setMeasuredDimension(dimensionPixelSize, dimensionPixelSize);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/customviews/RamboTextView.java b/app/src/main/java/awais/instagrabber/customviews/RamboTextView.java
index feca335c..b4b85673 100755
--- a/app/src/main/java/awais/instagrabber/customviews/RamboTextView.java
+++ b/app/src/main/java/awais/instagrabber/customviews/RamboTextView.java
@@ -34,7 +34,6 @@ public final class RamboTextView extends AppCompatTextView {
private ClickableSpan clickableSpanUnderTouchOnActionDown;
private MentionClickListener mentionClickListener;
private boolean isUrlHighlighted;
- private boolean isExpandable;
private boolean isExpanded;
private OnLongClickListener longClickListener;
@@ -59,13 +58,13 @@ public final class RamboTextView extends AppCompatTextView {
this.mentionClickListener = mentionClickListener;
}
- public void setCaptionIsExpandable(final boolean isExpandable) {
- this.isExpandable = isExpandable;
- }
+ // public void setCaptionIsExpandable(final boolean isExpandable) {
+ // this.isExpandable = isExpandable;
+ // }
- public void setCaptionIsExpanded(final boolean isExpanded) {
- this.isExpanded = isExpanded;
- }
+ // public void setCaptionIsExpanded(final boolean isExpanded) {
+ // this.isExpanded = isExpanded;
+ // }
@Override
public void setOnLongClickListener(@Nullable final OnLongClickListener l) {
@@ -98,7 +97,7 @@ public final class RamboTextView extends AppCompatTextView {
case MotionEvent.ACTION_DOWN:
final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
handler.postDelayed(longPressRunnable, longPressTimeout);
- if (feedModel != null) feedModel.setMentionClicked(false);
+ // if (feedModel != null) feedModel.setMentionClicked(false);
if (clickableSpanUnderTouch != null) {
highlightUrl(clickableSpanUnderTouch, spanText);
}
@@ -107,19 +106,19 @@ public final class RamboTextView extends AppCompatTextView {
handler.removeCallbacks(longPressRunnable);
if (touchStartedOverAClickableSpan && clickableSpanUnderTouch == clickableSpanUnderTouchOnActionDown) {
dispatchUrlClick(spanText, clickableSpanUnderTouch);
- if (feedModel != null) feedModel.setMentionClicked(true);
+ // if (feedModel != null) feedModel.setMentionClicked(true);
}
cleanupOnTouchUp(spanText);
return super.onTouchEvent(event);
case MotionEvent.ACTION_MOVE:
// handler.removeCallbacks(longPressRunnable);
- if (feedModel != null) feedModel.setMentionClicked(false);
+ // if (feedModel != null) feedModel.setMentionClicked(false);
if (clickableSpanUnderTouch != null) highlightUrl(clickableSpanUnderTouch, spanText);
else removeUrlHighlightColor(spanText);
return super.onTouchEvent(event);
case MotionEvent.ACTION_CANCEL:
handler.removeCallbacks(longPressRunnable);
- if (feedModel != null) feedModel.setMentionClicked(false);
+ // if (feedModel != null) feedModel.setMentionClicked(false);
cleanupOnTouchUp(spanText);
return super.onTouchEvent(event);
}
diff --git a/app/src/main/java/awais/instagrabber/customviews/RamboTextViewV2.java b/app/src/main/java/awais/instagrabber/customviews/RamboTextViewV2.java
new file mode 100644
index 00000000..14f09798
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/customviews/RamboTextViewV2.java
@@ -0,0 +1,149 @@
+package awais.instagrabber.customviews;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.github.armcha.autolink.AutoLinkItem;
+import io.github.armcha.autolink.AutoLinkTextView;
+import io.github.armcha.autolink.MODE_EMAIL;
+import io.github.armcha.autolink.MODE_HASHTAG;
+import io.github.armcha.autolink.MODE_MENTION;
+import io.github.armcha.autolink.MODE_URL;
+import io.github.armcha.autolink.Mode;
+
+public class RamboTextViewV2 extends AutoLinkTextView {
+ private final List onMentionClickListeners = new ArrayList<>();
+ private final List onHashtagClickListeners = new ArrayList<>();
+ private final List onURLClickListeners = new ArrayList<>();
+ private final List onEmailClickListeners = new ArrayList<>();
+
+ public RamboTextViewV2(@NonNull final Context context,
+ @Nullable final AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ private void init() {
+ addAutoLinkMode(MODE_HASHTAG.INSTANCE, MODE_MENTION.INSTANCE, MODE_EMAIL.INSTANCE, MODE_URL.INSTANCE);
+ onAutoLinkClick(autoLinkItem -> {
+ final Mode mode = autoLinkItem.getMode();
+ if (mode.equals(MODE_MENTION.INSTANCE)) {
+ for (final OnMentionClickListener onMentionClickListener : onMentionClickListeners) {
+ onMentionClickListener.onMentionClick(autoLinkItem);
+ }
+ return;
+ }
+ if (mode.equals(MODE_HASHTAG.INSTANCE)) {
+ for (final OnHashtagClickListener onHashtagClickListener : onHashtagClickListeners) {
+ onHashtagClickListener.onHashtagClick(autoLinkItem);
+ }
+ return;
+ }
+ if (mode.equals(MODE_URL.INSTANCE)) {
+ for (final OnURLClickListener onURLClickListener : onURLClickListeners) {
+ onURLClickListener.onURLClick(autoLinkItem);
+ }
+ return;
+ }
+ if (mode.equals(MODE_EMAIL.INSTANCE)) {
+ for (final OnEmailClickListener onEmailClickListener : onEmailClickListeners) {
+ onEmailClickListener.onEmailClick(autoLinkItem);
+ }
+ }
+ });
+ }
+
+ public void addOnMentionClickListener(final OnMentionClickListener onMentionClickListener) {
+ if (onMentionClickListener == null) {
+ return;
+ }
+ onMentionClickListeners.add(onMentionClickListener);
+ }
+
+ public void removeOnMentionClickListener(final OnMentionClickListener onMentionClickListener) {
+ if (onMentionClickListener == null) {
+ return;
+ }
+ onMentionClickListeners.remove(onMentionClickListener);
+ }
+
+ public void clearOnMentionClickListeners() {
+ onMentionClickListeners.clear();
+ }
+
+ public void addOnHashtagListener(final OnHashtagClickListener onHashtagClickListener) {
+ if (onHashtagClickListener == null) {
+ return;
+ }
+ onHashtagClickListeners.add(onHashtagClickListener);
+ }
+
+ public void removeOnHashtagListener(final OnHashtagClickListener onHashtagClickListener) {
+ if (onHashtagClickListener == null) {
+ return;
+ }
+ onHashtagClickListeners.remove(onHashtagClickListener);
+ }
+
+ public void clearOnHashtagClickListeners() {
+ onHashtagClickListeners.clear();
+ }
+
+ public void addOnURLClickListener(final OnURLClickListener onURLClickListener) {
+ if (onURLClickListener == null) {
+ return;
+ }
+ onURLClickListeners.add(onURLClickListener);
+ }
+
+ public void removeOnURLClickListener(final OnURLClickListener onURLClickListener) {
+ if (onURLClickListener == null) {
+ return;
+ }
+ onURLClickListeners.remove(onURLClickListener);
+ }
+
+ public void clearOnURLClickListeners() {
+ onURLClickListeners.clear();
+ }
+
+ public void addOnEmailClickListener(final OnEmailClickListener onEmailClickListener) {
+ if (onEmailClickListener == null) {
+ return;
+ }
+ onEmailClickListeners.add(onEmailClickListener);
+ }
+
+ public void removeOnEmailClickListener(final OnEmailClickListener onEmailClickListener) {
+ if (onEmailClickListener == null) {
+ return;
+ }
+ onEmailClickListeners.remove(onEmailClickListener);
+ }
+
+ public void clearOnEmailClickListeners() {
+ onEmailClickListeners.clear();
+ }
+
+ public interface OnMentionClickListener {
+ void onMentionClick(final AutoLinkItem autoLinkItem);
+ }
+
+ public interface OnHashtagClickListener {
+ void onHashtagClick(final AutoLinkItem autoLinkItem);
+ }
+
+ public interface OnURLClickListener {
+ void onURLClick(final AutoLinkItem autoLinkItem);
+ }
+
+ public interface OnEmailClickListener {
+ void onEmailClick(final AutoLinkItem autoLinkItem);
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/customviews/RemixDrawerLayout.java b/app/src/main/java/awais/instagrabber/customviews/RemixDrawerLayout.java
deleted file mode 100755
index ebfc4e80..00000000
--- a/app/src/main/java/awais/instagrabber/customviews/RemixDrawerLayout.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package awais.instagrabber.customviews;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.view.GravityCompat;
-import androidx.core.view.ViewCompat;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.viewpager.widget.PagerAdapter;
-import androidx.viewpager.widget.ViewPager;
-
-import awais.instagrabber.R;
-
-public final class RemixDrawerLayout extends MouseDrawer implements MouseDrawer.DrawerListener {
- private final FrameLayout frameLayout;
- private View drawerView;
- private RecyclerView highlightsList, feedPosts, feedStories;
- private float startX;
-
- public RemixDrawerLayout(@NonNull final Context context) {
- this(context, null);
- }
-
- public RemixDrawerLayout(@NonNull final Context context, @Nullable final AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public RemixDrawerLayout(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyle) {
- super(context, attrs, defStyle);
-
- super.setDrawerElevation(getDrawerElevation());
-
- addDrawerListener(this);
-
- frameLayout = new FrameLayout(context);
- frameLayout.setPadding(0, 0, 0, 0);
- super.addView(frameLayout);
- }
-
- @Override
- public void addView(@NonNull final View child, final ViewGroup.LayoutParams params) {
- child.setLayoutParams(params);
- addView(child);
- }
-
- @Override
- public void addView(@NonNull final View child) {
- if (child.getTag() != null) super.addView(child);
- else frameLayout.addView(child);
- }
-
- @Override
- public boolean onInterceptTouchEvent(@NonNull final MotionEvent ev) {
- final float x = ev.getX();
- final float y = ev.getY();
-
- // another one of my own weird hack thingies to make this app work
- if (feedPosts == null) feedPosts = findViewById(R.id.feedPosts);
- if (feedPosts != null) {
- for (int i = 0; i < feedPosts.getChildCount(); ++i) {
- final View viewHolder = feedPosts.getChildAt(i);
- final View mediaList = viewHolder.findViewById(R.id.media_list);
- if (mediaList instanceof ViewPager) {
- final ViewPager viewPager = (ViewPager) mediaList;
-
- final Rect rect = new Rect();
- viewPager.getGlobalVisibleRect(rect);
-
- final boolean touchIsInMediaList = rect.contains((int) x, (int) y);
- if (touchIsInMediaList) {
- final PagerAdapter adapter = viewPager.getAdapter();
- final int count = adapter != null ? adapter.getCount() : 0;
- if (count < 1 || viewPager.getCurrentItem() != count - 1) return false;
- break;
- }
- }
- }
- }
-
- // thanks to Fede @ https://stackoverflow.com/questions/6920137/android-viewpager-and-horizontalscrollview/7258579#7258579
- if (highlightsList == null) highlightsList = findViewById(R.id.highlightsList);
- if (highlightsList != null) {
- final Boolean result = handleHorizontalRecyclerView(ev, highlightsList);
- if (result != null) {
- return result;
- }
- }
- if (feedStories == null) feedStories = findViewById(R.id.feedStories);
- if (feedStories != null) {
- final Boolean result = handleHorizontalRecyclerView(ev, feedStories);
- if (result != null) {
- return result;
- }
- }
- return super.onInterceptTouchEvent(ev);
- }
-
- private Boolean handleHorizontalRecyclerView(@NonNull final MotionEvent ev, final RecyclerView view) {
- final float x = ev.getX();
- final float y = ev.getY();
- final boolean touchIsInRecycler = x >= view.getLeft() && x < view.getRight()
- && y >= view.getTop() && view.getBottom() > y;
-
- if (touchIsInRecycler) {
- final int action = ev.getActionMasked();
-
- if (action == MotionEvent.ACTION_CANCEL) return super.onInterceptTouchEvent(ev);
-
- if (action == MotionEvent.ACTION_DOWN) startX = x;
- else if (action == MotionEvent.ACTION_MOVE) {
- final int scrollRange = view.computeHorizontalScrollRange();
- final int scrollOffset = view.computeHorizontalScrollOffset();
- final boolean scrollable = scrollRange > view.getWidth();
- final boolean draggingFromRight = startX > x;
-
- if (scrollOffset < 1) {
- if (!scrollable) return super.onInterceptTouchEvent(ev);
- else if (!draggingFromRight) return super.onInterceptTouchEvent(ev);
- } else if (scrollable && draggingFromRight && scrollRange - scrollOffset == view.computeHorizontalScrollExtent()) {
- return super.onInterceptTouchEvent(ev);
- }
-
- return false;
- }
- }
- return null;
- }
-
- @Override
- public void onDrawerSlide(@NonNull final View view, @EdgeGravity final int gravity, final float slideOffset) {
- drawerView = view;
- final int absHorizGravity = getDrawerViewAbsoluteGravity(GravityCompat.START);
- final int childAbsGravity = getDrawerViewAbsoluteGravity(drawerView);
-
- final Window window = getActivity(getContext()).getWindow();
- final boolean isRtl = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
- || window.getDecorView().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
- || getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
-
- final int drawerViewWidth = drawerView.getWidth();
-
- // for (int i = 0; i < frameLayout.getChildCount(); i++) {
- // final View child = frameLayout.getChildAt(i);
- //
- // final boolean isLeftDrawer = isRtl == (childAbsGravity != absHorizGravity);
- // float width = isLeftDrawer ? drawerViewWidth : -drawerViewWidth;
- //
- // child.setX(width * slideOffset);
- // }
-
- final boolean isLeftDrawer = isRtl == (childAbsGravity != absHorizGravity);
- float width = isLeftDrawer ? drawerViewWidth : -drawerViewWidth;
-
- frameLayout.setX(width * (isRtl ? -slideOffset : slideOffset));
- }
-
- @Override
- public void openDrawer(@NonNull final View drawerView, final boolean animate) {
- super.openDrawer(drawerView, animate);
- post(() -> onDrawerSlide(drawerView, Gravity.NO_GRAVITY, isDrawerOpen(drawerView) ? 1f : 0f));
- }
-
- @Override
- protected void onConfigurationChanged(final Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- if (drawerView != null) onDrawerSlide(drawerView, Gravity.NO_GRAVITY, isDrawerOpen(drawerView) ? 1f : 0f);
- }
-
- private static Activity getActivity(final Context context) {
- if (context != null) {
- if (context instanceof Activity) return (Activity) context;
- if (context instanceof ContextWrapper)
- return getActivity(((ContextWrapper) context).getBaseContext());
- }
- return null;
- }
-
- final int getDrawerViewAbsoluteGravity(final int gravity) {
- return GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(this)) & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK;
- }
-
- final int getDrawerViewAbsoluteGravity(@NonNull final View drawerView) {
- final int gravity = ((LayoutParams) drawerView.getLayoutParams()).gravity;
- return getDrawerViewAbsoluteGravity(gravity);
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/customviews/SharedElementTransitionDialogFragment.java b/app/src/main/java/awais/instagrabber/customviews/SharedElementTransitionDialogFragment.java
new file mode 100644
index 00000000..e6e16b0a
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/customviews/SharedElementTransitionDialogFragment.java
@@ -0,0 +1,304 @@
+package awais.instagrabber.customviews;
+
+import android.animation.Animator;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.animation.AccelerateDecelerateInterpolator;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.DialogFragment;
+import androidx.transition.ChangeBounds;
+import androidx.transition.ChangeTransform;
+import androidx.transition.Transition;
+import androidx.transition.TransitionListenerAdapter;
+import androidx.transition.TransitionManager;
+import androidx.transition.TransitionSet;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import awais.instagrabber.utils.Utils;
+
+public abstract class SharedElementTransitionDialogFragment extends DialogFragment {
+ // private static final String TAG = "SETDialogFragment";
+ private static final int DURATION = 200;
+
+ private final Map startViews = new HashMap<>();
+ private final Map destViews = new HashMap<>();
+ private final Map viewBoundsMap = new HashMap<>();
+ private final List additionalAnimators = new ArrayList<>();
+ private final Handler initialBoundsHandler = new Handler();
+
+ private boolean startCalled = false;
+ private boolean startInitiated = false;
+ private int boundsCalculatedCount = 0;
+
+ protected int getAnimationDuration() {
+ return DURATION;
+ }
+
+ public void addSharedElement(@NonNull final View startView, @NonNull final View destView) {
+ final int key = destView.hashCode();
+ startViews.put(key, startView);
+ destViews.put(key, destView);
+ initialBoundsHandler.post(() -> setupInitialBounds(startView, destView));
+ }
+
+ public void startPostponedEnterTransition() {
+ startCalled = true;
+ if (startInitiated) return;
+ if (boundsCalculatedCount < startViews.size()) return;
+ startInitiated = true;
+ final Set keySet = startViews.keySet();
+ final View view = getView();
+ if (!(view instanceof ViewGroup)) return;
+ final TransitionSet transitionSet = new TransitionSet()
+ .setDuration(DURATION)
+ .setInterpolator(new AccelerateDecelerateInterpolator())
+ .addTransition(new ChangeBounds())
+ .addTransition(new ChangeTransform())
+ .addListener(new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionStart(@NonNull final Transition transition) {
+ for (Animator animator : additionalAnimators) {
+ animator.start();
+ }
+ }
+
+ @Override
+ public void onTransitionEnd(@NonNull final Transition transition) {
+ for (final Integer key : keySet) {
+ final View startView = startViews.get(key);
+ final View destView = destViews.get(key);
+ final ViewBounds viewBounds = viewBoundsMap.get(key);
+ if (startView == null || destView == null || viewBounds == null) return;
+ onEndSharedElementAnimation(startView, destView, viewBounds);
+ }
+ }
+ });
+ view.post(() -> {
+ TransitionManager.beginDelayedTransition((ViewGroup) view, transitionSet);
+ for (final Integer key : keySet) {
+ final View startView = startViews.get(key);
+ final View destView = destViews.get(key);
+ final ViewBounds viewBounds = viewBoundsMap.get(key);
+ if (startView == null || destView == null || viewBounds == null) return;
+ onBeforeSharedElementAnimation(startView, destView, viewBounds);
+ setDestBounds(key);
+ }
+ });
+ }
+
+ private void setDestBounds(final int key) {
+ final View startView = startViews.get(key);
+ if (startView == null) return;
+ final View destView = destViews.get(key);
+ if (destView == null) return;
+ final ViewBounds viewBounds = viewBoundsMap.get(key);
+ if (viewBounds == null) return;
+ destView.setX((int) viewBounds.getDestX());
+ destView.setY((int) viewBounds.getDestY());
+ destView.setTranslationX(0);
+ destView.setTranslationY(0);
+ final ViewGroup.LayoutParams layoutParams = destView.getLayoutParams();
+ layoutParams.height = viewBounds.getDestHeight();
+ layoutParams.width = viewBounds.getDestWidth();
+ destView.requestLayout();
+ }
+
+ protected void onBeforeSharedElementAnimation(@NonNull final View startView,
+ @NonNull final View destView,
+ @NonNull final ViewBounds viewBounds) {}
+
+ protected void onEndSharedElementAnimation(@NonNull final View startView,
+ @NonNull final View destView,
+ @NonNull final ViewBounds viewBounds) {}
+
+ private void setupInitialBounds(@NonNull final View startView, @NonNull final View destView) {
+ final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
+ private boolean firstPassDone;
+
+ @Override
+ public boolean onPreDraw() {
+ destView.getViewTreeObserver().removeOnPreDrawListener(this);
+ if (!firstPassDone) {
+ getViewBounds(startView, destView, this);
+ firstPassDone = true;
+ return false;
+ }
+ final int[] location = new int[2];
+ startView.getLocationOnScreen(location);
+ final int initX = location[0];
+ final int initY = location[1];
+ destView.setX(initX);
+ destView.setY(initY - Utils.getStatusBarHeight(getContext()));
+ destView.requestLayout();
+ boundsCalculatedCount++;
+ if (startCalled) {
+ startPostponedEnterTransition();
+ }
+ return true;
+ }
+ };
+ destView.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
+ }
+
+ private void getViewBounds(@NonNull final View startView,
+ @NonNull final View destView,
+ @NonNull final ViewTreeObserver.OnPreDrawListener preDrawListener) {
+ final ViewBounds viewBounds = new ViewBounds();
+ viewBounds.setDestWidth(destView.getWidth());
+ viewBounds.setDestHeight(destView.getHeight());
+ viewBounds.setDestX(destView.getX());
+ viewBounds.setDestY(destView.getY());
+
+ final Rect destBounds = new Rect();
+ destView.getDrawingRect(destBounds);
+ viewBounds.setDestBounds(destBounds);
+
+ final ViewGroup.LayoutParams layoutParams = destView.getLayoutParams();
+
+ final Rect startBounds = new Rect();
+ startView.getDrawingRect(startBounds);
+ viewBounds.setStartBounds(startBounds);
+
+ final int key = destView.hashCode();
+ viewBoundsMap.put(key, viewBounds);
+
+ layoutParams.height = startView.getHeight();
+ layoutParams.width = startView.getWidth();
+
+ destView.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
+ destView.requestLayout();
+ }
+
+ // private void animateBounds(@NonNull final View startView,
+ // @NonNull final View destView,
+ // @NonNull final ViewBounds viewBounds) {
+ // final ValueAnimator heightAnimator = ObjectAnimator.ofInt(startView.getHeight(), viewBounds.getDestHeight());
+ // final ValueAnimator widthAnimator = ObjectAnimator.ofInt(startView.getWidth(), viewBounds.getDestWidth());
+ // heightAnimator.setDuration(DURATION);
+ // widthAnimator.setDuration(DURATION);
+ // additionalAnimators.add(heightAnimator);
+ // additionalAnimators.add(widthAnimator);
+ // heightAnimator.addUpdateListener(animation -> {
+ // ViewGroup.LayoutParams params = destView.getLayoutParams();
+ // params.height = (int) animation.getAnimatedValue();
+ // destView.requestLayout();
+ // });
+ // widthAnimator.addUpdateListener(animation -> {
+ // ViewGroup.LayoutParams params = destView.getLayoutParams();
+ // params.width = (int) animation.getAnimatedValue();
+ // destView.requestLayout();
+ // });
+ // onBeforeSharedElementAnimation(startView, destView, viewBounds);
+ // final float destX = viewBounds.getDestX();
+ // final float destY = viewBounds.getDestY();
+ // final AnimatorSet animatorSet = new AnimatorSet();
+ // animatorSet.addListener(new AnimatorListenerAdapter() {
+ // @Override
+ // public void onAnimationEnd(final Animator animation) {
+ // animationEnded(startView, destView, viewBounds);
+ // }
+ // });
+ //
+ // destView.animate()
+ // .x(destX)
+ // .y(destY)
+ // .setDuration(DURATION)
+ // .withStartAction(() -> {
+ // if (!additionalAnimatorsStarted && additionalAnimators.size() > 0) {
+ // additionalAnimatorsStarted = true;
+ // animatorSet.playTogether(additionalAnimators);
+ // animatorSet.start();
+ // }
+ // })
+ // .withEndAction(() -> animationEnded(startView, destView, viewBounds))
+ // .start();
+ // }
+
+ // private int endCount = 0;
+ // private void animationEnded(final View startView, final View destView, final ViewBounds viewBounds) {
+ // ++endCount;
+ // if (endCount != startViews.size() + 1) return;
+ // onEndSharedElementAnimation(startView, destView, viewBounds);
+ // }
+
+ protected void addAnimator(@NonNull final Animator animator) {
+ additionalAnimators.add(animator);
+ }
+
+ protected static class ViewBounds {
+ private float destY;
+ private float destX;
+ private int destHeight;
+ private int destWidth;
+ private Rect startBounds;
+ private Rect destBounds;
+
+ public ViewBounds() {}
+
+ public float getDestY() {
+ return destY;
+ }
+
+ public void setDestY(final float destY) {
+ this.destY = destY;
+ }
+
+ public float getDestX() {
+ return destX;
+ }
+
+ public void setDestX(final float destX) {
+ this.destX = destX;
+ }
+
+ public int getDestHeight() {
+ return destHeight;
+ }
+
+ public void setDestHeight(final int destHeight) {
+ this.destHeight = destHeight;
+ }
+
+ public int getDestWidth() {
+ return destWidth;
+ }
+
+ public void setDestWidth(final int destWidth) {
+ this.destWidth = destWidth;
+ }
+
+ public Rect getStartBounds() {
+ return startBounds;
+ }
+
+ public void setStartBounds(final Rect startBounds) {
+ this.startBounds = startBounds;
+ }
+
+ public Rect getDestBounds() {
+ return destBounds;
+ }
+
+ public void setDestBounds(final Rect destBounds) {
+ this.destBounds = destBounds;
+ }
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ startViews.clear();
+ destViews.clear();
+ viewBoundsMap.clear();
+ additionalAnimators.clear();
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/customviews/VerticalDragHelper.java b/app/src/main/java/awais/instagrabber/customviews/VerticalDragHelper.java
new file mode 100644
index 00000000..e7527e9b
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/customviews/VerticalDragHelper.java
@@ -0,0 +1,203 @@
+package awais.instagrabber.customviews;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewParent;
+
+import androidx.annotation.NonNull;
+
+public class VerticalDragHelper {
+ private static final String TAG = "VerticalDragHelper";
+ private static final float PIXELS_PER_SECOND = 10;
+
+ private final View view;
+
+ private GestureDetector gestureDetector;
+ private Context context;
+ private float flingVelocity;
+ private OnVerticalDragListener onVerticalDragListener;
+
+ private final GestureDetector.OnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() {
+
+ @Override
+ public boolean onSingleTapConfirmed(final MotionEvent e) {
+ view.performClick();
+ return true;
+ }
+
+ @Override
+ public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, final float velocityY) {
+ float maxFlingVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();
+ float velocityPercentY = velocityY / maxFlingVelocity;
+ float normalizedVelocityY = velocityPercentY * PIXELS_PER_SECOND;
+ if (Math.abs(normalizedVelocityY) > 4) {
+ flingVelocity = normalizedVelocityY;
+ }
+ return super.onFling(e1, e2, velocityX, velocityY);
+ }
+
+
+ };
+
+ private final GestureDetector.OnGestureListener dragPreventionGestureListener = new GestureDetector.SimpleOnGestureListener() {
+ float prevDistanceY = 0;
+
+ @Override
+ public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX, final float distanceY) {
+ Log.d(TAG, "onScroll: distanceX: " + distanceX + ", distanceY: " + distanceY);
+ return super.onScroll(e1, e2, distanceX, distanceY);
+ }
+
+ @Override
+ public boolean onSingleTapUp(final MotionEvent e) {
+ Log.d(TAG, "onSingleTapUp");
+ return super.onSingleTapUp(e);
+ }
+ };
+
+ private float prevRawY;
+ private boolean isDragging;
+ private float prevRawX;
+ private float dX;
+ private float prevDY;
+ private GestureDetector dragPreventionGestureDetector;
+
+ public VerticalDragHelper(@NonNull final View view) {
+ this.view = view;
+ final Context context = view.getContext();
+ if (context == null) return;
+ this.context = context;
+ init();
+ }
+
+ public void setOnVerticalDragListener(@NonNull final OnVerticalDragListener onVerticalDragListener) {
+ this.onVerticalDragListener = onVerticalDragListener;
+ }
+
+ protected void init() {
+ gestureDetector = new GestureDetector(context, gestureListener);
+ dragPreventionGestureDetector = new GestureDetector(context, dragPreventionGestureListener);
+ }
+
+ public boolean onDragTouch(final MotionEvent event) {
+ if (onVerticalDragListener == null) {
+ return false;
+ }
+ // dragPreventionGestureDetector.onTouchEvent(event);
+ if (gestureDetector.onTouchEvent(event)) {
+ return true;
+ }
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ boolean handled = false;
+ final float rawY = event.getRawY();
+ final float dY = rawY - prevRawY;
+ if (!isDragging) {
+ final float rawX = event.getRawX();
+ if (prevRawX != 0) {
+ dX = rawX - prevRawX;
+ }
+ prevRawX = rawX;
+ if (prevRawY != 0) {
+ final float dYAbs = Math.abs(dY - prevDY);
+ if (!isDragging && dYAbs < 50) {
+ final float abs = Math.abs(dY) - Math.abs(dX);
+ if (abs > 0) {
+ isDragging = true;
+ }
+ }
+ }
+ }
+ if (isDragging) {
+ final ViewParent parent = view.getParent();
+ parent.requestDisallowInterceptTouchEvent(true);
+ onVerticalDragListener.onDrag(dY);
+ handled = true;
+ }
+ prevDY = dY;
+ prevRawY = rawY;
+ return handled;
+ case MotionEvent.ACTION_UP:
+ // Log.d(TAG, "onDragTouch: reset prevRawY");
+ prevRawY = 0;
+ if (flingVelocity != 0) {
+ onVerticalDragListener.onFling(flingVelocity);
+ flingVelocity = 0;
+ isDragging = false;
+ return true;
+ }
+ if (isDragging) {
+ onVerticalDragListener.onDragEnd();
+ isDragging = false;
+ return true;
+ }
+ return false;
+ default:
+ return false;
+ }
+ }
+
+ public boolean isDragging() {
+ return isDragging;
+ }
+
+ public boolean onGestureTouchEvent(final MotionEvent event) {
+ return gestureDetector.onTouchEvent(event);
+ }
+
+ private final static int DIRECTION_UP = 0;
+ private final static int DIRECTION_DOWN = 1;
+ float prevY = -1;
+ int edgeHitCount = 0;
+ float prevDirection = -1;
+
+
+ // private boolean shouldPreventDrag(final MotionEvent event) {
+ // switch (event.getAction()) {
+ // case MotionEvent.ACTION_DOWN:
+ // if (!firstDrag) {
+ // firstDrag = true;
+ // }
+ // return false;
+ // case MotionEvent.ACTION_MOVE:
+ // float y = event.getY();
+ // int direction = -2;
+ // if (prevY != -1) {
+ // final float dy = y - prevY;
+ // // Log.d(TAG, "shouldPreventDrag: dy: " + dy);
+ // if (dy > 0) {
+ // direction = DIRECTION_DOWN;
+ // // move direction is down
+ // } else {
+ // direction = DIRECTION_UP;
+ // // move direction is up
+ // }
+ // }
+ // prevY = y;
+ // if (prevDirection == direction) {
+ // edgeHitCount++;
+ // } else {
+ // edgeHitCount = 1;
+ // }
+ // if (edgeHitCount >= 2) {
+ // return false;
+ // }
+ // return true;
+ // break;
+ // }
+ // }
+
+ public interface OnVerticalDragListener {
+ void onDrag(final float dY);
+
+ void onDragEnd();
+
+ void onFling(final float flingVelocity);
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/customviews/VideoPlayerCallbackAdapter.java b/app/src/main/java/awais/instagrabber/customviews/VideoPlayerCallbackAdapter.java
new file mode 100644
index 00000000..7e393800
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/customviews/VideoPlayerCallbackAdapter.java
@@ -0,0 +1,18 @@
+package awais.instagrabber.customviews;
+
+public class VideoPlayerCallbackAdapter implements VideoPlayerViewHelper.VideoPlayerCallback {
+ @Override
+ public void onThumbnailLoaded() {}
+
+ @Override
+ public void onThumbnailClick() {}
+
+ @Override
+ public void onPlayerViewLoaded() {}
+
+ @Override
+ public void onPlay() {}
+
+ @Override
+ public void onPause() {}
+}
diff --git a/app/src/main/java/awais/instagrabber/customviews/VideoPlayerViewHelper.java b/app/src/main/java/awais/instagrabber/customviews/VideoPlayerViewHelper.java
new file mode 100644
index 00000000..04e07854
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/customviews/VideoPlayerViewHelper.java
@@ -0,0 +1,371 @@
+package awais.instagrabber.customviews;
+
+import android.content.Context;
+import android.graphics.drawable.Animatable;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.view.ContextThemeWrapper;
+import androidx.appcompat.widget.PopupMenu;
+
+import com.facebook.drawee.backends.pipeline.Fresco;
+import com.facebook.drawee.controller.BaseControllerListener;
+import com.facebook.drawee.interfaces.DraweeController;
+import com.facebook.imagepipeline.image.ImageInfo;
+import com.facebook.imagepipeline.request.ImageRequest;
+import com.facebook.imagepipeline.request.ImageRequestBuilder;
+import com.google.android.exoplayer2.ExoPlaybackException;
+import com.google.android.exoplayer2.MediaItem;
+import com.google.android.exoplayer2.PlaybackParameters;
+import com.google.android.exoplayer2.Player;
+import com.google.android.exoplayer2.SimpleExoPlayer;
+import com.google.android.exoplayer2.audio.AudioListener;
+import com.google.android.exoplayer2.source.ProgressiveMediaSource;
+import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
+
+import awais.instagrabber.R;
+import awais.instagrabber.databinding.LayoutExoCustomControlsBinding;
+import awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding;
+import awais.instagrabber.utils.TextUtils;
+
+import static com.google.android.exoplayer2.C.TIME_UNSET;
+import static com.google.android.exoplayer2.Player.STATE_ENDED;
+import static com.google.android.exoplayer2.Player.STATE_IDLE;
+import static com.google.android.exoplayer2.Player.STATE_READY;
+
+public class VideoPlayerViewHelper implements Player.EventListener {
+ private static final String TAG = "VideoPlayerViewHelper";
+
+ private final Context context;
+ private final awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding binding;
+ private final float initialVolume;
+ private final float thumbnailAspectRatio;
+ private final String thumbnailUrl;
+ private final boolean loadPlayerOnClick;
+ private final awais.instagrabber.databinding.LayoutExoCustomControlsBinding controlsBinding;
+ private final VideoPlayerCallback videoPlayerCallback;
+ private final String videoUrl;
+ private final DefaultDataSourceFactory dataSourceFactory;
+ private SimpleExoPlayer player;
+ private PopupMenu speedPopup;
+
+ public VideoPlayerViewHelper(@NonNull final Context context,
+ @NonNull final LayoutVideoPlayerWithThumbnailBinding binding,
+ @NonNull final String videoUrl,
+ final float initialVolume,
+ final float thumbnailAspectRatio,
+ final String thumbnailUrl,
+ final boolean loadPlayerOnClick,
+ final LayoutExoCustomControlsBinding controlsBinding,
+ final VideoPlayerCallback videoPlayerCallback) {
+ this.context = context;
+ this.binding = binding;
+ this.initialVolume = initialVolume;
+ this.thumbnailAspectRatio = thumbnailAspectRatio;
+ this.thumbnailUrl = thumbnailUrl;
+ this.loadPlayerOnClick = loadPlayerOnClick;
+ this.controlsBinding = controlsBinding;
+ this.videoPlayerCallback = videoPlayerCallback;
+ this.videoUrl = videoUrl;
+ this.dataSourceFactory = new DefaultDataSourceFactory(binding.getRoot().getContext(), "instagram");
+ bind();
+ }
+
+ private void bind() {
+ binding.thumbnailParent.setOnClickListener(v -> {
+ if (videoPlayerCallback != null) {
+ videoPlayerCallback.onThumbnailClick();
+ }
+ if (loadPlayerOnClick) {
+ loadPlayer();
+ }
+ });
+ setThumbnail();
+ setupControls();
+ }
+
+ private void setThumbnail() {
+ binding.thumbnail.setAspectRatio(thumbnailAspectRatio);
+ final ImageRequest thumbnailRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(thumbnailUrl))
+ .build();
+ final DraweeController controller = Fresco.newDraweeControllerBuilder()
+ .setControllerListener(new BaseControllerListener() {
+ @Override
+ public void onFailure(final String id, final Throwable throwable) {
+ if (videoPlayerCallback != null) {
+ videoPlayerCallback.onThumbnailLoaded();
+ }
+ }
+
+ @Override
+ public void onFinalImageSet(final String id,
+ final ImageInfo imageInfo,
+ final Animatable animatable) {
+ if (videoPlayerCallback != null) {
+ videoPlayerCallback.onThumbnailLoaded();
+ }
+ }
+ })
+ .setImageRequest(thumbnailRequest)
+ .build();
+ binding.thumbnail.setController(controller);
+ }
+
+ private void loadPlayer() {
+ if (videoUrl == null) return;
+ if (binding.root.getDisplayedChild() == 0) {
+ binding.root.showNext();
+ }
+ if (videoPlayerCallback != null) {
+ videoPlayerCallback.onPlayerViewLoaded();
+ }
+ player = (SimpleExoPlayer) binding.playerView.getPlayer();
+ if (player != null) {
+ player.release();
+ }
+ player = new SimpleExoPlayer.Builder(context)
+ .setLooper(Looper.getMainLooper())
+ .build();
+ player.addListener(this);
+ player.setVolume(initialVolume);
+ player.setPlayWhenReady(true);
+ player.setRepeatMode(Player.REPEAT_MODE_ALL);
+ final ProgressiveMediaSource.Factory sourceFactory = new ProgressiveMediaSource.Factory(dataSourceFactory);
+ final MediaItem mediaItem = MediaItem.fromUri(videoUrl);
+ final ProgressiveMediaSource mediaSource = sourceFactory.createMediaSource(mediaItem);
+ player.setMediaSource(mediaSource);
+ setupControls();
+ player.prepare();
+ binding.playerView.setPlayer(player);
+ }
+
+ private void setupControls() {
+ if (controlsBinding == null) return;
+ binding.playerView.setUseController(false);
+ if (player == null) {
+ enableControls(false);
+ controlsBinding.playPause.setEnabled(true);
+ controlsBinding.playPause.setOnClickListener(v -> binding.thumbnailParent.performClick());
+ return;
+ }
+ enableControls(true);
+ final Handler handler = new Handler();
+ final long initialDelay = 0;
+ final long recurringDelay = 60;
+ final Runnable positionChecker = new Runnable() {
+ @Override
+ public void run() {
+ handler.removeCallbacks(this);
+ if (player == null) return;
+ final long currentPosition = player.getCurrentPosition();
+ final long duration = player.getDuration();
+ if (duration == TIME_UNSET) {
+ controlsBinding.timeline.setValueFrom(0);
+ controlsBinding.timeline.setValueTo(0);
+ controlsBinding.timeline.setEnabled(false);
+ return;
+ }
+ controlsBinding.timeline.setValue(Math.min(currentPosition, duration));
+ controlsBinding.fromTime.setText(TextUtils.millisToTimeString(currentPosition));
+ handler.postDelayed(this, recurringDelay);
+ }
+ };
+ updatePlayPauseDrawable(player.getPlayWhenReady());
+ updateMuteIcon(player.getVolume());
+ player.addListener(new Player.EventListener() {
+ @Override
+ public void onPlaybackStateChanged(final int state) {
+ switch (state) {
+ case Player.STATE_BUFFERING:
+ case STATE_IDLE:
+ case STATE_ENDED:
+ handler.removeCallbacks(positionChecker);
+ return;
+ case STATE_READY:
+ setupTimeline();
+ handler.postDelayed(positionChecker, initialDelay);
+ break;
+ }
+ }
+
+ @Override
+ public void onPlayWhenReadyChanged(final boolean playWhenReady, final int reason) {
+ updatePlayPauseDrawable(playWhenReady);
+ }
+ });
+ player.addAudioListener(new AudioListener() {
+ @Override
+ public void onVolumeChanged(final float volume) {
+ updateMuteIcon(volume);
+ }
+ });
+ controlsBinding.timeline.addOnChangeListener((slider, value, fromUser) -> {
+ if (!fromUser) return;
+ long actualValue = (long) value;
+ if (actualValue < 0) {
+ actualValue = 0;
+ } else if (actualValue > player.getDuration()) {
+ actualValue = player.getDuration();
+ }
+ player.seekTo(actualValue);
+ });
+ controlsBinding.timeline.setLabelFormatter(value -> TextUtils.millisToTimeString((long) value));
+ controlsBinding.playPause.setOnClickListener(v -> player.setPlayWhenReady(!player.getPlayWhenReady()));
+ controlsBinding.mute.setOnClickListener(v -> toggleMute());
+ controlsBinding.rewWithAmount.setOnClickListener(v -> {
+ final long positionMs = player.getCurrentPosition() - 5000;
+ player.seekTo(positionMs < 0 ? 0 : positionMs);
+ });
+ controlsBinding.ffWithAmount.setOnClickListener(v -> {
+ long positionMs = player.getCurrentPosition() + 5000;
+ long duration = player.getDuration();
+ if (duration == TIME_UNSET) {
+ duration = 0;
+ }
+ player.seekTo(Math.min(positionMs, duration));
+ });
+ controlsBinding.speed.setOnClickListener(this::showMenu);
+ }
+
+ private void setupTimeline() {
+ final long duration = player.getDuration();
+ controlsBinding.timeline.setEnabled(true);
+ controlsBinding.timeline.setValueFrom(0);
+ controlsBinding.timeline.setValueTo(duration);
+ controlsBinding.fromTime.setText(TextUtils.millisToTimeString(0));
+ controlsBinding.toTime.setText(TextUtils.millisToTimeString(duration));
+ }
+
+ private void enableControls(final boolean enable) {
+ controlsBinding.speed.setEnabled(enable);
+ controlsBinding.mute.setEnabled(enable);
+ controlsBinding.ffWithAmount.setEnabled(enable);
+ controlsBinding.rewWithAmount.setEnabled(enable);
+ controlsBinding.fromTime.setEnabled(enable);
+ controlsBinding.toTime.setEnabled(enable);
+ controlsBinding.playPause.setEnabled(enable);
+ }
+
+ public void showMenu(View anchor) {
+ PopupMenu popup = getPopupMenu(anchor);
+ popup.show();
+ }
+
+ @NonNull
+ private PopupMenu getPopupMenu(final View anchor) {
+ if (speedPopup != null) {
+ return speedPopup;
+ }
+ final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(context, R.style.popupMenuStyle);
+ // final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(context, R.style.Widget_MaterialComponents_PopupMenu_Exoplayer);
+ speedPopup = new PopupMenu(themeWrapper, anchor);
+ speedPopup.getMenuInflater().inflate(R.menu.speed_menu, speedPopup.getMenu());
+ speedPopup.setOnMenuItemClickListener(item -> {
+ float nextSpeed;
+ int textResId;
+ int itemId = item.getItemId();
+ if (itemId == R.id.pt_two_five_x) {
+ nextSpeed = 0.25f;
+ textResId = R.string.pt_two_five_x;
+ } else if (itemId == R.id.pt_five_x) {
+ nextSpeed = 0.5f;
+ textResId = R.string.pt_five_x;
+ } else if (itemId == R.id.pt_seven_five_x) {
+ nextSpeed = 0.75f;
+ textResId = R.string.pt_seven_five_x;
+ } else if (itemId == R.id.one_x) {
+ nextSpeed = 1f;
+ textResId = R.string.one_x;
+ } else if (itemId == R.id.one_pt_two_five_x) {
+ nextSpeed = 1.25f;
+ textResId = R.string.one_pt_two_five_x;
+ } else if (itemId == R.id.one_pt_five_x) {
+ nextSpeed = 1.5f;
+ textResId = R.string.one_pt_five_x;
+ } else if (itemId == R.id.two_x) {
+ nextSpeed = 2f;
+ textResId = R.string.two_x;
+ } else {
+ nextSpeed = 1;
+ textResId = R.string.one_x;
+ }
+ player.setPlaybackParameters(new PlaybackParameters(nextSpeed));
+ controlsBinding.speed.setText(textResId);
+ return true;
+ });
+ return speedPopup;
+ }
+
+ private void updateMuteIcon(final float volume) {
+ if (volume == 0) {
+ controlsBinding.mute.setIconResource(R.drawable.ic_volume_off_24);
+ return;
+ }
+ controlsBinding.mute.setIconResource(R.drawable.ic_volume_up_24);
+ }
+
+ private void updatePlayPauseDrawable(final boolean playWhenReady) {
+ if (playWhenReady) {
+ controlsBinding.playPause.setIconResource(R.drawable.ic_pause_24);
+ return;
+ }
+ controlsBinding.playPause.setIconResource(R.drawable.ic_play_arrow_24);
+ }
+
+ @Override
+ public void onPlayWhenReadyChanged(final boolean playWhenReady, final int reason) {
+ if (videoPlayerCallback == null) return;
+ if (playWhenReady) {
+ videoPlayerCallback.onPlay();
+ return;
+ }
+ videoPlayerCallback.onPause();
+ }
+
+ @Override
+ public void onPlayerError(final ExoPlaybackException error) {
+ Log.e(TAG, "onPlayerError", error);
+ }
+
+ public float toggleMute() {
+ if (player == null) return 0;
+ final float vol = player.getVolume() == 0f ? 1f : 0f;
+ player.setVolume(vol);
+ return vol;
+ }
+
+ public void togglePlayback() {
+ if (player == null) return;
+ final int playbackState = player.getPlaybackState();
+ if (playbackState == STATE_IDLE || playbackState == STATE_ENDED) return;
+ final boolean playWhenReady = player.getPlayWhenReady();
+ player.setPlayWhenReady(!playWhenReady);
+ }
+
+ public void releasePlayer() {
+ if (player == null) return;
+ player.release();
+ player = null;
+ }
+
+ public void pause() {
+ if (player == null) return;
+ player.pause();
+ }
+
+ public interface VideoPlayerCallback {
+ void onThumbnailLoaded();
+
+ void onThumbnailClick();
+
+ void onPlayerViewLoaded();
+
+ void onPlay();
+
+ void onPause();
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/customviews/drawee/DefaultZoomableController.java b/app/src/main/java/awais/instagrabber/customviews/drawee/DefaultZoomableController.java
index 0ee5d0cd..7e83dc2f 100644
--- a/app/src/main/java/awais/instagrabber/customviews/drawee/DefaultZoomableController.java
+++ b/app/src/main/java/awais/instagrabber/customviews/drawee/DefaultZoomableController.java
@@ -572,16 +572,16 @@ public class DefaultZoomableController
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;
+ final boolean shouldLimitX = shouldLimit(limitTypes, LIMIT_TRANSLATION_X);
+ float offsetLeft = shouldLimitX
+ ? 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 (mListener != null) {
+ mListener.onTranslationLimited(offsetLeft, offsetTop);
+ }
if (offsetLeft != 0 || offsetTop != 0) {
transform.postTranslate(offsetLeft, offsetTop);
return true;
diff --git a/app/src/main/java/awais/instagrabber/customviews/drawee/DraggableZoomableDraweeView.java b/app/src/main/java/awais/instagrabber/customviews/drawee/DraggableZoomableDraweeView.java
new file mode 100644
index 00000000..c204e8a4
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/customviews/drawee/DraggableZoomableDraweeView.java
@@ -0,0 +1,98 @@
+/*
+ * 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.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Matrix;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+
+import com.facebook.drawee.generic.GenericDraweeHierarchy;
+
+import awais.instagrabber.customviews.VerticalDragHelper;
+import awais.instagrabber.customviews.VerticalDragHelper.OnVerticalDragListener;
+
+public class DraggableZoomableDraweeView extends ZoomableDraweeView {
+ private static final String TAG = "DraggableZoomableDV";
+
+ private VerticalDragHelper verticalDragHelper;
+
+ public DraggableZoomableDraweeView(final Context context, final GenericDraweeHierarchy hierarchy) {
+ super(context, hierarchy);
+ verticalDragHelper = new VerticalDragHelper(this);
+ }
+
+ public DraggableZoomableDraweeView(final Context context) {
+ super(context);
+ verticalDragHelper = new VerticalDragHelper(this);
+ }
+
+ public DraggableZoomableDraweeView(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ verticalDragHelper = new VerticalDragHelper(this);
+ }
+
+ public DraggableZoomableDraweeView(final Context context, final AttributeSet attrs, final int defStyle) {
+ super(context, attrs, defStyle);
+ verticalDragHelper = new VerticalDragHelper(this);
+ }
+
+ public void setOnVerticalDragListener(@NonNull final OnVerticalDragListener onVerticalDragListener) {
+ verticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
+ }
+
+ private int lastPointerCount;
+ private int lastNewPointerCount;
+ private boolean wasTransformCorrected;
+
+ @Override
+ protected void onTransformEnd(final Matrix transform) {
+ super.onTransformEnd(transform);
+ final AnimatedZoomableController zoomableController = (AnimatedZoomableController) getZoomableController();
+ final TransformGestureDetector detector = zoomableController.getDetector();
+ lastNewPointerCount = detector.getNewPointerCount();
+ lastPointerCount = detector.getPointerCount();
+ }
+
+ @Override
+ protected void onTranslationLimited(final float offsetLeft, final float offsetTop) {
+ super.onTranslationLimited(offsetLeft, offsetTop);
+ wasTransformCorrected = offsetTop != 0;
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouchEvent(final MotionEvent event) {
+ boolean superResult = false;
+ if (verticalDragHelper.isDragging()) {
+ final boolean onDragTouch = verticalDragHelper.onDragTouch(event);
+ if (onDragTouch) {
+ return true;
+ }
+ }
+ if (!verticalDragHelper.isDragging()) {
+ superResult = super.onTouchEvent(event);
+ if (wasTransformCorrected
+ && (lastPointerCount == 1 || lastPointerCount == 0)
+ && (lastNewPointerCount == 1 || lastNewPointerCount == 0)) {
+ final boolean onDragTouch = verticalDragHelper.onDragTouch(event);
+ if (onDragTouch) {
+ return true;
+ }
+ }
+ }
+ final boolean gestureListenerResult = verticalDragHelper.onGestureTouchEvent(event);
+ if (gestureListenerResult) {
+ return true;
+ }
+ return superResult;
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/customviews/drawee/MultiZoomableControllerListener.java b/app/src/main/java/awais/instagrabber/customviews/drawee/MultiZoomableControllerListener.java
index b459ece6..578c6ecb 100644
--- a/app/src/main/java/awais/instagrabber/customviews/drawee/MultiZoomableControllerListener.java
+++ b/app/src/main/java/awais/instagrabber/customviews/drawee/MultiZoomableControllerListener.java
@@ -41,6 +41,13 @@ public class MultiZoomableControllerListener implements ZoomableController.Liste
}
}
+ @Override
+ public void onTranslationLimited(final float offsetLeft, final float offsetTop) {
+ for (ZoomableController.Listener listener : mListeners) {
+ listener.onTranslationLimited(offsetLeft, offsetTop);
+ }
+ }
+
public synchronized void addListener(ZoomableController.Listener listener) {
mListeners.add(listener);
}
diff --git a/app/src/main/java/awais/instagrabber/customviews/drawee/ZoomableController.java b/app/src/main/java/awais/instagrabber/customviews/drawee/ZoomableController.java
index 93d83586..dc31ea97 100644
--- a/app/src/main/java/awais/instagrabber/customviews/drawee/ZoomableController.java
+++ b/app/src/main/java/awais/instagrabber/customviews/drawee/ZoomableController.java
@@ -42,6 +42,8 @@ public interface ZoomableController {
* @param transform the current transform matrix
*/
void onTransformEnd(Matrix transform);
+
+ void onTranslationLimited(float offsetLeft, float offsetTop);
}
/**
diff --git a/app/src/main/java/awais/instagrabber/customviews/drawee/ZoomableDraweeView.java b/app/src/main/java/awais/instagrabber/customviews/drawee/ZoomableDraweeView.java
index 5f5f4286..5a7d55ad 100644
--- a/app/src/main/java/awais/instagrabber/customviews/drawee/ZoomableDraweeView.java
+++ b/app/src/main/java/awais/instagrabber/customviews/drawee/ZoomableDraweeView.java
@@ -33,8 +33,6 @@ import com.facebook.drawee.generic.GenericDraweeHierarchyInflater;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.drawee.view.DraweeView;
-import awais.instagrabber.customviews.helpers.SwipeGestureListener;
-
/**
* DraweeView that has zoomable capabilities.
@@ -54,7 +52,7 @@ public class ZoomableDraweeView extends DraweeView
private DraweeController mHugeImageController;
private ZoomableController mZoomableController;
private GestureDetector mTapGestureDetector;
- private boolean mAllowTouchInterceptionWhileZoomed = true;
+ private boolean mAllowTouchInterceptionWhileZoomed = false;
private boolean mIsDialtoneEnabled = false;
private boolean mZoomingEnabled = true;
@@ -76,7 +74,9 @@ public class ZoomableDraweeView extends DraweeView
private final ZoomableController.Listener mZoomableListener =
new ZoomableController.Listener() {
@Override
- public void onTransformBegin(Matrix transform) {}
+ public void onTransformBegin(Matrix transform) {
+ ZoomableDraweeView.this.onTransformBegin(transform);
+ }
@Override
public void onTransformChanged(Matrix transform) {
@@ -84,7 +84,14 @@ public class ZoomableDraweeView extends DraweeView
}
@Override
- public void onTransformEnd(Matrix transform) {}
+ public void onTransformEnd(Matrix transform) {
+ ZoomableDraweeView.this.onTransformEnd(transform);
+ }
+
+ @Override
+ public void onTranslationLimited(final float offsetLeft, final float offsetTop) {
+ ZoomableDraweeView.this.onTranslationLimited(offsetLeft, offsetTop);
+ }
};
private final GestureListenerWrapper mTapListenerWrapper = new GestureListenerWrapper();
@@ -302,11 +309,10 @@ public class ZoomableDraweeView extends DraweeView
int a = event.getActionMasked();
FLog.v(getLogTag(), "onTouchEvent: %d, view %x, received", a, this.hashCode());
if (!mIsDialtoneEnabled && mTapGestureDetector.onTouchEvent(event)) {
- FLog.v(
- getLogTag(),
- "onTouchEvent: %d, view %x, handled by tap gesture detector",
- a,
- this.hashCode());
+ FLog.v(getLogTag(),
+ "onTouchEvent: %d, view %x, handled by tap gesture detector",
+ a,
+ this.hashCode());
return true;
}
@@ -389,23 +395,29 @@ public class ZoomableDraweeView extends DraweeView
mZoomableController.setEnabled(false);
}
+ protected void onTransformBegin(final Matrix transform) {}
+
protected void onTransformChanged(Matrix transform) {
FLog.v(getLogTag(), "onTransformChanged: view %x, transform: %s", this.hashCode(), transform);
maybeSetHugeImageController();
invalidate();
}
+ protected void onTransformEnd(final Matrix transform) {}
+
+ protected void onTranslationLimited(final float offsetLeft, final float offsetTop) {}
+
protected void updateZoomableControllerBounds() {
getImageBounds(mImageBounds);
getLimitBounds(mViewBounds);
+ // Log.d(TAG.getSimpleName(), "updateZoomableControllerBounds: mImageBounds: " + mImageBounds);
mZoomableController.setImageBounds(mImageBounds);
mZoomableController.setViewBounds(mViewBounds);
- FLog.v(
- getLogTag(),
- "updateZoomableControllerBounds: view %x, view bounds: %s, image bounds: %s",
- this.hashCode(),
- mViewBounds,
- mImageBounds);
+ FLog.v(getLogTag(),
+ "updateZoomableControllerBounds: view %x, view bounds: %s, image bounds: %s",
+ this.hashCode(),
+ mViewBounds,
+ mImageBounds);
}
protected Class> getLogTag() {
diff --git a/app/src/main/java/awais/instagrabber/customviews/helpers/GridSpacingItemDecoration.java b/app/src/main/java/awais/instagrabber/customviews/helpers/GridSpacingItemDecoration.java
index c0f24d21..81b4be92 100755
--- a/app/src/main/java/awais/instagrabber/customviews/helpers/GridSpacingItemDecoration.java
+++ b/app/src/main/java/awais/instagrabber/customviews/helpers/GridSpacingItemDecoration.java
@@ -4,7 +4,6 @@ import android.graphics.Rect;
import android.view.View;
import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
@@ -16,16 +15,14 @@ public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
- final RecyclerView.LayoutManager manager = parent.getLayoutManager();
- if (manager instanceof GridLayoutManager) {
- final int spanCount = ((GridLayoutManager) manager).getSpanCount();
- final int position = parent.getChildAdapterPosition(view);
- final int column = position % spanCount;
-
- outRect.left = column * spacing / spanCount;
- outRect.right = spacing - (column + 1) * spacing / spanCount;
- if (position < spanCount) outRect.top = spacing;
- outRect.bottom = spacing;
+ final int halfSpace = spacing / 2;
+ if (parent.getPaddingLeft() != halfSpace) {
+ parent.setPadding(halfSpace, halfSpace, halfSpace, halfSpace);
+ parent.setClipToPadding(false);
}
+ outRect.top = halfSpace;
+ outRect.bottom = halfSpace;
+ outRect.left = halfSpace;
+ outRect.right = halfSpace;
}
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/customviews/helpers/PostFetcher.java b/app/src/main/java/awais/instagrabber/customviews/helpers/PostFetcher.java
new file mode 100644
index 00000000..22145bfa
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/customviews/helpers/PostFetcher.java
@@ -0,0 +1,48 @@
+package awais.instagrabber.customviews.helpers;
+
+import java.util.List;
+
+import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.models.FeedModel;
+
+public class PostFetcher {
+ private final PostFetchService postFetchService;
+ private final FetchListener> fetchListener;
+ private boolean fetching;
+
+ public PostFetcher(final PostFetchService postFetchService,
+ final FetchListener> fetchListener) {
+ this.postFetchService = postFetchService;
+ this.fetchListener = fetchListener;
+ }
+
+ public void fetch() {
+ if (!fetching) {
+ fetching = true;
+ postFetchService.fetch(result -> {
+ fetching = false;
+ fetchListener.onResult(result);
+ });
+ }
+ }
+
+ public void reset() {
+ postFetchService.reset();
+ }
+
+ public boolean isFetching() {
+ return fetching;
+ }
+
+ public boolean hasMore() {
+ return postFetchService.hasNextPage();
+ }
+
+ public interface PostFetchService {
+ void fetch(FetchListener> fetchListener);
+
+ void reset();
+
+ boolean hasNextPage();
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/customviews/helpers/RecyclerLazyLoader.java b/app/src/main/java/awais/instagrabber/customviews/helpers/RecyclerLazyLoader.java
index 6a165451..37de2ff2 100755
--- a/app/src/main/java/awais/instagrabber/customviews/helpers/RecyclerLazyLoader.java
+++ b/app/src/main/java/awais/instagrabber/customviews/helpers/RecyclerLazyLoader.java
@@ -1,23 +1,41 @@
package awais.instagrabber.customviews.helpers;
+import android.os.Handler;
+
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import awais.instagrabber.interfaces.LazyLoadListener;
-// thanks to nesquena's EndlessRecyclerViewScrollListener
-// https://gist.github.com/nesquena/d09dc68ff07e845cc622
+/**
+ * thanks to nesquena's EndlessRecyclerViewScrollListener
+ */
public final class RecyclerLazyLoader extends RecyclerView.OnScrollListener {
- private int currentPage = 0; // The current offset index of data you have loaded
- private int previousTotalItemCount = 0; // The total number of items in the dataset after the last load
- private boolean loading = true; // True if we are still waiting for the last set of data to load.
- private final int visibleThreshold; // The minimum amount of items to have below your current scroll position before loading more.
+ /**
+ * The current offset index of data you have loaded
+ */
+ private int currentPage = 0;
+ /**
+ * The total number of items in the data set after the last load
+ */
+ private int previousTotalItemCount = 0;
+ /**
+ * true
if we are still waiting for the last set of data to load.
+ */
+ private boolean loading = true;
+ /**
+ * The minimum amount of items to have below your current scroll position before loading more.
+ */
+ private final int visibleThreshold;
private final LazyLoadListener lazyLoadListener;
private final RecyclerView.LayoutManager layoutManager;
- public RecyclerLazyLoader(@NonNull final RecyclerView.LayoutManager layoutManager, final LazyLoadListener lazyLoadListener, final int threshold) {
+ public RecyclerLazyLoader(@NonNull final RecyclerView.LayoutManager layoutManager,
+ final LazyLoadListener lazyLoadListener,
+ final int threshold) {
this.layoutManager = layoutManager;
this.lazyLoadListener = lazyLoadListener;
if (threshold > 0) {
@@ -26,6 +44,8 @@ public final class RecyclerLazyLoader extends RecyclerView.OnScrollListener {
}
if (layoutManager instanceof GridLayoutManager) {
this.visibleThreshold = 5 * Math.max(3, ((GridLayoutManager) layoutManager).getSpanCount());
+ } else if (layoutManager instanceof StaggeredGridLayoutManager) {
+ this.visibleThreshold = 4 * Math.max(3, ((StaggeredGridLayoutManager) layoutManager).getSpanCount());
} else if (layoutManager instanceof LinearLayoutManager) {
this.visibleThreshold = ((LinearLayoutManager) layoutManager).getReverseLayout() ? 4 : 8;
} else {
@@ -33,7 +53,8 @@ public final class RecyclerLazyLoader extends RecyclerView.OnScrollListener {
}
}
- public RecyclerLazyLoader(@NonNull final RecyclerView.LayoutManager layoutManager, final LazyLoadListener lazyLoadListener) {
+ public RecyclerLazyLoader(@NonNull final RecyclerView.LayoutManager layoutManager,
+ final LazyLoadListener lazyLoadListener) {
this(layoutManager, lazyLoadListener, -1);
}
@@ -52,22 +73,37 @@ public final class RecyclerLazyLoader extends RecyclerView.OnScrollListener {
previousTotalItemCount = totalItemCount;
}
- final int lastVisibleItemPosition;
+ int lastVisibleItemPosition;
if (layoutManager instanceof GridLayoutManager) {
final GridLayoutManager layoutManager = (GridLayoutManager) this.layoutManager;
lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
+ } else if (layoutManager instanceof StaggeredGridLayoutManager) {
+ final StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) this.layoutManager;
+ final int spanCount = layoutManager.getSpanCount();
+ final int[] lastVisibleItemPositions = layoutManager.findLastVisibleItemPositions(null);
+ lastVisibleItemPosition = 0;
+ for (final int itemPosition : lastVisibleItemPositions) {
+ if (itemPosition > lastVisibleItemPosition) {
+ lastVisibleItemPosition = itemPosition;
+ }
+ }
} else {
final LinearLayoutManager layoutManager = (LinearLayoutManager) this.layoutManager;
lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
}
if (!loading && lastVisibleItemPosition + visibleThreshold > totalItemCount) {
- if (lazyLoadListener != null)
- lazyLoadListener.onLoadMore(++currentPage, totalItemCount);
loading = true;
+ if (lazyLoadListener != null) {
+ new Handler().postDelayed(() -> lazyLoadListener.onLoadMore(++currentPage, totalItemCount), 200);
+ }
}
}
+ public int getCurrentPage() {
+ return currentPage;
+ }
+
public void resetState() {
this.currentPage = 0;
this.previousTotalItemCount = 0;
diff --git a/app/src/main/java/awais/instagrabber/customviews/helpers/RecyclerLazyLoaderAtBottom.java b/app/src/main/java/awais/instagrabber/customviews/helpers/RecyclerLazyLoaderAtBottom.java
new file mode 100644
index 00000000..0c4168b6
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/customviews/helpers/RecyclerLazyLoaderAtBottom.java
@@ -0,0 +1,51 @@
+package awais.instagrabber.customviews.helpers;
+
+import android.os.Handler;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+public final class RecyclerLazyLoaderAtBottom extends RecyclerView.OnScrollListener {
+
+ @NonNull
+ private final RecyclerView.LayoutManager layoutManager;
+ private final LazyLoadListener lazyLoadListener;
+ private int currentPage;
+ private int previousItemCount;
+ private boolean loading;
+
+ public RecyclerLazyLoaderAtBottom(@NonNull final RecyclerView.LayoutManager layoutManager,
+ final LazyLoadListener lazyLoadListener) {
+ this.layoutManager = layoutManager;
+ this.lazyLoadListener = lazyLoadListener;
+ }
+
+ @Override
+ public void onScrollStateChanged(@NonNull final RecyclerView recyclerView, final int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ final int itemCount = layoutManager.getItemCount();
+ if (itemCount > previousItemCount) {
+ loading = false;
+ }
+ if (!recyclerView.canScrollVertically(RecyclerView.SCROLL_AXIS_HORIZONTAL) && newState == RecyclerView.SCROLL_STATE_IDLE) {
+ if (!loading && lazyLoadListener != null) {
+ loading = true;
+ new Handler().postDelayed(() -> lazyLoadListener.onLoadMore(++currentPage), 500);
+ }
+ }
+ }
+
+ public int getCurrentPage() {
+ return currentPage;
+ }
+
+ public void resetState() {
+ currentPage = 0;
+ previousItemCount = 0;
+ loading = true;
+ }
+
+ public interface LazyLoadListener {
+ void onLoadMore(final int page);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/customviews/helpers/VideoAwareRecyclerScroller.java b/app/src/main/java/awais/instagrabber/customviews/helpers/VideoAwareRecyclerScroller.java
index 2cd49f14..263052b8 100755
--- a/app/src/main/java/awais/instagrabber/customviews/helpers/VideoAwareRecyclerScroller.java
+++ b/app/src/main/java/awais/instagrabber/customviews/helpers/VideoAwareRecyclerScroller.java
@@ -70,7 +70,7 @@ public class VideoAwareRecyclerScroller extends RecyclerView.OnScrollListener {
final FeedVideoViewHolder videoHolder = getFirstVideoHolder(recyclerView, firstVisibleItemPos, lastVisibleItemPos);
if (videoHolder == null || videoHolder.getCurrentFeedModel() == null) {
if (currentlyPlayingViewHolder != null) {
- currentlyPlayingViewHolder.stopPlaying();
+ // currentlyPlayingViewHolder.stopPlaying();
currentlyPlayingViewHolder = null;
}
return;
@@ -80,9 +80,9 @@ public class VideoAwareRecyclerScroller extends RecyclerView.OnScrollListener {
return;
}
if (currentlyPlayingViewHolder != null) {
- currentlyPlayingViewHolder.stopPlaying();
+ // currentlyPlayingViewHolder.stopPlaying();
}
- videoHolder.startPlaying();
+ // videoHolder.startPlaying();
currentlyPlayingViewHolder = videoHolder;
}
// boolean processFirstItem = false, processLastItem = false;
@@ -196,7 +196,7 @@ public class VideoAwareRecyclerScroller extends RecyclerView.OnScrollListener {
for (int pos = firstVisibleItemPos; pos <= lastVisibleItemPos; pos++) {
final View view = layoutManager.findViewByPosition(pos);
if (view != null && view.getId() == R.id.videoHolder) {
- final View viewSwitcher = view.findViewById(R.id.view_switcher);
+ final View viewSwitcher = view.findViewById(R.id.root);
if (viewSwitcher == null) {
continue;
}
@@ -220,113 +220,113 @@ public class VideoAwareRecyclerScroller extends RecyclerView.OnScrollListener {
if (currentlyPlayingViewHolder == null) {
return;
}
- currentlyPlayingViewHolder.startPlaying();
+ // currentlyPlayingViewHolder.startPlaying();
}
public void stopPlaying() {
if (currentlyPlayingViewHolder == null) {
return;
}
- currentlyPlayingViewHolder.stopPlaying();
+ // currentlyPlayingViewHolder.stopPlaying();
}
-// private synchronized void attachVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
-// synchronized (LOCK) {
-// if (recyclerView != null) {
-// final RecyclerView.Adapter> adapter = recyclerView.getAdapter();
-// if (adapter instanceof FeedAdapter) {
-// final SimpleExoPlayer pagerPlayer = ((FeedAdapter) adapter).pagerPlayer;
-// if (pagerPlayer != null) pagerPlayer.setPlayWhenReady(false);
-// }
-// }
-// if (itemView == null) {
-// return;
-// }
-// final boolean shouldAutoplay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
-// final FeedModel feedModel = feedModels.get(itemPos);
-// // loadVideo(itemPos, itemView, shouldAutoplay, feedModel);
-// }
-// }
-//
-// private void loadVideo(final int itemPos, final View itemView, final boolean shouldAutoplay, final FeedModel feedModel) {
-// final PlayerView playerView = itemView.findViewById(R.id.playerView);
-// if (playerView == null) {
-// return;
-// }
-// if (player != null) {
-// player.stop(true);
-// player.release();
-// player = null;
-// }
-//
-// player = new SimpleExoPlayer.Builder(context)
-// .setUseLazyPreparation(!shouldAutoplay)
-// .build();
-// player.setPlayWhenReady(shouldAutoplay);
-//
-// final View btnComments = itemView.findViewById(R.id.btnComments);
-// if (btnComments != null) {
-// if (feedModel.getCommentsCount() <= 0) btnComments.setEnabled(false);
-// else {
-// btnComments.setTag(feedModel);
-// btnComments.setEnabled(true);
-// btnComments.setOnClickListener(commentClickListener);
-// }
-// }
-// playerView.setPlayer(player);
-// btnMute = itemView.findViewById(R.id.btnMute);
-// float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
-// if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;
-// player.setVolume(vol);
-//
-// if (btnMute != null) {
-// btnMute.setVisibility(View.VISIBLE);
-// btnMute.setImageResource(vol == 0f ? R.drawable.vol : R.drawable.mute);
-// btnMute.setOnClickListener(muteClickListener);
-// }
-// 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);
-// player.setVolume(vol);
-//
-// playerView.setOnClickListener(v -> player.setPlayWhenReady(!player.getPlayWhenReady()));
-//
-// if (videoChangeCallback != null) videoChangeCallback.playerChanged(itemPos, player);
-// }
-//
-// private void releaseVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
-// // Log.d("AWAISKING_APP", "release: " + itemPos);
-// // if (player != null) {
-// // player.stop(true);
-// // player.release();
-// // }
-// // player = null;
-// }
-//
-// private void playVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
-// // if (player != null) {
-// // final int playbackState = player.getPlaybackState();
-// // if (!player.isPlaying()
-// // || playbackState == Player.STATE_READY || playbackState == Player.STATE_ENDED
-// // ) {
-// // player.setPlayWhenReady(true);
-// // }
-// // }
-// // if (player != null) {
-// // player.setPlayWhenReady(true);
-// // player.getPlaybackState();
-// // }
-// }
-//
-// private void stopVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
-// if (player != null) {
-// player.setPlayWhenReady(false);
-// player.getPlaybackState();
-// }
-// }
+ // private synchronized void attachVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
+ // synchronized (LOCK) {
+ // if (recyclerView != null) {
+ // final RecyclerView.Adapter> adapter = recyclerView.getAdapter();
+ // if (adapter instanceof FeedAdapter) {
+ // final SimpleExoPlayer pagerPlayer = ((FeedAdapter) adapter).pagerPlayer;
+ // if (pagerPlayer != null) pagerPlayer.setPlayWhenReady(false);
+ // }
+ // }
+ // if (itemView == null) {
+ // return;
+ // }
+ // final boolean shouldAutoplay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
+ // final FeedModel feedModel = feedModels.get(itemPos);
+ // // loadVideo(itemPos, itemView, shouldAutoplay, feedModel);
+ // }
+ // }
+ //
+ // private void loadVideo(final int itemPos, final View itemView, final boolean shouldAutoplay, final FeedModel feedModel) {
+ // final PlayerView playerView = itemView.findViewById(R.id.playerView);
+ // if (playerView == null) {
+ // return;
+ // }
+ // if (player != null) {
+ // player.stop(true);
+ // player.release();
+ // player = null;
+ // }
+ //
+ // player = new SimpleExoPlayer.Builder(context)
+ // .setUseLazyPreparation(!shouldAutoplay)
+ // .build();
+ // player.setPlayWhenReady(shouldAutoplay);
+ //
+ // final View btnComments = itemView.findViewById(R.id.btnComments);
+ // if (btnComments != null) {
+ // if (feedModel.getCommentsCount() <= 0) btnComments.setEnabled(false);
+ // else {
+ // btnComments.setTag(feedModel);
+ // btnComments.setEnabled(true);
+ // btnComments.setOnClickListener(commentClickListener);
+ // }
+ // }
+ // playerView.setPlayer(player);
+ // btnMute = itemView.findViewById(R.id.btnMute);
+ // float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
+ // if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;
+ // player.setVolume(vol);
+ //
+ // if (btnMute != null) {
+ // btnMute.setVisibility(View.VISIBLE);
+ // btnMute.setImageResource(vol == 0f ? R.drawable.vol : R.drawable.mute);
+ // btnMute.setOnClickListener(muteClickListener);
+ // }
+ // 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);
+ // player.setVolume(vol);
+ //
+ // playerView.setOnClickListener(v -> player.setPlayWhenReady(!player.getPlayWhenReady()));
+ //
+ // if (videoChangeCallback != null) videoChangeCallback.playerChanged(itemPos, player);
+ // }
+ //
+ // private void releaseVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
+ // // Log.d("AWAISKING_APP", "release: " + itemPos);
+ // // if (player != null) {
+ // // player.stop(true);
+ // // player.release();
+ // // }
+ // // player = null;
+ // }
+ //
+ // private void playVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
+ // // if (player != null) {
+ // // final int playbackState = player.getPlaybackState();
+ // // if (!player.isPlaying()
+ // // || playbackState == Player.STATE_READY || playbackState == Player.STATE_ENDED
+ // // ) {
+ // // player.setPlayWhenReady(true);
+ // // }
+ // // }
+ // // if (player != null) {
+ // // player.setPlayWhenReady(true);
+ // // player.getPlaybackState();
+ // // }
+ // }
+ //
+ // private void stopVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
+ // if (player != null) {
+ // player.setPlayWhenReady(false);
+ // player.getPlaybackState();
+ // }
+ // }
public interface VideoChangeCallback {
void playerChanged(final int itemPos, final SimpleExoPlayer player);
diff --git a/app/src/main/java/awais/instagrabber/dialogs/PostsLayoutPreferencesDialogFragment.java b/app/src/main/java/awais/instagrabber/dialogs/PostsLayoutPreferencesDialogFragment.java
new file mode 100644
index 00000000..9e35ae54
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/dialogs/PostsLayoutPreferencesDialogFragment.java
@@ -0,0 +1,216 @@
+package awais.instagrabber.dialogs;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+
+import awais.instagrabber.R;
+import awais.instagrabber.databinding.DialogPostLayoutPreferencesBinding;
+import awais.instagrabber.models.PostsLayoutPreferences;
+
+import static awais.instagrabber.utils.Utils.settingsHelper;
+
+public class PostsLayoutPreferencesDialogFragment extends DialogFragment {
+
+ private final OnApplyListener onApplyListener;
+ private final PostsLayoutPreferences.Builder preferencesBuilder;
+ private final String layoutPreferenceKey;
+ private DialogPostLayoutPreferencesBinding binding;
+ private Context context;
+
+ public PostsLayoutPreferencesDialogFragment(final String layoutPreferenceKey,
+ @NonNull final OnApplyListener onApplyListener) {
+ this.layoutPreferenceKey = layoutPreferenceKey;
+ final PostsLayoutPreferences preferences = PostsLayoutPreferences.fromJson(settingsHelper.getString(layoutPreferenceKey));
+ this.preferencesBuilder = PostsLayoutPreferences.builder().mergeFrom(preferences);
+ this.onApplyListener = onApplyListener;
+ }
+
+ @Override
+ public void onAttach(@NonNull final Context context) {
+ super.onAttach(context);
+ this.context = context;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
+ binding = DialogPostLayoutPreferencesBinding.inflate(LayoutInflater.from(context), null, false);
+ init();
+ return new MaterialAlertDialogBuilder(context)
+ .setView(binding.getRoot())
+ .setPositiveButton(R.string.apply, (dialog, which) -> {
+ final PostsLayoutPreferences preferences = preferencesBuilder.build();
+ final String json = preferences.getJson();
+ settingsHelper.putString(layoutPreferenceKey, json);
+ onApplyListener.onApply(preferences);
+ })
+ .create();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ final Dialog dialog = getDialog();
+ if (dialog == null) return;
+ final Window window = dialog.getWindow();
+ if (window == null) return;
+ window.setWindowAnimations(R.style.dialog_window_animation);
+ }
+
+ private void init() {
+ initLayoutToggle();
+ if (preferencesBuilder.getType() != PostsLayoutPreferences.PostsLayoutType.LINEAR) {
+ initStaggeredOrGridOptions();
+ }
+ }
+
+ private void initStaggeredOrGridOptions() {
+ initColCountToggle();
+ initNamesToggle();
+ initAvatarsToggle();
+ initCornersToggle();
+ initGapToggle();
+ }
+
+ private void initLayoutToggle() {
+ final int selectedLayoutId = getSelectedLayoutId();
+ binding.layoutToggle.check(selectedLayoutId);
+ if (selectedLayoutId == R.id.layout_linear) {
+ binding.staggeredOrGridOptions.setVisibility(View.GONE);
+ }
+ binding.layoutToggle.addOnButtonCheckedListener((group, checkedId, isChecked) -> {
+ if (isChecked) {
+ if (checkedId == R.id.layout_linear) {
+ preferencesBuilder.setType(PostsLayoutPreferences.PostsLayoutType.LINEAR);
+ preferencesBuilder.setColCount(1);
+ binding.staggeredOrGridOptions.setVisibility(View.GONE);
+ } else if (checkedId == R.id.layout_staggered) {
+ preferencesBuilder.setType(PostsLayoutPreferences.PostsLayoutType.STAGGERED_GRID);
+ if (preferencesBuilder.getColCount() == 1) {
+ preferencesBuilder.setColCount(2);
+ }
+ binding.staggeredOrGridOptions.setVisibility(View.VISIBLE);
+ initStaggeredOrGridOptions();
+ } else {
+ preferencesBuilder.setType(PostsLayoutPreferences.PostsLayoutType.GRID);
+ if (preferencesBuilder.getColCount() == 1) {
+ preferencesBuilder.setColCount(2);
+ }
+ binding.staggeredOrGridOptions.setVisibility(View.VISIBLE);
+ initStaggeredOrGridOptions();
+ }
+ }
+ });
+ }
+
+ private void initColCountToggle() {
+ binding.colCountToggle.check(getSelectedColCountId());
+ binding.colCountToggle.addOnButtonCheckedListener((group, checkedId, isChecked) -> {
+ if (!isChecked) return;
+ if (checkedId == R.id.col_count_two) {
+ preferencesBuilder.setColCount(2);
+ } else {
+ preferencesBuilder.setColCount(3);
+ }
+ });
+ }
+
+ private void initAvatarsToggle() {
+ binding.showAvatarToggle.setChecked(preferencesBuilder.isAvatarVisible());
+ binding.avatarSizeToggle.check(getSelectedAvatarSizeId());
+ binding.showAvatarToggle.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ preferencesBuilder.setAvatarVisible(isChecked);
+ binding.labelAvatarSize.setVisibility(isChecked ? View.VISIBLE : View.GONE);
+ binding.avatarSizeToggle.setVisibility(isChecked ? View.VISIBLE : View.GONE);
+ });
+ binding.labelAvatarSize.setVisibility(preferencesBuilder.isAvatarVisible() ? View.VISIBLE : View.GONE);
+ binding.avatarSizeToggle.setVisibility(preferencesBuilder.isAvatarVisible() ? View.VISIBLE : View.GONE);
+ binding.avatarSizeToggle.addOnButtonCheckedListener((group, checkedId, isChecked) -> {
+ if (!isChecked) return;
+ if (checkedId == R.id.avatar_size_tiny) {
+ preferencesBuilder.setProfilePicSize(PostsLayoutPreferences.ProfilePicSize.TINY);
+ } else if (checkedId == R.id.avatar_size_small) {
+ preferencesBuilder.setProfilePicSize(PostsLayoutPreferences.ProfilePicSize.SMALL);
+ } else {
+ preferencesBuilder.setProfilePicSize(PostsLayoutPreferences.ProfilePicSize.REGULAR);
+ }
+ });
+ }
+
+ private void initNamesToggle() {
+ binding.showNamesToggle.setChecked(preferencesBuilder.isNameVisible());
+ binding.showNamesToggle.setOnCheckedChangeListener((buttonView, isChecked) -> preferencesBuilder.setNameVisible(isChecked));
+ }
+
+ private void initCornersToggle() {
+ binding.cornersToggle.check(getSelectedCornersId());
+ binding.cornersToggle.addOnButtonCheckedListener((group, checkedId, isChecked) -> {
+ if (!isChecked) return;
+ if (checkedId == R.id.corners_round) {
+ preferencesBuilder.setHasRoundedCorners(true);
+ return;
+ }
+ preferencesBuilder.setHasRoundedCorners(false);
+ });
+ }
+
+ private void initGapToggle() {
+ binding.showGapToggle.setChecked(preferencesBuilder.getHasGap());
+ binding.showGapToggle.setOnCheckedChangeListener((buttonView, isChecked) -> preferencesBuilder.setHasGap(isChecked));
+ }
+
+ private int getSelectedLayoutId() {
+ switch (preferencesBuilder.getType()) {
+ case STAGGERED_GRID:
+ return R.id.layout_staggered;
+ case LINEAR:
+ return R.id.layout_linear;
+ default:
+ case GRID:
+ return R.id.layout_grid;
+ }
+ }
+
+ private int getSelectedColCountId() {
+ switch (preferencesBuilder.getColCount()) {
+ case 2:
+ return R.id.col_count_two;
+ case 3:
+ default:
+ return R.id.col_count_three;
+ }
+ }
+
+ private int getSelectedCornersId() {
+ if (preferencesBuilder.getHasRoundedCorners()) {
+ return R.id.corners_round;
+ }
+ return R.id.corners_square;
+ }
+
+ private int getSelectedAvatarSizeId() {
+ switch (preferencesBuilder.getProfilePicSize()) {
+ case TINY:
+ return R.id.avatar_size_tiny;
+ case SMALL:
+ return R.id.avatar_size_small;
+ case REGULAR:
+ default:
+ return R.id.avatar_size_regular;
+ }
+ }
+
+ public interface OnApplyListener {
+ void onApply(final PostsLayoutPreferences preferences);
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/dialogs/ProfilePicDialogFragment.java b/app/src/main/java/awais/instagrabber/dialogs/ProfilePicDialogFragment.java
index 4ea3eed4..6a468cf3 100644
--- a/app/src/main/java/awais/instagrabber/dialogs/ProfilePicDialogFragment.java
+++ b/app/src/main/java/awais/instagrabber/dialogs/ProfilePicDialogFragment.java
@@ -28,7 +28,6 @@ import com.facebook.imagepipeline.image.ImageInfo;
import java.io.File;
import awais.instagrabber.R;
-import awais.instagrabber.asyncs.DownloadAsync;
import awais.instagrabber.asyncs.ProfilePictureFetcher;
import awais.instagrabber.databinding.DialogProfilepicBinding;
import awais.instagrabber.interfaces.FetchListener;
@@ -147,54 +146,9 @@ public class ProfilePicDialogFragment extends DialogFragment {
if (context == null) return;
if (dir.exists() || dir.mkdirs()) {
final File saveFile = new File(dir, name + '_' + System.currentTimeMillis() + ".jpg");
- new DownloadAsync(context,
- url,
- saveFile,
- result -> {
- final int toastRes = result != null && result.exists() ?
- R.string.downloader_downloaded_in_folder : R.string.downloader_error_download_file;
- Toast.makeText(context, toastRes, Toast.LENGTH_SHORT).show();
- })
- .setItems(null, name)
- .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ DownloadUtils.download(context, url, saveFile.getAbsolutePath());
return;
}
Toast.makeText(context, R.string.downloader_error_creating_folder, Toast.LENGTH_SHORT).show();
}
-
- // 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);
- // }
- // }
}
diff --git a/app/src/main/java/awais/instagrabber/directdownload/DirectDownload.java b/app/src/main/java/awais/instagrabber/directdownload/DirectDownload.java
deleted file mode 100755
index 39819e64..00000000
--- a/app/src/main/java/awais/instagrabber/directdownload/DirectDownload.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package awais.instagrabber.directdownload;
-
-import android.Manifest;
-import android.app.Activity;
-import android.app.Notification;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.view.WindowManager;
-import android.widget.Toast;
-
-import androidx.annotation.Nullable;
-import androidx.core.app.ActivityCompat;
-import androidx.core.app.NotificationCompat;
-import androidx.core.app.NotificationManagerCompat;
-import androidx.core.content.ContextCompat;
-
-import java.util.Arrays;
-
-import awais.instagrabber.R;
-import awais.instagrabber.asyncs.PostFetcher;
-import awais.instagrabber.interfaces.FetchListener;
-import awais.instagrabber.models.IntentModel;
-import awais.instagrabber.models.ViewerPostModel;
-import awais.instagrabber.models.enums.DownloadMethod;
-import awais.instagrabber.models.enums.IntentModelType;
-import awais.instagrabber.utils.Constants;
-import awais.instagrabber.utils.DownloadUtils;
-import awais.instagrabber.utils.IntentUtils;
-import awais.instagrabber.utils.TextUtils;
-
-public final class DirectDownload extends Activity {
- private boolean isFound = false;
- private Intent intent;
- private Context context;
- private NotificationManagerCompat notificationManager;
-
- @Override
- protected void onCreate(@Nullable final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- notificationManager = NotificationManagerCompat.from(getApplicationContext());
- }
-
- @Override
- public void onWindowAttributesChanged(final WindowManager.LayoutParams params) {
- super.onWindowAttributesChanged(params);
- if (!isFound) {
- intent = getIntent();
- context = getApplicationContext();
- if (intent != null && context != null) {
- isFound = true;
- checkIntent();
- }
- }
- }
-
- @Override
- public Resources getResources() {
- if (!isFound) {
- intent = getIntent();
- context = getApplicationContext();
- if (intent != null && context != null) {
- isFound = true;
- checkIntent();
- }
- }
- return super.getResources();
- }
-
- private synchronized void checkIntent() {
- if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
- doDownload();
- else {
- final Handler handler = new Handler(Looper.getMainLooper());
- handler.post(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(context, R.string.direct_download_perms_ask, Toast.LENGTH_LONG).show();
- handler.removeCallbacks(this);
- }
- });
- ActivityCompat.requestPermissions(this, DownloadUtils.PERMS, 8020);
- }
- finish();
- }
-
- private synchronized void doDownload() {
- final String action = intent.getAction();
- if (TextUtils.isEmpty(action) || Intent.ACTION_MAIN.equals(action)) return;
- boolean error = true;
-
- String data = null;
- final Bundle extras = intent.getExtras();
- if (extras != null) {
- final Object extraData = extras.get(Intent.EXTRA_TEXT);
- if (extraData != null) {
- error = false;
- data = extraData.toString();
- }
- }
-
- if (error) {
- final Uri intentData = intent.getData();
- if (intentData != null) data = intentData.toString();
- }
-
- if (data != null && !TextUtils.isEmpty(data)) {
- final IntentModel model = IntentUtils.parseUrl(data);
- if (model != null && model.getType() == IntentModelType.POST) {
- final String text = model.getText();
-
- new PostFetcher(text, new FetchListener() {
- @Override
- public void doBefore() {
- final Notification fetchingPostNotif = new NotificationCompat.Builder(context, Constants.DOWNLOAD_CHANNEL_ID)
- .setCategory(NotificationCompat.CATEGORY_STATUS).setSmallIcon(R.mipmap.ic_launcher)
- .setAutoCancel(false).setPriority(NotificationCompat.PRIORITY_MIN)
- .setContentText(context.getString(R.string.direct_download_loading)).build();
- notificationManager.notify(1900000000, fetchingPostNotif);
- }
-
- @Override
- public void onResult(final ViewerPostModel[] result) {
- if (notificationManager != null) notificationManager.cancel(1900000000);
- if (result != null) {
- if (result.length == 1) {
- DownloadUtils.batchDownload(context, result[0].getProfileModel().getUsername(), DownloadMethod.DOWNLOAD_DIRECT,
- Arrays.asList(result));
- } else if (result.length > 1) {
- context.startActivity(new Intent(context, MultiDirectDialog.class)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
- .putExtra(Constants.EXTRAS_POST, result));
- }
- }
- }
- }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/directdownload/MultiDirectDialog.java b/app/src/main/java/awais/instagrabber/directdownload/MultiDirectDialog.java
deleted file mode 100755
index 72553a9c..00000000
--- a/app/src/main/java/awais/instagrabber/directdownload/MultiDirectDialog.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package awais.instagrabber.directdownload;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuItem;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.widget.Toolbar;
-import androidx.recyclerview.widget.RecyclerView;
-
-import java.util.ArrayList;
-
-import awais.instagrabber.R;
-import awais.instagrabber.activities.BaseLanguageActivity;
-import awais.instagrabber.adapters.PostsAdapter;
-import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
-import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
-import awais.instagrabber.models.BasePostModel;
-import awais.instagrabber.models.PostModel;
-import awais.instagrabber.models.ViewerPostModel;
-import awais.instagrabber.models.enums.DownloadMethod;
-import awais.instagrabber.utils.Constants;
-import awais.instagrabber.utils.DownloadUtils;
-import awais.instagrabber.utils.Utils;
-
-public final class MultiDirectDialog extends BaseLanguageActivity {
- public final ArrayList selectedItems = new ArrayList<>();
- private PostsAdapter postsAdapter;
- private MenuItem btnDownload;
- private String username = null;
-
- @Override
- protected void onCreate(@Nullable final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.dialog_direct);
-
- final Toolbar toolbar = findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- final ViewerPostModel[] postModels;
- final Intent intent = getIntent();
- if (intent == null || !intent.hasExtra(Constants.EXTRAS_POST)
- || (postModels = (ViewerPostModel[]) intent.getSerializableExtra(Constants.EXTRAS_POST)) == null) {
- Utils.errorFinish(this);
- return;
- }
-
- username = postModels[0].getProfileModel().getUsername();
- toolbar.setTitle(username);
- toolbar.setSubtitle(postModels[0].getShortCode());
-
- final RecyclerView recyclerView = findViewById(R.id.mainPosts);
- recyclerView.setNestedScrollingEnabled(false);
- recyclerView.setLayoutManager(new GridAutofitLayoutManager(this, Utils.convertDpToPx(130)));
- recyclerView.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
-
- final ArrayList models = new ArrayList<>(postModels.length - 1);
- for (final ViewerPostModel postModel : postModels)
- models.add(new PostModel(postModel.getItemType(), postModel.getPostId(), postModel.getDisplayUrl(),
- postModel.getSliderDisplayUrl(), postModel.getShortCode(), postModel.getPostCaption(), postModel.getTimestamp(),
- postModel.getLike(), postModel.getBookmark(), postModel.getLikes()));
-
- // postsAdapter = new PostsAdapter(models, v -> {
- // final Object tag = v.getTag();
- // if (tag instanceof PostModel) {
- // final PostModel postModel = (PostModel) tag;
- // if (postsAdapter.isSelecting) toggleSelection(postModel);
- // else {
- // Utils.batchDownload(this, username, DownloadMethod.DOWNLOAD_DIRECT, Collections.singletonList(postModel));
- // finish();
- // }
- // }
- // }, v -> {
- // final Object tag = v.getTag();
- // if (tag instanceof PostModel) {
- // postsAdapter.isSelecting = true;
- // toggleSelection((PostModel) tag);
- // }
- // return true;
- // });
-
- recyclerView.setAdapter(postsAdapter);
- }
-
- @Override
- public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
- DownloadUtils.batchDownload(this, username, DownloadMethod.DOWNLOAD_DIRECT, selectedItems);
- finish();
- return true;
- }
-
- @Override
- public boolean onCreateOptionsMenu(final Menu menu) {
- getMenuInflater().inflate(R.menu.menu, menu);
- btnDownload = menu.findItem(R.id.action_download);
- menu.findItem(R.id.action_search).setVisible(false);
- return true;
- }
-
- private void toggleSelection(final PostModel postModel) {
- if (postModel != null && postsAdapter != null) {
- if (postModel.isSelected()) selectedItems.remove(postModel);
- else selectedItems.add(postModel);
- postModel.setSelected(!postModel.isSelected());
- notifyAdapter(postModel);
- }
- }
-
- private void notifyAdapter(final PostModel postModel) {
- // if (selectedItems.size() < 1) postsAdapter.isSelecting = false;
- // if (postModel.getPosition() < 0) postsAdapter.notifyDataSetChanged();
- // else postsAdapter.notifyItemChanged(postModel.getPosition(), postModel);
- //
- // if (btnDownload != null) btnDownload.setVisible(postsAdapter.isSelecting);
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java
index 09ae3bd8..37be0268 100644
--- a/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/CommentsViewerFragment.java
@@ -12,69 +12,129 @@ import android.text.TextWatcher;
import android.text.style.RelativeSizeSpan;
import android.util.Log;
import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
-import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.SearchView;
-import androidx.fragment.app.Fragment;
+import androidx.appcompat.widget.LinearLayoutCompat;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
+import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
+
import awais.instagrabber.R;
import awais.instagrabber.adapters.CommentsAdapter;
import awais.instagrabber.asyncs.CommentsFetcher;
import awais.instagrabber.databinding.FragmentCommentsBinding;
import awais.instagrabber.dialogs.ProfilePicDialogFragment;
-import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
+import awais.instagrabber.viewmodels.CommentsViewModel;
import awais.instagrabber.webservices.MediaService;
import awais.instagrabber.webservices.ServiceCallback;
import static android.content.Context.INPUT_METHOD_SERVICE;
-public final class CommentsViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
+public final class CommentsViewerFragment extends BottomSheetDialogFragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "CommentsViewerFragment";
private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
private CommentsAdapter commentsAdapter;
- private CommentModel commentModel;
private FragmentCommentsBinding binding;
private String shortCode;
private String userId;
private Resources resources;
private InputMethodManager imm;
private AppCompatActivity fragmentActivity;
- private LinearLayout root;
+ private LinearLayoutCompat root;
private boolean shouldRefresh = true;
private MediaService mediaService;
private String postId;
+ private CommentsViewModel commentsViewModel;
+
+ private final CommentsAdapter.CommentCallback commentCallback = new CommentsAdapter.CommentCallback() {
+ @Override
+ public void onClick(final CommentModel comment) {
+ onCommentClick(comment);
+ }
+
+ @Override
+ public void onHashtagClick(final String hashtag) {
+ final NavDirections action = CommentsViewerFragmentDirections.actionGlobalHashTagFragment(hashtag);
+ NavHostFragment.findNavController(CommentsViewerFragment.this).navigate(action);
+ }
+
+ @Override
+ public void onMentionClick(final String mention) {
+ openProfile(mention);
+ }
+
+ @Override
+ public void onURLClick(final String url) {
+ Utils.openURL(getContext(), url);
+ }
+
+ @Override
+ public void onEmailClick(final String emailAddress) {
+ Utils.openEmailAddress(getContext(), emailAddress);
+ }
+ };
+ private final View.OnClickListener newCommentListener = v -> {
+ final Editable text = binding.commentText.getText();
+ final Context context = getContext();
+ if (context == null) return;
+ if (text == null || TextUtils.isEmpty(text.toString())) {
+ Toast.makeText(context, R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ final String userId = CookieUtils.getUserIdFromCookie(cookie);
+ if (userId == null) return;
+ String replyToId = null;
+ final CommentModel commentModel = commentsAdapter.getSelected();
+ if (commentModel != null) {
+ replyToId = commentModel.getId();
+ }
+ mediaService.comment(postId, text.toString(), userId, replyToId, CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback() {
+ @Override
+ public void onSuccess(final Boolean result) {
+ commentsAdapter.clearSelection();
+ binding.commentText.setText("");
+ if (!result) {
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ onRefresh();
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "Error during comment", t);
+ Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ });
+ };
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentActivity = (AppCompatActivity) getActivity();
mediaService = MediaService.getInstance();
- setHasOptionsMenu(true);
+ // setHasOptionsMenu(true);
}
@NonNull
@@ -85,6 +145,8 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
return root;
}
binding = FragmentCommentsBinding.inflate(getLayoutInflater());
+ binding.swipeRefreshLayout.setEnabled(false);
+ binding.swipeRefreshLayout.setNestedScrollingEnabled(false);
root = binding.getRoot();
return root;
}
@@ -96,34 +158,33 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
shouldRefresh = false;
}
- @Override
- public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
- inflater.inflate(R.menu.follow, menu);
- menu.findItem(R.id.action_compare).setVisible(false);
- 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;
- }
- });
- }
+ // @Override
+ // public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
+ // inflater.inflate(R.menu.follow, menu);
+ // menu.findItem(R.id.action_compare).setVisible(false);
+ // 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;
+ // }
+ // });
+ // }
@Override
public void onRefresh() {
binding.swipeRefreshLayout.setRefreshing(true);
new CommentsFetcher(shortCode, commentModels -> {
+ commentsViewModel.getList().postValue(commentModels);
binding.swipeRefreshLayout.setRefreshing(false);
- commentsAdapter = new CommentsAdapter(commentModels, true, clickListener, mentionClickListener);
- binding.rvComments.setAdapter(commentsAdapter);
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@@ -133,9 +194,14 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
shortCode = fragmentArgs.getShortCode();
postId = fragmentArgs.getPostId();
userId = fragmentArgs.getPostUserId();
- setTitle();
+ // setTitle();
binding.swipeRefreshLayout.setOnRefreshListener(this);
binding.swipeRefreshLayout.setRefreshing(true);
+ commentsViewModel = new ViewModelProvider(this).get(CommentsViewModel.class);
+ binding.rvComments.setLayoutManager(new LinearLayoutManager(getContext()));
+ commentsAdapter = new CommentsAdapter(commentCallback);
+ binding.rvComments.setAdapter(commentsAdapter);
+ commentsViewModel.getList().observe(getViewLifecycleOwner(), commentsAdapter::submitList);
resources = getResources();
if (!TextUtils.isEmpty(cookie)) {
binding.commentField.setStartIconVisible(false);
@@ -155,121 +221,107 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
public void afterTextChanged(final Editable s) {}
});
binding.commentField.setStartIconOnClickListener(v -> {
- if (commentModel != null) {
- final View focus = binding.rvComments.findViewWithTag(commentModel);
- focus.setBackgroundColor(0x00000000);
- commentModel = null;
- }
+ commentsAdapter.clearSelection();
binding.commentText.setText("");
});
binding.commentField.setEndIconOnClickListener(newCommentListener);
}
- new CommentsFetcher(this.shortCode, commentModels -> {
- commentsAdapter = new CommentsAdapter(commentModels, true, clickListener, mentionClickListener);
- binding.rvComments.setAdapter(commentsAdapter);
- binding.swipeRefreshLayout.setRefreshing(false);
- }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ onRefresh();
}
- private void setTitle() {
- final ActionBar actionBar = fragmentActivity.getSupportActionBar();
- if (actionBar == null) return;
- actionBar.setTitle(R.string.title_comments);
+ // private void setTitle() {
+ // final ActionBar actionBar = fragmentActivity.getSupportActionBar();
+ // if (actionBar == null) return;
+ // actionBar.setTitle(R.string.title_comments);
// actionBar.setSubtitle(shortCode);
- }
+ // }
- final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
+ private void onCommentClick(final CommentModel commentModel) {
+ 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;
+
+ final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
+ if (!TextUtils.isEmpty(cookie)
+ && userIdFromCookie != null
+ && (userIdFromCookie.equals(commentModel.getProfileModel().getId()) || userIdFromCookie.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 (!TextUtils.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)
+ };
+ }
final Context context = getContext();
if (context == null) return;
- if (commentModel == null) {
- Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
- return;
- }
- final ProfileModel profileModel = commentModel.getProfileModel();
- switch (which) {
- case 0: // open profile
- openProfile(profileModel.getUsername());
- break;
- case 1: // view profile pic
- final FragmentManager fragmentManager = getParentFragmentManager();
- final ProfilePicDialogFragment fragment = new ProfilePicDialogFragment(profileModel.getId(),
- profileModel.getUsername(),
- profileModel.getHdProfilePic());
- final FragmentTransaction ft = fragmentManager.beginTransaction();
- ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
- .add(fragment, "profilePicDialog")
- .commit();
- break;
- case 2: // copy username
- Utils.copyText(context, profileModel.getUsername());
- break;
- case 3: // copy comment
- Utils.copyText(context, commentModel.getText().toString());
- break;
- case 4: // reply to comment
- final View focus = binding.rvComments.findViewWithTag(commentModel);
- focus.setBackgroundColor(0x80888888);
- String mention = "@" + profileModel.getUsername() + " ";
- binding.commentText.setText(mention);
- binding.commentText.requestFocus();
- binding.commentText.setSelection(mention.length());
- binding.commentText.postDelayed(new Runnable() {
- @Override
- public void run() {
+ final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
+ final ProfileModel profileModel = commentModel.getProfileModel();
+ final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
+ switch (which) {
+ case 0: // open profile
+ openProfile("@" + profileModel.getUsername());
+ break;
+ case 1: // view profile pic
+ final FragmentManager fragmentManager = getParentFragmentManager();
+ final ProfilePicDialogFragment fragment = new ProfilePicDialogFragment(profileModel.getId(),
+ profileModel.getUsername(),
+ profileModel.getHdProfilePic());
+ final FragmentTransaction ft = fragmentManager.beginTransaction();
+ ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
+ .add(fragment, "profilePicDialog")
+ .commit();
+ break;
+ case 2: // copy username
+ Utils.copyText(context, profileModel.getUsername());
+ break;
+ // case 3: // copy comment
+ // Utils.copyText(context, commentModel.getText().toString());
+ // break;
+ case 3: // reply to comment
+ // final View focus = binding.rvComments.findViewWithTag(commentModel);
+ // focus.setBackgroundColor(0x80888888);
+ commentsAdapter.setSelected(commentModel);
+ String mention = "@" + profileModel.getUsername() + " ";
+ binding.commentText.setText(mention);
+ binding.commentText.requestFocus();
+ binding.commentText.setSelection(mention.length());
+ binding.commentText.postDelayed(() -> {
imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
if (imm == null) return;
imm.showSoftInput(binding.commentText, 0);
+ }, 200);
+ break;
+ case 4: // like/unlike comment
+ if (csrfToken == null) {
+ return;
}
- }, 200);
- break;
- case 5: // like/unlike comment
- if (!commentModel.getLiked()) {
- mediaService.commentLike(commentModel.getId(), CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback() {
- @Override
- public void onSuccess(final Boolean result) {
- commentModel = null;
- if (!result) {
- Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
- return;
- }
- onRefresh();
- }
-
- @Override
- public void onFailure(final Throwable t) {
- Log.e(TAG, "Error liking comment", t);
- Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
- }
- });
- return;
- }
- mediaService.commentUnlike(commentModel.getId(), CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback() {
- @Override
- public void onSuccess(final Boolean result) {
- commentModel = null;
- if (!result) {
- Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
- return;
- }
- onRefresh();
- }
-
- @Override
- public void onFailure(final Throwable t) {
- Log.e(TAG, "Error unliking comment", t);
- Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
- }
- });
- break;
- case 6: // delete comment
- final String userId = CookieUtils.getUserIdFromCookie(cookie);
- if (userId == null) return;
- mediaService.deleteComment(
- postId, userId, commentModel.getId(), CookieUtils.getCsrfTokenFromCookie(cookie),
- new ServiceCallback() {
+ if (!commentModel.getLiked()) {
+ mediaService.commentLike(commentModel.getId(), csrfToken, new ServiceCallback() {
@Override
public void onSuccess(final Boolean result) {
- commentModel = null;
if (!result) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
@@ -279,112 +331,62 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
@Override
public void onFailure(final Throwable t) {
- Log.e(TAG, "Error deleting comment", t);
+ Log.e(TAG, "Error liking comment", t);
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
- break;
- }
- };
+ return;
+ }
+ mediaService.commentUnlike(commentModel.getId(), csrfToken, new ServiceCallback() {
+ @Override
+ public void onSuccess(final Boolean result) {
+ if (!result) {
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ onRefresh();
+ }
- private final View.OnClickListener clickListener = v -> {
- final Object tag = v.getTag();
- if (tag instanceof CommentModel) {
- commentModel = (CommentModel) tag;
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "Error unliking comment", t);
+ Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ });
+ break;
+ case 5: // delete comment
+ final String userId = CookieUtils.getUserIdFromCookie(cookie);
+ if (userId == null) return;
+ mediaService.deleteComment(
+ postId, userId, commentModel.getId(), csrfToken,
+ new ServiceCallback() {
+ @Override
+ public void onSuccess(final Boolean result) {
+ if (!result) {
+ Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ onRefresh();
+ }
- 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;
-
- final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
- if (!TextUtils.isEmpty(cookie)
- && userIdFromCookie != null
- && (userIdFromCookie.equals(commentModel.getProfileModel().getId()) || userIdFromCookie.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 (!TextUtils.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)
- };
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "Error deleting comment", t);
+ Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ });
+ break;
}
- final Context context = getContext();
- if (context == null) return;
- new AlertDialog.Builder(context)
- .setTitle(title)
- .setItems(commentDialogList, profileDialogListener)
- .setNegativeButton(R.string.cancel, null)
- .show();
- }
- };
-
- private final MentionClickListener mentionClickListener = (view, text, isHashtag, isLocation) -> {
- if (isHashtag) {
- final NavDirections action = CommentsViewerFragmentDirections.actionGlobalHashTagFragment(text);
- NavHostFragment.findNavController(this).navigate(action);
- return;
- }
- openProfile(text);
- };
-
- private final View.OnClickListener newCommentListener = v -> {
- final Editable text = binding.commentText.getText();
- final Context context = getContext();
- if (context == null) return;
- if (text == null || TextUtils.isEmpty(text.toString())) {
- Toast.makeText(context, R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show();
- return;
- }
- final String userId = CookieUtils.getUserIdFromCookie(cookie);
- if (userId == null) return;
- String replyToId = null;
- if (commentModel != null) {
- replyToId = commentModel.getId();
- }
- mediaService.comment(postId, text.toString(), userId, replyToId, CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback() {
- @Override
- public void onSuccess(final Boolean result) {
- commentModel = null;
- binding.commentText.setText("");
- if (!result) {
- Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
- return;
- }
- onRefresh();
- }
-
- @Override
- public void onFailure(final Throwable t) {
- Log.e(TAG, "Error during comment", t);
- Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
- }
- });
- };
+ };
+ new AlertDialog.Builder(context)
+ .setTitle(title)
+ .setItems(commentDialogList, profileDialogListener)
+ .setNegativeButton(R.string.cancel, null)
+ .show();
+ }
private void openProfile(final String username) {
- final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment("@" + username);
+ final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment(username);
NavHostFragment.findNavController(this).navigate(action);
}
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
index 9eab384e..721940df 100644
--- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
@@ -1,7 +1,7 @@
package awais.instagrabber.fragments;
import android.content.Context;
-import android.content.DialogInterface;
+import android.content.pm.PackageManager;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -12,6 +12,8 @@ import android.text.style.StyleSpan;
import android.util.Log;
import android.view.ActionMode;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@@ -22,81 +24,79 @@ import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.app.AlertDialog;
+import androidx.core.content.PermissionChecker;
import androidx.fragment.app.Fragment;
-import androidx.lifecycle.ViewModelProvider;
+import androidx.navigation.NavController;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.snackbar.BaseTransientBottomBar;
import com.google.android.material.snackbar.Snackbar;
+import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.Set;
import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity;
-import awais.instagrabber.adapters.PostsAdapter;
+import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.asyncs.HashtagFetcher;
-import awais.instagrabber.asyncs.PostsFetcher;
+import awais.instagrabber.asyncs.HashtagPostFetchService;
import awais.instagrabber.customviews.PrimaryActionModeCallback;
-import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
-import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
-import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.FragmentHashtagBinding;
-import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
+import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.HashtagModel;
-import awais.instagrabber.models.PostModel;
+import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.StoryModel;
-import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.FavoriteType;
-import awais.instagrabber.models.enums.PostItemType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DataBox;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
-import awais.instagrabber.viewmodels.PostsViewModel;
import awais.instagrabber.webservices.ServiceCallback;
import awais.instagrabber.webservices.StoriesService;
import awais.instagrabber.webservices.TagsService;
import awaisomereport.LogCollector;
+import static androidx.core.content.PermissionChecker.checkSelfPermission;
+import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "HashTagFragment";
+ private static final int STORAGE_PERM_REQUEST_CODE = 8020;
+ private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
+
+ public static final String ARG_HASHTAG = "hashtag";
private MainActivity fragmentActivity;
private FragmentHashtagBinding binding;
private NestedCoordinatorLayout root;
- private boolean shouldRefresh = true, hasStories = false;
+ private boolean shouldRefresh = true;
+ private boolean hasStories = false;
private String hashtag;
private HashtagModel hashtagModel;
- private PostsViewModel postsViewModel;
- private PostsAdapter postsAdapter;
private ActionMode actionMode;
private StoriesService storiesService;
- private boolean hasNextPage;
- private String endCursor;
private AsyncTask, ?, ?> currentlyExecuting;
private boolean isLoggedIn;
private TagsService tagsService;
- private boolean isPullToRefresh;
+ private boolean storiesFetching;
+ private Set selectedFeedModels;
+ private FeedModel downloadFeedModel;
+ private int downloadChildPosition = -1;
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
@Override
public void handleOnBackPressed() {
- setEnabled(false);
- remove();
- if (postsAdapter == null) return;
- postsAdapter.clearSelection();
+ binding.posts.endSelection();
}
};
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
@@ -104,53 +104,146 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
new PrimaryActionModeCallback.CallbacksHelper() {
@Override
public void onDestroy(final ActionMode mode) {
- onBackPressedCallback.handleOnBackPressed();
+ binding.posts.endSelection();
}
@Override
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
if (item.getItemId() == R.id.action_download) {
- if (postsAdapter == null || hashtag == null) {
- return false;
- }
+ if (HashTagFragment.this.selectedFeedModels == null) return false;
final Context context = getContext();
if (context == null) return false;
- DownloadUtils.batchDownload(context,
- hashtag,
- DownloadMethod.DOWNLOAD_MAIN,
- postsAdapter.getSelectedModels());
- checkAndResetAction();
+ if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
+ DownloadUtils.download(context, ImmutableList.copyOf(HashTagFragment.this.selectedFeedModels));
+ binding.posts.endSelection();
+ return true;
+ }
+ requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE_FOR_SELECTION);
return true;
}
return false;
}
});
- private final FetchListener> postsFetchListener = new FetchListener>() {
+ private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
@Override
- public void onResult(final List result) {
- binding.swipeRefreshLayout.setRefreshing(false);
- if (result == null) return;
- binding.mainPosts.post(() -> binding.mainPosts.setVisibility(View.VISIBLE));
- final List postModels = postsViewModel.getList().getValue();
- List finalList = postModels == null || postModels.isEmpty()
- ? new ArrayList<>()
- : new ArrayList<>(postModels);
- if (isPullToRefresh) {
- finalList = result;
- isPullToRefresh = false;
- } else {
- finalList.addAll(result);
+ public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
+ openPostDialog(feedModel, profilePicView, mainPostImage, -1);
+ }
+
+ @Override
+ public void onSliderClick(final FeedModel feedModel, final int position) {
+ openPostDialog(feedModel, null, null, position);
+ }
+
+ @Override
+ public void onCommentsClick(final FeedModel feedModel) {
+ final NavDirections commentsAction = HashTagFragmentDirections.actionGlobalCommentsViewerFragment(
+ feedModel.getShortCode(),
+ feedModel.getPostId(),
+ feedModel.getProfileModel().getId()
+ );
+ NavHostFragment.findNavController(HashTagFragment.this).navigate(commentsAction);
+ }
+
+ @Override
+ public void onDownloadClick(final FeedModel feedModel, final int childPosition) {
+ final Context context = getContext();
+ if (context == null) return;
+ if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
+ DownloadUtils.showDownloadDialog(context, feedModel, childPosition);
+ return;
}
- finalList.addAll(result);
- postsViewModel.getList().postValue(finalList);
- PostModel model = null;
- if (!result.isEmpty()) {
- model = result.get(result.size() - 1);
+ downloadFeedModel = feedModel;
+ downloadChildPosition = childPosition;
+ requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
+ }
+
+ @Override
+ public void onHashtagClick(final String hashtag) {
+ final NavDirections action = HashTagFragmentDirections.actionGlobalHashTagFragment(hashtag);
+ NavHostFragment.findNavController(HashTagFragment.this).navigate(action);
+ }
+
+ @Override
+ public void onLocationClick(final FeedModel feedModel) {
+ final NavDirections action = HashTagFragmentDirections.actionGlobalLocationFragment(feedModel.getLocationId());
+ NavHostFragment.findNavController(HashTagFragment.this).navigate(action);
+ }
+
+ @Override
+ public void onMentionClick(final String mention) {
+ navigateToProfile(mention.trim());
+ }
+
+ @Override
+ public void onNameClick(final FeedModel feedModel, final View profilePicView) {
+ navigateToProfile("@" + feedModel.getProfileModel().getUsername());
+ }
+
+ @Override
+ public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {
+ navigateToProfile("@" + feedModel.getProfileModel().getUsername());
+ }
+
+ @Override
+ public void onURLClick(final String url) {
+ Utils.openURL(getContext(), url);
+ }
+
+ @Override
+ public void onEmailClick(final String emailId) {
+ Utils.openEmailAddress(getContext(), emailId);
+ }
+
+ private void openPostDialog(final FeedModel feedModel,
+ final View profilePicView,
+ final View mainPostImage,
+ final int position) {
+ final PostViewV2Fragment.Builder builder = PostViewV2Fragment
+ .builder(feedModel);
+ if (position >= 0) {
+ builder.setPosition(position);
+ }
+ final PostViewV2Fragment fragment = builder
+ .setSharedProfilePicElement(profilePicView)
+ .setSharedMainPostElement(mainPostImage)
+ .build();
+ fragment.show(getChildFragmentManager(), "post_view");
+ }
+ };
+ private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
+
+ @Override
+ public void onSelectionStart() {
+ if (!onBackPressedCallback.isEnabled()) {
+ final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
+ onBackPressedCallback.setEnabled(true);
+ onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
+ }
+ if (actionMode == null) {
+ actionMode = fragmentActivity.startActionMode(multiSelectAction);
+ }
+ }
+
+ @Override
+ public void onSelectionChange(final Set selectedFeedModels) {
+ final String title = getString(R.string.number_selected, selectedFeedModels.size());
+ if (actionMode != null) {
+ actionMode.setTitle(title);
+ }
+ HashTagFragment.this.selectedFeedModels = selectedFeedModels;
+ }
+
+ @Override
+ public void onSelectionEnd() {
+ if (onBackPressedCallback.isEnabled()) {
+ onBackPressedCallback.setEnabled(false);
+ onBackPressedCallback.remove();
+ }
+ if (actionMode != null) {
+ actionMode.finish();
+ actionMode = null;
}
- if (model == null) return;
- endCursor = model.getEndCursor();
- hasNextPage = model.hasNextPage();
- model.setPageCursor(false, null);
}
};
@@ -160,6 +253,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
fragmentActivity = (MainActivity) requireActivity();
tagsService = TagsService.getInstance();
storiesService = StoriesService.getInstance();
+ setHasOptionsMenu(true);
}
@Nullable
@@ -184,9 +278,8 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
@Override
public void onRefresh() {
- isPullToRefresh = true;
- endCursor = null;
- fetchHashtagModel();
+ binding.posts.refresh();
+ fetchStories();
}
@Override
@@ -196,10 +289,35 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
}
@Override
- public void onDestroy() {
- super.onDestroy();
- if (postsViewModel != null) {
- postsViewModel.getList().postValue(Collections.emptyList());
+ public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
+ inflater.inflate(R.menu.topic_posts_menu, menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
+ if (item.getItemId() == R.id.layout) {
+ showPostsLayoutPreferences();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
+ final Context context = getContext();
+ if (context == null) return;
+ if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
+ if (downloadFeedModel == null) return;
+ DownloadUtils.showDownloadDialog(context, downloadFeedModel, downloadChildPosition);
+ downloadFeedModel = null;
+ downloadChildPosition = -1;
+ return;
+ }
+ if (requestCode == STORAGE_PERM_REQUEST_CODE_FOR_SELECTION && granted) {
+ DownloadUtils.download(context, ImmutableList.copyOf(selectedFeedModels));
+ binding.posts.endSelection();
}
}
@@ -209,113 +327,42 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
final HashTagFragmentArgs fragmentArgs = HashTagFragmentArgs.fromBundle(getArguments());
hashtag = fragmentArgs.getHashtag();
- setTitle();
- setupPosts();
fetchHashtagModel();
}
- private void setupPosts() {
- postsViewModel = new ViewModelProvider(this).get(PostsViewModel.class);
- final Context context = getContext();
- if (context == null) return;
- final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(context, Utils.convertDpToPx(110));
- binding.mainPosts.setLayoutManager(layoutManager);
- binding.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
- postsAdapter = new PostsAdapter((postModel, position) -> {
- if (postsAdapter.isSelecting()) {
- if (actionMode == null) return;
- final String title = getString(R.string.number_selected, postsAdapter.getSelectedModels().size());
- actionMode.setTitle(title);
- return;
- }
- if (checkAndResetAction()) return;
- final List postModels = postsViewModel.getList().getValue();
- if (postModels == null || postModels.size() == 0) return;
- if (postModels.get(0) == null) return;
- final String postId = postModels.get(0).getPostId();
- final boolean isId = postId != null && isLoggedIn;
- final String[] idsOrShortCodes = new String[postModels.size()];
- for (int i = 0; i < postModels.size(); i++) {
- idsOrShortCodes[i] = isId ? postModels.get(i).getPostId()
- : postModels.get(i).getShortCode();
- }
- final NavDirections action = HashTagFragmentDirections.actionGlobalPostViewFragment(
- position,
- idsOrShortCodes,
- isId);
- NavHostFragment.findNavController(this).navigate(action);
-
- }, (model, position) -> {
- if (!postsAdapter.isSelecting()) {
- checkAndResetAction();
- return true;
- }
- if (onBackPressedCallback.isEnabled()) {
- return true;
- }
- final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
- onBackPressedCallback.setEnabled(true);
- actionMode = fragmentActivity.startActionMode(multiSelectAction);
- final String title = getString(R.string.number_selected, 1);
- actionMode.setTitle(title);
- onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
- return true;
- });
- postsViewModel.getList().observe(fragmentActivity, postsAdapter::submitList);
- binding.mainPosts.setAdapter(postsAdapter);
- final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
- if (!hasNextPage || getContext() == null) return;
- binding.swipeRefreshLayout.setRefreshing(true);
- fetchPosts();
- endCursor = null;
- });
- binding.mainPosts.addOnScrollListener(lazyLoader);
- }
-
private void fetchHashtagModel() {
stopCurrentExecutor();
binding.swipeRefreshLayout.setRefreshing(true);
currentlyExecuting = new HashtagFetcher(hashtag.substring(1), result -> {
- final Context context = getContext();
- if (context == null) return;
hashtagModel = result;
binding.swipeRefreshLayout.setRefreshing(false);
+ final Context context = getContext();
+ if (context == null) return;
if (hashtagModel == null) {
Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_SHORT).show();
return;
}
- fetchPosts();
+ setTitle();
+ setHashtagDetails();
+ setupPosts();
+ fetchStories();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
- private void fetchPosts() {
- stopCurrentExecutor();
+ private void setupPosts() {
+ binding.posts.setViewModelStoreOwner(this)
+ .setLifeCycleOwner(this)
+ .setPostFetchService(new HashtagPostFetchService(hashtagModel))
+ .setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_HASHTAG_POSTS_LAYOUT)))
+ .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
+ .setFeedItemCallback(feedItemCallback)
+ .setSelectionModeCallback(selectionModeCallback)
+ .init();
binding.swipeRefreshLayout.setRefreshing(true);
- if (TextUtils.isEmpty(hashtag)) return;
- currentlyExecuting = new PostsFetcher(hashtag.substring(1), PostItemType.HASHTAG, endCursor, postsFetchListener)
- .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- final Context context = getContext();
- if (context == null) return;
- if (isLoggedIn) {
- storiesService.getUserStory(hashtagModel.getName(),
- null,
- false,
- true,
- false,
- new ServiceCallback>() {
- @Override
- public void onSuccess(final List storyModels) {
- if (storyModels != null && !storyModels.isEmpty()) {
- binding.mainHashtagImage.setStoriesBorder();
- hasStories = true;
- }
- }
+ }
- @Override
- public void onFailure(final Throwable t) {
- Log.e(TAG, "Error", t);
- }
- });
+ private void setHashtagDetails() {
+ if (isLoggedIn) {
binding.btnFollowTag.setVisibility(View.VISIBLE);
binding.btnFollowTag.setText(hashtagModel.getFollowing() ? R.string.unfollow : R.string.follow);
binding.btnFollowTag.setChipIconResource(hashtagModel.getFollowing()
@@ -324,9 +371,35 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
binding.btnFollowTag.setOnClickListener(v -> {
final String cookie = settingsHelper.getString(Constants.COOKIE);
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
- binding.btnFollowTag.setClickable(false);
- if (!hashtagModel.getFollowing()) {
- tagsService.follow(hashtag.substring(1), csrfToken, new ServiceCallback() {
+ if (csrfToken != null) {
+ binding.btnFollowTag.setClickable(false);
+ if (!hashtagModel.getFollowing()) {
+ tagsService.follow(hashtag.substring(1), csrfToken, new ServiceCallback() {
+ @Override
+ public void onSuccess(final Boolean result) {
+ binding.btnFollowTag.setClickable(true);
+ if (!result) {
+ Log.e(TAG, "onSuccess: result is false");
+ return;
+ }
+ onRefresh();
+ }
+
+ @Override
+ public void onFailure(@NonNull final Throwable t) {
+ binding.btnFollowTag.setClickable(true);
+ Log.e(TAG, "onFailure: ", t);
+ final String message = t.getMessage();
+ Snackbar.make(root,
+ message != null ? message
+ : getString(R.string.downloader_unknown_error),
+ BaseTransientBottomBar.LENGTH_LONG)
+ .show();
+ }
+ });
+ return;
+ }
+ tagsService.unfollow(hashtag.substring(1), csrfToken, new ServiceCallback() {
@Override
public void onSuccess(final Boolean result) {
binding.btnFollowTag.setClickable(true);
@@ -349,31 +422,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
.show();
}
});
- return;
}
- tagsService.unfollow(hashtag.substring(1), csrfToken, new ServiceCallback() {
- @Override
- public void onSuccess(final Boolean result) {
- binding.btnFollowTag.setClickable(true);
- if (!result) {
- Log.e(TAG, "onSuccess: result is false");
- return;
- }
- onRefresh();
- }
-
- @Override
- public void onFailure(@NonNull final Throwable t) {
- binding.btnFollowTag.setClickable(true);
- Log.e(TAG, "onFailure: ", t);
- final String message = t.getMessage();
- Snackbar.make(root,
- message != null ? message
- : getString(R.string.downloader_unknown_error),
- BaseTransientBottomBar.LENGTH_LONG)
- .show();
- }
- });
});
} else {
binding.btnFollowTag.setVisibility(View.GONE);
@@ -420,16 +469,43 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
binding.mainTagPostCount.setText(span);
binding.mainTagPostCount.setVisibility(View.VISIBLE);
binding.mainHashtagImage.setOnClickListener(v -> {
- if (hasStories) {
- // show stories
- final NavDirections action = HashTagFragmentDirections
- .actionHashtagFragmentToStoryViewerFragment(-1, null, true, false, hashtagModel.getName(), hashtagModel.getName());
- NavHostFragment.findNavController(this).navigate(action);
- return;
- }
+ if (!hasStories) return;
+ // show stories
+ final NavDirections action = HashTagFragmentDirections
+ .actionHashtagFragmentToStoryViewerFragment(-1, null, true, false, hashtagModel.getName(), hashtagModel.getName());
+ NavHostFragment.findNavController(this).navigate(action);
});
}
+ private void fetchStories() {
+ if (!isLoggedIn) return;
+ storiesFetching = true;
+ storiesService.getUserStory(
+ hashtagModel.getName(),
+ null,
+ false,
+ true,
+ false,
+ new ServiceCallback>() {
+ @Override
+ public void onSuccess(final List storyModels) {
+ if (storyModels != null && !storyModels.isEmpty()) {
+ binding.mainHashtagImage.setStoriesBorder();
+ hasStories = true;
+ } else {
+ hasStories = false;
+ }
+ storiesFetching = false;
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "Error", t);
+ storiesFetching = false;
+ }
+ });
+ }
+
public void stopCurrentExecutor() {
if (currentlyExecuting != null) {
try {
@@ -445,24 +521,28 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
private void setTitle() {
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar != null) {
- Log.d(TAG, "setting title: " + hashtag);
- final Handler handler = new Handler();
- handler.postDelayed(() -> actionBar.setTitle(hashtag), 200);
+ // Log.d(TAG, "setting title: " + hashtag);
+ actionBar.setTitle(hashtag);
+ // final Handler handler = new Handler();
+ // handler.postDelayed(() -> , 1000);
}
}
- private boolean checkAndResetAction() {
- if (!onBackPressedCallback.isEnabled() && actionMode == null) {
- return false;
- }
- if (onBackPressedCallback.isEnabled()) {
- onBackPressedCallback.setEnabled(false);
- onBackPressedCallback.remove();
- }
- if (actionMode != null) {
- actionMode.finish();
- actionMode = null;
- }
- return true;
+ private void updateSwipeRefreshState() {
+ binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching() || storiesFetching);
+ }
+
+ private void navigateToProfile(final String username) {
+ final NavController navController = NavHostFragment.findNavController(this);
+ final Bundle bundle = new Bundle();
+ bundle.putString("username", username);
+ navController.navigate(R.id.action_global_profileFragment, bundle);
+ }
+
+ private void showPostsLayoutPreferences() {
+ final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(
+ Constants.PREF_HASHTAG_POSTS_LAYOUT,
+ preferences -> new Handler().postDelayed(() -> binding.posts.setLayoutPreferences(preferences), 200));
+ fragment.show(getChildFragmentManager(), "posts_layout_preferences");
}
}
diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
index c8837682..2992f9b2 100644
--- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
@@ -2,16 +2,20 @@ package awais.instagrabber.fragments;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.Handler;
import android.text.SpannableStringBuilder;
import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import android.util.Log;
import android.view.ActionMode;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@@ -23,134 +27,221 @@ import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
+import androidx.core.content.PermissionChecker;
import androidx.fragment.app.Fragment;
-import androidx.lifecycle.ViewModelProvider;
+import androidx.navigation.NavController;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.snackbar.BaseTransientBottomBar;
import com.google.android.material.snackbar.Snackbar;
+import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.Set;
import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity;
-import awais.instagrabber.adapters.PostsAdapter;
+import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.asyncs.LocationFetcher;
-import awais.instagrabber.asyncs.PostsFetcher;
+import awais.instagrabber.asyncs.LocationPostFetchService;
import awais.instagrabber.customviews.PrimaryActionModeCallback;
-import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
-import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
-import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.FragmentLocationBinding;
-import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
+import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.LocationModel;
-import awais.instagrabber.models.PostModel;
+import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.StoryModel;
-import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.FavoriteType;
-import awais.instagrabber.models.enums.PostItemType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DataBox;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
-import awais.instagrabber.viewmodels.PostsViewModel;
import awais.instagrabber.webservices.ServiceCallback;
import awais.instagrabber.webservices.StoriesService;
import awaisomereport.LogCollector;
+import static androidx.core.content.PermissionChecker.checkSelfPermission;
+import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "LocationFragment";
+ private static final int STORAGE_PERM_REQUEST_CODE = 8020;
+ private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
private MainActivity fragmentActivity;
private FragmentLocationBinding binding;
private NestedCoordinatorLayout root;
- private boolean shouldRefresh = true, hasStories = false;
+ private boolean shouldRefresh = true;
+ private boolean hasStories = false;
private String locationId;
private LocationModel locationModel;
- private PostsViewModel postsViewModel;
- private PostsAdapter postsAdapter;
private ActionMode actionMode;
private StoriesService storiesService;
- private boolean hasNextPage;
- private String endCursor;
private AsyncTask, ?, ?> currentlyExecuting;
private boolean isLoggedIn;
- private boolean isPullToRefresh;
+ private boolean storiesFetching;
+ private Set selectedFeedModels;
+ private FeedModel downloadFeedModel;
+ private int downloadChildPosition = -1;
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
@Override
public void handleOnBackPressed() {
- if (postsAdapter == null) {
- setEnabled(false);
- remove();
- return;
- }
- postsAdapter.clearSelection();
- setEnabled(false);
- remove();
+ binding.posts.endSelection();
}
};
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
R.menu.multi_select_download_menu, new PrimaryActionModeCallback.CallbacksHelper() {
@Override
public void onDestroy(final ActionMode mode) {
- onBackPressedCallback.handleOnBackPressed();
+ binding.posts.endSelection();
}
@Override
public boolean onActionItemClicked(final ActionMode mode,
final MenuItem item) {
if (item.getItemId() == R.id.action_download) {
- if (postsAdapter == null || locationId == null) {
- return false;
- }
+ if (LocationFragment.this.selectedFeedModels == null) return false;
final Context context = getContext();
if (context == null) return false;
- DownloadUtils.batchDownload(context,
- locationId,
- DownloadMethod.DOWNLOAD_MAIN,
- postsAdapter.getSelectedModels());
- checkAndResetAction();
- return true;
+ if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
+ DownloadUtils.download(context, ImmutableList.copyOf(LocationFragment.this.selectedFeedModels));
+ binding.posts.endSelection();
+ return true;
+ }
+ requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE_FOR_SELECTION);
}
return false;
}
});
- private final FetchListener> postsFetchListener = new FetchListener>() {
+ private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
@Override
- public void onResult(final List result) {
- binding.swipeRefreshLayout.setRefreshing(false);
- if (result == null) return;
- binding.mainPosts.post(() -> binding.mainPosts.setVisibility(View.VISIBLE));
- final List postModels = postsViewModel.getList().getValue();
- List finalList = postModels == null || postModels.isEmpty() ? new ArrayList<>()
- : new ArrayList<>(postModels);
- if (isPullToRefresh) {
- finalList = result;
- isPullToRefresh = false;
- } else {
- finalList.addAll(result);
+ public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
+ openPostDialog(feedModel, profilePicView, mainPostImage, -1);
+ }
+
+ @Override
+ public void onSliderClick(final FeedModel feedModel, final int position) {
+ openPostDialog(feedModel, null, null, position);
+ }
+
+ @Override
+ public void onCommentsClick(final FeedModel feedModel) {
+ final NavDirections commentsAction = LocationFragmentDirections.actionGlobalCommentsViewerFragment(
+ feedModel.getShortCode(),
+ feedModel.getPostId(),
+ feedModel.getProfileModel().getId()
+ );
+ NavHostFragment.findNavController(LocationFragment.this).navigate(commentsAction);
+ }
+
+ @Override
+ public void onDownloadClick(final FeedModel feedModel, final int childPosition) {
+ final Context context = getContext();
+ if (context == null) return;
+ if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
+ DownloadUtils.showDownloadDialog(context, feedModel, childPosition);
+ return;
}
- postsViewModel.getList().postValue(finalList);
- PostModel model = null;
- if (!result.isEmpty()) {
- model = result.get(result.size() - 1);
+ downloadFeedModel = feedModel;
+ downloadChildPosition = childPosition;
+ requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
+ }
+
+ @Override
+ public void onHashtagClick(final String hashtag) {
+ final NavDirections action = LocationFragmentDirections.actionGlobalHashTagFragment(hashtag);
+ NavHostFragment.findNavController(LocationFragment.this).navigate(action);
+ }
+
+ @Override
+ public void onLocationClick(final FeedModel feedModel) {
+ final NavDirections action = LocationFragmentDirections.actionGlobalLocationFragment(feedModel.getLocationId());
+ NavHostFragment.findNavController(LocationFragment.this).navigate(action);
+ }
+
+ @Override
+ public void onMentionClick(final String mention) {
+ navigateToProfile(mention.trim());
+ }
+
+ @Override
+ public void onNameClick(final FeedModel feedModel, final View profilePicView) {
+ navigateToProfile("@" + feedModel.getProfileModel().getUsername());
+ }
+
+ @Override
+ public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {
+ navigateToProfile("@" + feedModel.getProfileModel().getUsername());
+ }
+
+ @Override
+ public void onURLClick(final String url) {
+ Utils.openURL(getContext(), url);
+ }
+
+ @Override
+ public void onEmailClick(final String emailId) {
+ Utils.openEmailAddress(getContext(), emailId);
+ }
+
+ private void openPostDialog(final FeedModel feedModel,
+ final View profilePicView,
+ final View mainPostImage,
+ final int position) {
+ final PostViewV2Fragment.Builder builder = PostViewV2Fragment
+ .builder(feedModel);
+ if (position >= 0) {
+ builder.setPosition(position);
+ }
+ final PostViewV2Fragment fragment = builder
+ .setSharedProfilePicElement(profilePicView)
+ .setSharedMainPostElement(mainPostImage)
+ .build();
+ fragment.show(getChildFragmentManager(), "post_view");
+ }
+ };
+ private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
+
+ @Override
+ public void onSelectionStart() {
+ if (!onBackPressedCallback.isEnabled()) {
+ final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
+ onBackPressedCallback.setEnabled(true);
+ onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
+ }
+ if (actionMode == null) {
+ actionMode = fragmentActivity.startActionMode(multiSelectAction);
+ }
+ }
+
+ @Override
+ public void onSelectionChange(final Set selectedFeedModels) {
+ final String title = getString(R.string.number_selected, selectedFeedModels.size());
+ if (actionMode != null) {
+ actionMode.setTitle(title);
+ }
+ LocationFragment.this.selectedFeedModels = selectedFeedModels;
+ }
+
+ @Override
+ public void onSelectionEnd() {
+ if (onBackPressedCallback.isEnabled()) {
+ onBackPressedCallback.setEnabled(false);
+ onBackPressedCallback.remove();
+ }
+ if (actionMode != null) {
+ actionMode.finish();
+ actionMode = null;
}
- if (model == null) return;
- endCursor = model.getEndCursor();
- hasNextPage = model.hasNextPage();
- model.setPageCursor(false, null);
}
};
@@ -159,6 +250,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
super.onCreate(savedInstanceState);
fragmentActivity = (MainActivity) requireActivity();
storiesService = StoriesService.getInstance();
+ setHasOptionsMenu(true);
}
@Nullable
@@ -185,9 +277,8 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
@Override
public void onRefresh() {
- isPullToRefresh = true;
- endCursor = null;
- fetchLocationModel();
+ binding.posts.refresh();
+ fetchStories();
}
@Override
@@ -197,10 +288,35 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
}
@Override
- public void onDestroy() {
- super.onDestroy();
- if (postsViewModel != null) {
- postsViewModel.getList().postValue(Collections.emptyList());
+ public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
+ inflater.inflate(R.menu.topic_posts_menu, menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
+ if (item.getItemId() == R.id.layout) {
+ showPostsLayoutPreferences();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
+ final Context context = getContext();
+ if (context == null) return;
+ if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
+ if (downloadFeedModel == null) return;
+ DownloadUtils.showDownloadDialog(context, downloadFeedModel, downloadChildPosition);
+ downloadFeedModel = null;
+ downloadChildPosition = -1;
+ return;
+ }
+ if (requestCode == STORAGE_PERM_REQUEST_CODE_FOR_SELECTION && granted) {
+ DownloadUtils.download(context, ImmutableList.copyOf(selectedFeedModels));
+ binding.posts.endSelection();
}
}
@@ -213,66 +329,19 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
binding.favChip.setVisibility(View.GONE);
binding.btnMap.setVisibility(View.GONE);
setTitle();
- setupPosts();
fetchLocationModel();
}
private void setupPosts() {
- postsViewModel = new ViewModelProvider(this).get(PostsViewModel.class);
- final Context context = getContext();
- if (context == null) return;
- final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(context, Utils.convertDpToPx(110));
- binding.mainPosts.setLayoutManager(layoutManager);
- binding.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
- postsAdapter = new PostsAdapter((postModel, position) -> {
- if (postsAdapter.isSelecting()) {
- if (actionMode == null) return;
- final String title = getString(R.string.number_selected, postsAdapter.getSelectedModels().size());
- actionMode.setTitle(title);
- return;
- }
- if (checkAndResetAction()) return;
- final List postModels = postsViewModel.getList().getValue();
- if (postModels == null || postModels.size() == 0) return;
- if (postModels.get(0) == null) return;
- final String postId = postModels.get(0).getPostId();
- final boolean isId = postId != null && isLoggedIn;
- final String[] idsOrShortCodes = new String[postModels.size()];
- for (int i = 0; i < postModels.size(); i++) {
- idsOrShortCodes[i] = isId ? postModels.get(i).getPostId()
- : postModels.get(i).getShortCode();
- }
- final NavDirections action = LocationFragmentDirections.actionGlobalPostViewFragment(
- position,
- idsOrShortCodes,
- isId);
- NavHostFragment.findNavController(this).navigate(action);
- }, (model, position) -> {
- if (!postsAdapter.isSelecting()) {
- checkAndResetAction();
- return true;
- }
- if (onBackPressedCallback.isEnabled()) {
- return true;
- }
- final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity
- .getOnBackPressedDispatcher();
- onBackPressedCallback.setEnabled(true);
- actionMode = fragmentActivity.startActionMode(multiSelectAction);
- final String title = getString(R.string.number_selected, 1);
- actionMode.setTitle(title);
- onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
- return true;
- });
- postsViewModel.getList().observe(fragmentActivity, postsAdapter::submitList);
- binding.mainPosts.setAdapter(postsAdapter);
- final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
- if (!hasNextPage) return;
- binding.swipeRefreshLayout.setRefreshing(true);
- fetchPosts();
- endCursor = null;
- });
- binding.mainPosts.addOnScrollListener(lazyLoader);
+ binding.posts.setViewModelStoreOwner(this)
+ .setLifeCycleOwner(this)
+ .setPostFetchService(new LocationPostFetchService(locationModel))
+ .setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_LOCATION_POSTS_LAYOUT)))
+ .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
+ .setFeedItemCallback(feedItemCallback)
+ .setSelectionModeCallback(selectionModeCallback)
+ .init();
+ binding.swipeRefreshLayout.setRefreshing(true);
}
private void fetchLocationModel() {
@@ -289,34 +358,15 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
}
setTitle();
setupLocationDetails();
- fetchPosts();
+ setupPosts();
+ fetchStories();
+ // fetchPosts();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void setupLocationDetails() {
final String locationId = locationModel.getId();
- binding.swipeRefreshLayout.setRefreshing(true);
- if (isLoggedIn) {
- storiesService.getUserStory(locationId,
- null,
- true,
- false,
- false,
- new ServiceCallback>() {
- @Override
- public void onSuccess(final List storyModels) {
- if (storyModels != null && !storyModels.isEmpty()) {
- binding.mainLocationImage.setStoriesBorder();
- hasStories = true;
- }
- }
-
- @Override
- public void onFailure(final Throwable t) {
- Log.e(TAG, "Error", t);
- }
- });
- }
+ // binding.swipeRefreshLayout.setRefreshing(true);
binding.mainLocationImage.setImageURI(locationModel.getSdProfilePic());
final String postCount = String.valueOf(locationModel.getPostCount());
final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count_inline,
@@ -327,8 +377,8 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
binding.mainLocPostCount.setVisibility(View.VISIBLE);
binding.locationFullName.setText(locationModel.getName());
CharSequence biography = locationModel.getBio();
- binding.locationBiography.setCaptionIsExpandable(true);
- binding.locationBiography.setCaptionIsExpanded(true);
+ // binding.locationBiography.setCaptionIsExpandable(true);
+ // binding.locationBiography.setCaptionIsExpanded(true);
if (TextUtils.isEmpty(biography)) {
binding.locationBiography.setVisibility(View.GONE);
@@ -405,18 +455,38 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
final NavDirections action = LocationFragmentDirections
.actionLocationFragmentToStoryViewerFragment(-1, null, false, true, locationId, locationModel.getName());
NavHostFragment.findNavController(this).navigate(action);
- return;
}
});
}
- private void fetchPosts() {
- stopCurrentExecutor();
- currentlyExecuting = new PostsFetcher(locationModel.getId(), PostItemType.LOCATION, endCursor, postsFetchListener)
- .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ private void fetchStories() {
+ if (isLoggedIn) {
+ storiesFetching = true;
+ storiesService.getUserStory(locationId,
+ null,
+ true,
+ false,
+ false,
+ new ServiceCallback>() {
+ @Override
+ public void onSuccess(final List storyModels) {
+ if (storyModels != null && !storyModels.isEmpty()) {
+ binding.mainLocationImage.setStoriesBorder();
+ hasStories = true;
+ }
+ storiesFetching = false;
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "Error", t);
+ storiesFetching = false;
+ }
+ });
+ }
}
- public void stopCurrentExecutor() {
+ private void stopCurrentExecutor() {
if (currentlyExecuting != null) {
try {
currentlyExecuting.cancel(true);
@@ -435,18 +505,21 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}
- private boolean checkAndResetAction() {
- if (!onBackPressedCallback.isEnabled() && actionMode == null) {
- return false;
- }
- if (onBackPressedCallback.isEnabled()) {
- onBackPressedCallback.setEnabled(false);
- onBackPressedCallback.remove();
- }
- if (actionMode != null) {
- actionMode.finish();
- actionMode = null;
- }
- return true;
+ private void updateSwipeRefreshState() {
+ binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching() || storiesFetching);
+ }
+
+ private void navigateToProfile(final String username) {
+ final NavController navController = NavHostFragment.findNavController(this);
+ final Bundle bundle = new Bundle();
+ bundle.putString("username", username);
+ navController.navigate(R.id.action_global_profileFragment, bundle);
+ }
+
+ private void showPostsLayoutPreferences() {
+ final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(
+ Constants.PREF_LOCATION_POSTS_LAYOUT,
+ preferences -> new Handler().postDelayed(() -> binding.posts.setLayoutPreferences(preferences), 200));
+ fragment.show(getChildFragmentManager(), "posts_layout_preferences");
}
}
diff --git a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java
index e7543b89..651bd8b3 100644
--- a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java
@@ -28,6 +28,7 @@ import awais.instagrabber.R;
import awais.instagrabber.adapters.NotificationsAdapter;
import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener;
import awais.instagrabber.asyncs.NotificationsFetcher;
+import awais.instagrabber.asyncs.PostFetcher;
import awais.instagrabber.databinding.FragmentNotificationsViewerBinding;
import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections;
import awais.instagrabber.interfaces.MentionClickListener;
@@ -75,7 +76,8 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
} else {
commentDialogList = new String[]{getString(R.string.open_profile)};
}
- if (getContext() == null) return;
+ final Context context = getContext();
+ if (context == null) return;
final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
switch (which) {
case 0:
@@ -101,9 +103,18 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
});
return;
}
- final NavDirections action = MorePreferencesFragmentDirections
- .actionGlobalPostViewFragment(0, new String[]{model.getShortCode()}, false);
- NavHostFragment.findNavController(this).navigate(action);
+ final AlertDialog alertDialog = new AlertDialog.Builder(context)
+ .setCancelable(false)
+ .setView(R.layout.dialog_opening_post)
+ .create();
+ alertDialog.show();
+ new PostFetcher(model.getShortCode(), feedModel -> {
+ final PostViewV2Fragment fragment = PostViewV2Fragment
+ .builder(feedModel)
+ .build();
+ fragment.setOnShowListener(dialog1 -> alertDialog.dismiss());
+ fragment.show(getChildFragmentManager(), "post_view");
+ }).execute();
break;
case 2:
friendshipService.ignore(userId, model.getUserId(), csrfToken, new ServiceCallback() {
@@ -125,7 +136,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
break;
}
};
- new AlertDialog.Builder(getContext())
+ new AlertDialog.Builder(context)
.setTitle(title)
.setItems(commentDialogList, profileDialogListener)
.setNegativeButton(R.string.cancel, null)
diff --git a/app/src/main/java/awais/instagrabber/fragments/PostViewFragment.java b/app/src/main/java/awais/instagrabber/fragments/PostViewFragment.java
deleted file mode 100644
index dd0574fc..00000000
--- a/app/src/main/java/awais/instagrabber/fragments/PostViewFragment.java
+++ /dev/null
@@ -1,342 +0,0 @@
-package awais.instagrabber.fragments;
-
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.pm.PackageManager;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.navigation.NavDirections;
-import androidx.navigation.fragment.NavHostFragment;
-import androidx.viewpager2.widget.ViewPager2;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import awais.instagrabber.R;
-import awais.instagrabber.adapters.PostViewAdapter;
-import awais.instagrabber.adapters.PostViewAdapter.OnPostViewChildViewClickListener;
-import awais.instagrabber.asyncs.PostFetcher;
-import awais.instagrabber.asyncs.i.iPostFetcher;
-import awais.instagrabber.databinding.FragmentPostViewBinding;
-import awais.instagrabber.interfaces.FetchListener;
-import awais.instagrabber.interfaces.MentionClickListener;
-import awais.instagrabber.models.ViewerPostModel;
-import awais.instagrabber.models.ViewerPostModelWrapper;
-import awais.instagrabber.models.enums.DownloadMethod;
-import awais.instagrabber.utils.Constants;
-import awais.instagrabber.utils.CookieUtils;
-import awais.instagrabber.utils.DownloadUtils;
-import awais.instagrabber.utils.Utils;
-import awais.instagrabber.viewmodels.ViewerPostViewModel;
-import awais.instagrabber.webservices.MediaService;
-import awais.instagrabber.webservices.ServiceCallback;
-
-import static androidx.core.content.ContextCompat.checkSelfPermission;
-import static awais.instagrabber.utils.Utils.settingsHelper;
-
-public class PostViewFragment extends Fragment {
- private static final String TAG = "PostViewFragment";
- private static final String COOKIE = settingsHelper.getString(Constants.COOKIE);
-
- private FragmentActivity fragmentActivity;
- private FragmentPostViewBinding binding;
- private ViewPager2 root;
- private boolean shouldRefresh = true;
- private ViewerPostViewModel viewerPostViewModel;
- private boolean isId;
- private int currentPostIndex;
- private List idOrCodeList;
- private boolean hasInitialResult = false;
- private PostViewAdapter adapter;
- private boolean session;
- private MediaService mediaService;
-
- private FetchListener pfl = result -> {
- if (result == null) return;
- if (result.length <= 0) return;
- final List viewerPostModels = viewerPostViewModel.getList().getValue();
- final List temp = viewerPostModels == null ? new ArrayList<>(idOrCodeList.size())
- : new ArrayList<>(viewerPostModels);
- final ViewerPostModel firstPost = result[0];
- if (firstPost == null) return;
- String idOrCode = isId ? firstPost.getPostId() : firstPost.getShortCode();
- if (idOrCode == null) return;
- if (isId) {
- // the post id is appended with `_` in the result
- idOrCode = idOrCode.substring(0, idOrCode.indexOf('_'));
- }
- final int index = idOrCodeList.indexOf(idOrCode);
- if (index < 0) return;
- final ViewerPostModelWrapper viewerPostModelWrapper = temp.get(index);
- viewerPostModelWrapper.setViewerPostModels(result);
- temp.set(index, viewerPostModelWrapper);
- viewerPostViewModel.getList().setValue(temp);
- adapter.notifyItemChanged(index);
- if (!hasInitialResult) {
- Log.d(TAG, "setting delayed position to: " + currentPostIndex);
- binding.getRoot()
- .postDelayed(() -> binding.getRoot().setCurrentItem(currentPostIndex), 200);
- }
- hasInitialResult = true;
- };
- private MentionClickListener mentionListener = (view, text, isHashtag, isLocation) -> {
- if (isHashtag) {
- final NavDirections action = PostViewFragmentDirections
- .actionGlobalHashTagFragment(text);
- NavHostFragment.findNavController(this).navigate(action);
- return;
- }
- if (isLocation) {
- final NavDirections action = PostViewFragmentDirections
- .actionGlobalLocationFragment(text);
- NavHostFragment.findNavController(this).navigate(action);
- return;
- }
- final NavDirections action = PostViewFragmentDirections
- .actionGlobalProfileFragment("@" + text);
- NavHostFragment.findNavController(this).navigate(action);
- };
- private OnPostViewChildViewClickListener clickListener = (v, wrapper, postPosition, childPosition) -> {
- final ViewerPostModel postModel = wrapper.getViewerPostModels()[0];
- final String username = postModel.getProfileModel().getUsername();
- final int id = v.getId();
- switch (id) {
- case R.id.viewerCaption:
- break;
- case R.id.btnComments:
- String postId = postModel.getPostId();
- if (postId.contains("_")) postId = postId.substring(0, postId.indexOf("_"));
- final NavDirections commentsAction = PostViewFragmentDirections.actionGlobalCommentsViewerFragment(
- postModel.getShortCode(),
- postId,
- postModel.getProfileModel().getId()
- );
- NavHostFragment.findNavController(this).navigate(commentsAction);
- break;
- case R.id.btnDownload:
- final Context context = getContext();
- if (context == null) return;
- if (checkSelfPermission(context,
- DownloadUtils.PERMS[0]) == PackageManager.PERMISSION_GRANTED) {
- showDownloadDialog(Arrays.asList(wrapper.getViewerPostModels()),
- childPosition,
- username);
- return;
- }
- requestPermissions(DownloadUtils.PERMS, 8020);
- break;
- case R.id.ivProfilePic:
- case R.id.title:
- mentionListener.onClick(null, username, false, false);
- break;
- case R.id.btnLike:
- if (mediaService != null) {
- final String userId = CookieUtils.getUserIdFromCookie(COOKIE);
- final String csrfToken = CookieUtils.getCsrfTokenFromCookie(COOKIE);
- v.setEnabled(false);
- final ServiceCallback likeCallback = new ServiceCallback() {
- @Override
- public void onSuccess(final Boolean result) {
- v.setEnabled(true);
- if (result) {
- postModel.setManualLike(!postModel.getLike());
- adapter.notifyItemChanged(postPosition);
- return;
- }
- Log.e(TAG, "like/unlike unsuccessful!");
- }
-
- @Override
- public void onFailure(final Throwable t) {
- v.setEnabled(true);
- Log.e(TAG, "Error during like/unlike", t);
- }
- };
- if (!postModel.getLike()) {
- mediaService.like(postModel.getPostId(), userId, csrfToken, likeCallback);
- } else {
- mediaService.unlike(postModel.getPostId(), userId, csrfToken, likeCallback);
- }
- }
- break;
- case R.id.btnBookmark:
- if (mediaService != null) {
- final String userId = CookieUtils.getUserIdFromCookie(COOKIE);
- final String csrfToken = CookieUtils.getCsrfTokenFromCookie(COOKIE);
- v.setEnabled(false);
- final ServiceCallback saveCallback = new ServiceCallback() {
- @Override
- public void onSuccess(final Boolean result) {
- v.setEnabled(true);
- if (result) {
- postModel.setBookmarked(!postModel.getBookmark());
- adapter.notifyItemChanged(postPosition);
- return;
- }
- Log.e(TAG, "save/unsave unsuccessful!");
- }
-
- @Override
- public void onFailure(final Throwable t) {
- v.setEnabled(true);
- Log.e(TAG, "Error during save/unsave", t);
- }
- };
- if (!postModel.getBookmark()) {
- mediaService.save(postModel.getPostId(), userId, csrfToken, saveCallback);
- } else {
- mediaService.unsave(postModel.getPostId(), userId, csrfToken, saveCallback);
- }
- }
- break;
- }
- };
- private PostViewAdapter.OnPostCaptionLongClickListener captionLongClickListener = text -> {
- final Context context = getContext();
- if (context == null) return;
- Utils.copyText(context, text);
- };
-
- @Override
- public void onCreate(@Nullable final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- fragmentActivity = getActivity();
- mediaService = MediaService.getInstance();
- }
-
- @Nullable
- @Override
- public View onCreateView(@NonNull final LayoutInflater inflater,
- @Nullable final ViewGroup container,
- @Nullable final Bundle savedInstanceState) {
- if (root != null) {
- shouldRefresh = false;
- return root;
- }
- binding = FragmentPostViewBinding.inflate(inflater, container, false);
- root = binding.getRoot();
- setupViewPager();
- return root;
- }
-
- @Override
- public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
- if (!shouldRefresh) return;
- init();
- shouldRefresh = false;
- }
-
- private void setupViewPager() {
- viewerPostViewModel = new ViewModelProvider(fragmentActivity)
- .get(ViewerPostViewModel.class);
- adapter = new PostViewAdapter(clickListener, captionLongClickListener, mentionListener);
- root.setAdapter(adapter);
- root.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
-
- @Override
- public void onPageSelected(final int position) {
- // Log.d(TAG, "onPageSelected: " + position + ", hasInitialResult: " + hasInitialResult);
- if (!hasInitialResult) {
- return;
- }
- currentPostIndex = position;
- fetchPost();
- }
- });
- viewerPostViewModel.getList().observe(fragmentActivity, list -> adapter.submitList(list));
- }
-
- private void init() {
- if (getArguments() == null) return;
- final PostViewFragmentArgs fragmentArgs = PostViewFragmentArgs.fromBundle(getArguments());
- final String[] idOrCodeArray = fragmentArgs.getIdOrCodeArray();
- if (idOrCodeArray.length == 0) return;
- currentPostIndex = fragmentArgs.getIndex();
- if (currentPostIndex < 0) return;
- if (currentPostIndex >= idOrCodeArray.length) return;
- idOrCodeList = Arrays.asList(idOrCodeArray);
- viewerPostViewModel.getList().setValue(createPlaceholderModels(idOrCodeArray.length));
- isId = fragmentArgs.getIsId();
- fetchPost();
- }
-
- private List createPlaceholderModels(final int size) {
- final List viewerPostModels = new ArrayList<>(size);
- for (int i = 0; i < size; i++) {
- // viewerPostModels.add(new ViewerPostModel[]{ViewerPostModel.getDefaultModel(-i, "")});
- viewerPostModels.add(new ViewerPostModelWrapper(i, null));
- }
- return viewerPostModels;
- }
-
- private void fetchPost() {
- // Log.d(TAG, "fetchPost, currentPostIndex: " + currentPostIndex);
- final List list = viewerPostViewModel.getList().getValue();
- if (list != null) {
- final ViewerPostModelWrapper viewerPostModels = list.get(currentPostIndex);
- if (viewerPostModels != null && viewerPostModels
- .getViewerPostModels() != null && viewerPostModels
- .getViewerPostModels().length > 0) {
- Log.d(TAG, "returning without fetching");
- return;
- }
- }
- if (currentPostIndex >= idOrCodeList.size() || currentPostIndex < 0) return;
- final String idOrShortCode = idOrCodeList.get(currentPostIndex);
- if (isId) {
- new iPostFetcher(idOrShortCode, pfl).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- return;
- }
- new PostFetcher(idOrShortCode, pfl).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
-
- private void showDownloadDialog(final List postModels,
- final int childPosition,
- final String username) {
- final List postModelsToDownload = new ArrayList<>();
- final Context context = getContext();
- if (context == null) return;
- if (!session && postModels.size() > 1) {
- final DialogInterface.OnClickListener clickListener = (dialog, which) -> {
- if (which == DialogInterface.BUTTON_NEGATIVE) {
- postModelsToDownload.addAll(postModels);
- } else if (which == DialogInterface.BUTTON_POSITIVE) {
- postModelsToDownload.add(postModels.get(childPosition));
- } else {
- session = true;
- postModelsToDownload.add(postModels.get(childPosition));
- }
- if (postModelsToDownload.size() > 0) {
- DownloadUtils.batchDownload(context,
- username,
- DownloadMethod.DOWNLOAD_POST_VIEWER,
- postModelsToDownload);
- }
- };
- new AlertDialog.Builder(context)
- .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 {
- DownloadUtils.batchDownload(context,
- username,
- DownloadMethod.DOWNLOAD_POST_VIEWER,
- Collections.singletonList(postModels.get(childPosition)));
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java
new file mode 100644
index 00000000..1cc4c2a5
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java
@@ -0,0 +1,1144 @@
+package awais.instagrabber.fragments;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.ScrollView;
+import android.widget.Toast;
+import android.widget.ViewSwitcher;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.core.content.PermissionChecker;
+import androidx.core.widget.NestedScrollView;
+import androidx.fragment.app.DialogFragment;
+import androidx.navigation.NavController;
+import androidx.navigation.fragment.NavHostFragment;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.transition.TransitionManager;
+import androidx.viewpager2.widget.ViewPager2;
+
+import com.facebook.drawee.backends.pipeline.Fresco;
+import com.facebook.drawee.controller.BaseControllerListener;
+import com.facebook.drawee.drawable.ScalingUtils;
+import com.facebook.drawee.generic.GenericDraweeHierarchy;
+import com.facebook.drawee.interfaces.DraweeController;
+import com.facebook.imagepipeline.image.ImageInfo;
+import com.facebook.imagepipeline.request.ImageRequest;
+import com.facebook.imagepipeline.request.ImageRequestBuilder;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+import com.google.android.material.snackbar.BaseTransientBottomBar;
+import com.google.android.material.snackbar.Snackbar;
+
+import java.io.Serializable;
+
+import awais.instagrabber.R;
+import awais.instagrabber.activities.MainActivity;
+import awais.instagrabber.adapters.SliderCallbackAdapter;
+import awais.instagrabber.adapters.SliderItemsAdapter;
+import awais.instagrabber.adapters.viewholder.SliderVideoViewHolder;
+import awais.instagrabber.customviews.SharedElementTransitionDialogFragment;
+import awais.instagrabber.customviews.VerticalDragHelper;
+import awais.instagrabber.customviews.VideoPlayerCallbackAdapter;
+import awais.instagrabber.customviews.VideoPlayerViewHelper;
+import awais.instagrabber.customviews.drawee.AnimatedZoomableController;
+import awais.instagrabber.databinding.DialogPostViewBinding;
+import awais.instagrabber.models.FeedModel;
+import awais.instagrabber.models.PostChild;
+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.NumberUtils;
+import awais.instagrabber.utils.TextUtils;
+import awais.instagrabber.utils.Utils;
+import awais.instagrabber.webservices.MediaService;
+import awais.instagrabber.webservices.ServiceCallback;
+
+import static androidx.core.content.PermissionChecker.checkSelfPermission;
+import static awais.instagrabber.fragments.HashTagFragment.ARG_HASHTAG;
+import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
+import static awais.instagrabber.utils.Utils.settingsHelper;
+
+public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
+ private static final String TAG = "PostViewV2Fragment";
+ private static final String COOKIE = settingsHelper.getString(Constants.COOKIE);
+ private static final int DETAILS_HIDE_DELAY_MILLIS = 2000;
+ private static final String ARG_FEED_MODEL = "feedModel";
+ private static final String ARG_SLIDER_POSITION = "position";
+ private static final int STORAGE_PERM_REQUEST_CODE = 8020;
+
+ private FeedModel feedModel;
+ private View sharedProfilePicElement;
+ private View sharedMainPostElement;
+ private MainActivity fragmentActivity;
+ private DialogPostViewBinding binding;
+ private MediaService mediaService;
+ private Context context;
+ private BottomSheetBehavior bottomSheetBehavior;
+ private boolean detailsVisible = true, video;
+ private VideoPlayerViewHelper videoPlayerViewHelper;
+ private SliderItemsAdapter sliderItemsAdapter;
+ private boolean wasControlsVisible;
+ private boolean wasPaused;
+ private int captionState = BottomSheetBehavior.STATE_HIDDEN;
+ private int sliderPosition = -1;
+ private DialogInterface.OnShowListener onShowListener;
+
+ private final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener = new VerticalDragHelper.OnVerticalDragListener() {
+
+ @Override
+ public void onDrag(final float dY) {
+ // allow the view to be draggable
+ final ConstraintLayout v = binding.getRoot();
+ final float finalY = v.getY() + dY;
+ animateY(v, finalY, 0, null);
+ }
+
+ @Override
+ public void onDragEnd() {
+ // animate and dismiss if user drags the view more that 30% of the view
+ if (Math.abs(binding.getRoot().getY()) > Utils.displayMetrics.heightPixels * 0.35) {
+ animateAndDismiss(binding.getRoot().getY() < 0 ? -1 : 1);
+ return;
+ }
+ // animate back the view to proper position
+ animateY(binding.getRoot(), 0, 200, null);
+ }
+
+ @Override
+ public void onFling(final float flingVelocity) {
+ // animate and dismiss if user flings up/down
+ animateAndDismiss(flingVelocity < 0 ? -1 : 1);
+ }
+
+ private void animateAndDismiss(final int direction) {
+ final int height = binding.getRoot().getHeight();
+ final int finalYDist = height + Utils.getStatusBarHeight(context);
+ // less than 0 means up direction, else down
+ final int finalY = direction < 0 ? -finalYDist : finalYDist;
+ animateY(binding.getRoot(), finalY, 200, new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(final Animator animation) {
+ dismiss();
+ }
+ });
+ }
+ };
+
+ public void setOnShowListener(final DialogInterface.OnShowListener onShowListener) {
+ this.onShowListener = onShowListener;
+ }
+
+ public static class Builder {
+ private final FeedModel feedModel;
+ private View profilePicElement;
+ private View mainPostElement;
+ private int position;
+
+ public Builder setSharedProfilePicElement(final View profilePicElement) {
+ this.profilePicElement = profilePicElement;
+ return this;
+ }
+
+ public Builder setSharedMainPostElement(final View mainPostElement) {
+ this.mainPostElement = mainPostElement;
+ return this;
+ }
+
+ public Builder setPosition(final int position) {
+ this.position = position;
+ return this;
+ }
+
+ public PostViewV2Fragment build() {
+ return PostViewV2Fragment.newInstance(feedModel, profilePicElement, mainPostElement, position);
+ }
+
+ public Builder(final FeedModel feedModel) {
+ this.feedModel = feedModel;
+ }
+ }
+
+ private static PostViewV2Fragment newInstance(final FeedModel feedModel,
+ final View profilePicElement,
+ final View mainPostElement,
+ final int position) {
+ final PostViewV2Fragment f = new PostViewV2Fragment(profilePicElement, mainPostElement);
+ final Bundle args = new Bundle();
+ args.putSerializable(ARG_FEED_MODEL, feedModel);
+ if (position >= 0) {
+ args.putInt(ARG_SLIDER_POSITION, position);
+ }
+ f.setArguments(args);
+ return f;
+ }
+
+ public static Builder builder(final FeedModel feedModel) {
+ return new Builder(feedModel);
+ }
+
+ // default constructor for fragment manager
+ public PostViewV2Fragment() {}
+
+ private PostViewV2Fragment(final View sharedProfilePicElement,
+ final View sharedMainPostElement) {
+ this.sharedProfilePicElement = sharedProfilePicElement;
+ this.sharedMainPostElement = sharedMainPostElement;
+ }
+
+ @Override
+ public void onCreate(@Nullable final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setStyle(DialogFragment.STYLE_NO_FRAME, android.R.style.Theme_Translucent);
+ fragmentActivity = (MainActivity) getActivity();
+ mediaService = MediaService.getInstance();
+ final Bundle arguments = getArguments();
+ if (arguments == null) return;
+ final Serializable feedModelSerializable = arguments.getSerializable(ARG_FEED_MODEL);
+ if (feedModelSerializable == null) {
+ Log.e(TAG, "onCreate: feedModelSerializable is null");
+ return;
+ }
+ if (!(feedModelSerializable instanceof FeedModel)) {
+ return;
+ }
+ feedModel = (FeedModel) feedModelSerializable;
+ if (feedModel.getItemType() == MediaItemType.MEDIA_TYPE_SLIDER) {
+ sliderPosition = arguments.getInt(ARG_SLIDER_POSITION, 0);
+ }
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull final LayoutInflater inflater,
+ @Nullable final ViewGroup container,
+ @Nullable final Bundle savedInstanceState) {
+ binding = DialogPostViewBinding.inflate(inflater, container, false);
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
+ setupToolbar();
+ init();
+ }
+
+ @Override
+ public void onAttach(@NonNull final Context context) {
+ super.onAttach(context);
+ this.context = context;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ final Dialog dialog = getDialog();
+ if (dialog == null) return;
+ final Window window = dialog.getWindow();
+ if (window == null) return;
+ window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ window.setDimAmount(0);
+ int width = ViewGroup.LayoutParams.MATCH_PARENT;
+ int height = ViewGroup.LayoutParams.MATCH_PARENT;
+ window.setLayout(width, height);
+ if (!wasPaused && (sharedProfilePicElement != null || sharedMainPostElement != null)) {
+ final ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(
+ binding.getRoot().getBackground().mutate(),
+ PropertyValuesHolder.ofInt("alpha", 0, 255)
+ );
+ addAnimator(animator);
+ }
+ if (onShowListener != null) {
+ onShowListener.onShow(dialog);
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ wasPaused = true;
+ if (bottomSheetBehavior != null) {
+ captionState = bottomSheetBehavior.getState();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ switch (feedModel.getItemType()) {
+ case MEDIA_TYPE_VIDEO:
+ if (videoPlayerViewHelper != null) {
+ videoPlayerViewHelper.releasePlayer();
+ }
+ return;
+ case MEDIA_TYPE_SLIDER:
+ if (sliderItemsAdapter != null) {
+ releaseAllSliderPlayers();
+ }
+ default:
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull final Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (feedModel.getItemType() == MediaItemType.MEDIA_TYPE_SLIDER) {
+ outState.putInt(ARG_SLIDER_POSITION, sliderPosition);
+ }
+ }
+
+ @Override
+ protected void onBeforeSharedElementAnimation(@NonNull final View startView,
+ @NonNull final View destView,
+ @NonNull final SharedElementTransitionDialogFragment.ViewBounds viewBounds) {
+ GenericDraweeHierarchy hierarchy = null;
+ if (destView == binding.postImage) {
+ hierarchy = binding.postImage.getHierarchy();
+ } else if (destView == binding.videoPost.thumbnailParent) {
+ hierarchy = binding.videoPost.thumbnail.getHierarchy();
+ }
+ if (hierarchy != null) {
+ final ScalingUtils.ScaleType scaleTypeTo = ScalingUtils.ScaleType.FIT_CENTER;
+ final ScalingUtils.InterpolatingScaleType scaleType = new ScalingUtils.InterpolatingScaleType(
+ ScalingUtils.ScaleType.CENTER_CROP,
+ scaleTypeTo,
+ viewBounds.getStartBounds(),
+ viewBounds.getDestBounds()
+ );
+ hierarchy.setActualImageScaleType(scaleType);
+ final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+ animator.setDuration(getAnimationDuration());
+ animator.addUpdateListener(animation -> {
+ float fraction = (float) animation.getAnimatedValue();
+ scaleType.setValue(fraction);
+ });
+ final GenericDraweeHierarchy finalHierarchy = hierarchy;
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finalHierarchy.setActualImageScaleType(scaleTypeTo);
+ destView.requestLayout();
+ }
+ });
+ addAnimator(animator);
+ }
+ }
+
+ @Override
+ protected void onEndSharedElementAnimation(@NonNull final View startView,
+ @NonNull final View destView,
+ @NonNull final ViewBounds viewBounds) {
+ if (destView == binding.postImage) {
+ binding.postImage.setTranslationX(0);
+ binding.postImage.setTranslationY(0);
+ binding.postImage.setX(0);
+ binding.postImage.setY(0);
+ binding.postImage.setLayoutParams(new ConstraintLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ binding.postImage.requestLayout();
+ if (bottomSheetBehavior != null) {
+ bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
+ }
+ return;
+ }
+ if (destView == binding.sliderParent) {
+ binding.sliderParent.setLayoutParams(new ConstraintLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ binding.sliderParent.requestLayout();
+ bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
+ return;
+ }
+ if (destView == binding.videoPost.thumbnailParent) {
+ final FrameLayout.LayoutParams params = new ViewSwitcher.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ params.gravity = Gravity.CENTER;
+ binding.videoPost.thumbnailParent.setLayoutParams(params);
+ binding.videoPost.thumbnailParent.requestLayout();
+ bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == STORAGE_PERM_REQUEST_CODE && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ DownloadUtils.showDownloadDialog(context, feedModel, sliderPosition);
+ }
+ }
+
+ private void init() {
+ if (!wasPaused && (sharedProfilePicElement != null || sharedMainPostElement != null)) {
+ binding.getRoot().getBackground().mutate().setAlpha(0);
+ }
+ setupProfilePic();
+ setupTitles();
+ setupCaption();
+ setupCounts();
+ setupPostTypeLayout();
+ setupCommonActions();
+ }
+
+ private void setupCommonActions() {
+ setupLike();
+ setupSave();
+ setupDownload();
+ setupComment();
+ }
+
+ private void setupComment() {
+ binding.comment.setOnClickListener(v -> {
+ final NavController navController = getNavController();
+ if (navController == null) return;
+ final Bundle bundle = new Bundle();
+ bundle.putString("shortCode", feedModel.getShortCode());
+ bundle.putString("postId", feedModel.getPostId());
+ bundle.putString("postUserId", feedModel.getProfileModel().getId());
+ navController.navigate(R.id.action_global_commentsViewerFragment, bundle);
+ });
+ binding.comment.setOnLongClickListener(v -> {
+ Utils.displayToastAboveView(context, v, getString(R.string.comment));
+ return true;
+ });
+ }
+
+ private void setupDownload() {
+ binding.download.setOnClickListener(v -> {
+ if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
+ DownloadUtils.showDownloadDialog(context, feedModel, sliderPosition);
+ return;
+ }
+ requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
+ });
+ binding.download.setOnLongClickListener(v -> {
+ Utils.displayToastAboveView(context, v, getString(R.string.action_download));
+ return true;
+ });
+ }
+
+ private void setupLike() {
+ if (mediaService == null) return;
+ setLikedResources(feedModel.getLike());
+ final ServiceCallback likeCallback = new ServiceCallback