1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-22 14:47:29 +00:00

Merge pull request #503 from ammargitham/dm-notifications-enhancements

DM update
This commit is contained in:
Austin Huang 2021-02-13 13:32:07 -05:00 committed by GitHub
commit 76213a9966
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
546 changed files with 40595 additions and 10253 deletions

View File

@ -36,5 +36,10 @@
<option name="name" value="maven" />
<option name="url" value="http://maven.geotoolkit.org/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://www.jitpack.io" />
</remote-repository>
</component>
</project>

View File

@ -10,8 +10,8 @@ android {
minSdkVersion 21
targetSdkVersion 29
versionCode 57
versionName '19.0.5'
versionCode 58
versionName '19.1.0-a1'
multiDexEnabled true
@ -62,7 +62,7 @@ dependencies {
def nav_version = '2.3.2'
def exoplayer_version = '2.12.0'
implementation 'com.google.android.material:material:1.3.0-alpha04'
implementation 'com.google.android.material:material:1.3.0'
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-dash:$exoplayer_version"
@ -83,11 +83,22 @@ dependencies {
implementation 'com.google.guava:guava:27.0.1-android'
// Room
def room_version = "2.2.5"
def room_version = "2.2.6"
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-guava:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
// CameraX
def camerax_version = "1.0.0-rc01"
implementation "androidx.camera:camera-camera2:$camerax_version"
implementation "androidx.camera:camera-lifecycle:$camerax_version"
implementation "androidx.camera:camera-view:1.0.0-alpha20"
// EmojiCompat
def emoji_compat_version = "1.1.0"
implementation "androidx.emoji:emoji:$emoji_compat_version"
implementation "androidx.emoji:emoji-appcompat:$emoji_compat_version"
implementation 'me.austinhuang:AutoLinkTextViewV2:-SNAPSHOT'
implementation 'com.facebook.fresco:fresco:2.3.0'
@ -99,8 +110,11 @@ dependencies {
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'org.apache.commons:commons-imaging:1.0-alpha2'
implementation 'com.ibm.icu:icu4j:68.1'
implementation 'com.github.ammargitham:uCrop:2.3-native-beta-2'
implementation 'com.github.ammargitham:android-gpuimage:2.1.1-beta4'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6'
testImplementation 'org.junit.jupiter:junit-jupiter:5.7.0'
}

View File

@ -23,6 +23,7 @@
#noinspection ShrinkerUnresolvedReference
#-keep class !com.google.android.exoplayer2.**, ** { *; }
#-keep class !awais.instagrabber.** { *; }
-dontobfuscate
# prevent shrinking retrofit response entities
-keep class awais.instagrabber.repositories.responses.** { *; }

View File

@ -6,6 +6,13 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature
android:name="android.hardware.camera.any"
android:required="false" />
<application
android:name=".InstaGrabberApplication"
@ -21,7 +28,7 @@
android:name=".activities.MainActivity"
android:launchMode="singleTop"
android:taskAffinity=".Main"
android:windowSoftInputMode="adjustPan">
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
@ -114,6 +121,15 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity" />
</activity>
<activity
android:name=".activities.CameraActivity"
android:label="Camera"
android:parentActivityName=".activities.MainActivity"
android:theme="@style/AppTheme.Light.White">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity" />
</activity>
<provider
android:name="androidx.core.content.FileProvider"
@ -131,5 +147,13 @@
<service
android:name=".services.DeleteImageIntentService"
android:exported="false" />
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
<meta-data
android:name="fontProviderRequests"
android:value="Noto Color Emoji Compat" />
</application>
</manifest>

View File

@ -3,6 +3,7 @@ package awais.instagrabber;
import android.app.Application;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import com.facebook.drawee.backends.pipeline.Fresco;
@ -11,14 +12,19 @@ import com.facebook.imagepipeline.core.ImagePipelineConfig;
import java.net.CookieHandler;
import java.text.SimpleDateFormat;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.SettingsHelper;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.UserAgentUtils;
import awaisomereport.CrashReporter;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.CookieUtils.NET_COOKIE_MANAGER;
import static awais.instagrabber.utils.Utils.applicationHandler;
import static awais.instagrabber.utils.Utils.cacheDir;
import static awais.instagrabber.utils.Utils.clipboardManager;
import static awais.instagrabber.utils.Utils.datetimeParser;
import static awais.instagrabber.utils.Utils.logCollector;
@ -59,6 +65,14 @@ public final class InstaGrabberApplication extends Application {
if (settingsHelper == null)
settingsHelper = new SettingsHelper(this);
if (applicationHandler == null) {
applicationHandler = new Handler(getApplicationContext().getMainLooper());
}
if (cacheDir == null) {
cacheDir = getCacheDir().getAbsolutePath();
}
LocaleUtils.setLocale(getBaseContext());
if (clipboardManager == null)
@ -70,6 +84,18 @@ public final class InstaGrabberApplication extends Application {
settingsHelper.getString(Constants.CUSTOM_DATE_TIME_FORMAT) :
settingsHelper.getString(Constants.DATE_TIME_FORMAT), LocaleUtils.getCurrentLocale());
if (TextUtils.isEmpty(settingsHelper.getString(Constants.DEVICE_UUID))) {
settingsHelper.putString(Constants.DEVICE_UUID, UUID.randomUUID().toString());
}
if (settingsHelper.getInteger(Constants.BROWSER_UA_CODE) == -1) {
int randomNum = ThreadLocalRandom.current().nextInt(0, UserAgentUtils.browsers.length);
settingsHelper.putInteger(Constants.BROWSER_UA_CODE, randomNum);
}
if (settingsHelper.getInteger(Constants.APP_UA_CODE) == -1) {
int randomNum = ThreadLocalRandom.current().nextInt(0, UserAgentUtils.devices.length);
settingsHelper.putInteger(Constants.APP_UA_CODE, randomNum);
}
}
}

View File

@ -0,0 +1,276 @@
package awais.instagrabber.activities;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.webkit.MimeTypeMap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.core.CameraInfoUnavailableException;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.content.ContextCompat;
import com.google.common.io.Files;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import awais.instagrabber.databinding.ActivityCameraBinding;
import awais.instagrabber.utils.DirectoryUtils;
import awais.instagrabber.utils.PermissionUtils;
import awais.instagrabber.utils.Utils;
public class CameraActivity extends BaseLanguageActivity {
private static final String TAG = CameraActivity.class.getSimpleName();
private static final int CAMERA_REQUEST_CODE = 100;
private static final String FILE_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS";
private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat(FILE_FORMAT, Locale.US);
private ActivityCameraBinding binding;
private ImageCapture imageCapture;
private File outputDirectory;
private ExecutorService cameraExecutor;
private int displayId = -1;
private final DisplayManager.DisplayListener displayListener = new DisplayManager.DisplayListener() {
@Override
public void onDisplayAdded(final int displayId) {}
@Override
public void onDisplayRemoved(final int displayId) {}
@Override
public void onDisplayChanged(final int displayId) {
if (displayId == CameraActivity.this.displayId) {
imageCapture.setTargetRotation(binding.getRoot().getDisplay().getRotation());
}
}
};
private DisplayManager displayManager;
private ProcessCameraProvider cameraProvider;
private int lensFacing;
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityCameraBinding.inflate(LayoutInflater.from(getBaseContext()));
setContentView(binding.getRoot());
Utils.transparentStatusBar(this, true, false);
displayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
outputDirectory = DirectoryUtils.getOutputMediaDirectory(this, "Camera");
cameraExecutor = Executors.newSingleThreadExecutor();
displayManager.registerDisplayListener(displayListener, null);
binding.viewFinder.post(() -> {
displayId = binding.viewFinder.getDisplay().getDisplayId();
updateUi();
checkPermissionsAndSetupCamera();
});
}
@Override
protected void onResume() {
super.onResume();
// Make sure that all permissions are still present, since the
// user could have removed them while the app was in paused state.
if (!PermissionUtils.hasCameraPerms(this)) {
PermissionUtils.requestCameraPerms(this, CAMERA_REQUEST_CODE);
}
}
@Override
public void onConfigurationChanged(@NonNull final Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Redraw the camera UI controls
updateUi();
// Enable or disable switching between cameras
updateCameraSwitchButton();
}
@Override
protected void onDestroy() {
super.onDestroy();
Utils.transparentStatusBar(this, false, false);
cameraExecutor.shutdown();
displayManager.unregisterDisplayListener(displayListener);
}
private void updateUi() {
binding.cameraCaptureButton.setOnClickListener(v -> takePhoto());
// Disable the button until the camera is set up
binding.switchCamera.setEnabled(false);
// Listener for button used to switch cameras. Only called if the button is enabled
binding.switchCamera.setOnClickListener(v -> {
lensFacing = CameraSelector.LENS_FACING_FRONT == lensFacing ? CameraSelector.LENS_FACING_BACK
: CameraSelector.LENS_FACING_FRONT;
// Re-bind use cases to update selected camera
bindCameraUseCases();
});
binding.close.setOnClickListener(v -> {
setResult(Activity.RESULT_CANCELED);
finish();
});
}
private void checkPermissionsAndSetupCamera() {
if (PermissionUtils.hasCameraPerms(this)) {
setupCamera();
return;
}
PermissionUtils.requestCameraPerms(this, CAMERA_REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
if (requestCode == CAMERA_REQUEST_CODE) {
if (PermissionUtils.hasCameraPerms(this)) {
setupCamera();
}
}
}
private void setupCamera() {
final ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(() -> {
try {
cameraProvider = cameraProviderFuture.get();
// Select lensFacing depending on the available cameras
lensFacing = -1;
if (hasBackCamera()) {
lensFacing = CameraSelector.LENS_FACING_BACK;
} else if (hasFrontCamera()) {
lensFacing = CameraSelector.LENS_FACING_FRONT;
}
if (lensFacing == -1) {
throw new IllegalStateException("Back and front camera are unavailable");
}
// Enable or disable switching between cameras
updateCameraSwitchButton();
// Build and bind the camera use cases
bindCameraUseCases();
} catch (ExecutionException | InterruptedException | CameraInfoUnavailableException e) {
Log.e(TAG, "setupCamera: ", e);
}
}, ContextCompat.getMainExecutor(this));
}
private void bindCameraUseCases() {
final int rotation = binding.viewFinder.getDisplay().getRotation();
// CameraSelector
final CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(lensFacing)
.build();
// Preview
final Preview preview = new Preview.Builder()
// Set initial target rotation
.setTargetRotation(rotation)
.build();
// ImageCapture
imageCapture = new ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
// Set initial target rotation, we will have to call this again if rotation changes
// during the lifecycle of this use case
.setTargetRotation(rotation)
.build();
cameraProvider.unbindAll();
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture);
preview.setSurfaceProvider(binding.viewFinder.getSurfaceProvider());
}
private void takePhoto() {
if (imageCapture == null) return;
final File photoFile = new File(outputDirectory, SIMPLE_DATE_FORMAT.format(System.currentTimeMillis()) + ".jpg");
final ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(photoFile).build();
imageCapture.takePicture(
outputFileOptions,
cameraExecutor,
new ImageCapture.OnImageSavedCallback() {
@Override
public void onImageSaved(@NonNull final ImageCapture.OutputFileResults outputFileResults) {
final Uri uri = Uri.fromFile(photoFile);
//noinspection UnstableApiUsage
final String mimeType = MimeTypeMap.getSingleton()
.getMimeTypeFromExtension(Files.getFileExtension(photoFile.getName()));
MediaScannerConnection.scanFile(
CameraActivity.this,
new String[]{photoFile.getAbsolutePath()},
new String[]{mimeType},
(path, uri1) -> {
Log.d(TAG, "onImageSaved: scan complete");
final Intent intent = new Intent();
intent.setData(uri1);
setResult(Activity.RESULT_OK, intent);
finish();
});
Log.d(TAG, "onImageSaved: " + uri);
}
@Override
public void onError(@NonNull final ImageCaptureException exception) {
Log.e(TAG, "onError: ", exception);
}
}
);
// We can only change the foreground Drawable using API level 23+ API
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// // Display flash animation to indicate that photo was captured
// final ConstraintLayout container = binding.getRoot();
// container.postDelayed(() -> {
// container.setForeground(new ColorDrawable(Color.WHITE));
// container.postDelayed(() -> container.setForeground(null), 50);
// }, 100);
// }
}
/**
* Enabled or disabled a button to switch cameras depending on the available cameras
*/
private void updateCameraSwitchButton() {
try {
binding.switchCamera.setEnabled(hasBackCamera() && hasFrontCamera());
} catch (CameraInfoUnavailableException e) {
binding.switchCamera.setEnabled(false);
}
}
/**
* Returns true if the device has an available back camera. False otherwise
*/
private boolean hasBackCamera() throws CameraInfoUnavailableException {
if (cameraProvider == null) return false;
return cameraProvider.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA);
}
/**
* Returns true if the device has an available front camera. False otherwise
*/
private boolean hasFrontCamera() throws CameraInfoUnavailableException {
if (cameraProvider == null) {
return false;
}
return cameraProvider.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA);
}
}

View File

@ -22,9 +22,9 @@ 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.repositories.responses.Media;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DownloadUtils;
@ -123,7 +123,7 @@ public final class DirectDownload extends AppCompatActivity {
return;
}
final String text = model.getText();
new PostFetcher(text, new FetchListener<FeedModel>() {
new PostFetcher(text, new FetchListener<Media>() {
@Override
public void doBefore() {
if (notificationManager == null) return;
@ -138,7 +138,7 @@ public final class DirectDownload extends AppCompatActivity {
}
@Override
public void onResult(final FeedModel result) {
public void onResult(final Media result) {
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
if (result == null) {
finish();

View File

@ -1,5 +1,6 @@
package awais.instagrabber.activities;
import android.animation.LayoutTransition;
import android.annotation.SuppressLint;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@ -25,11 +26,15 @@ 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.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.provider.FontRequest;
import androidx.emoji.text.EmojiCompat;
import androidx.emoji.text.FontRequestEmojiCompatConfig;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LiveData;
@ -53,6 +58,7 @@ import awais.instagrabber.R;
import awais.instagrabber.adapters.SuggestionsAdapter;
import awais.instagrabber.asyncs.PostFetcher;
import awais.instagrabber.asyncs.SuggestionsFetcher;
import awais.instagrabber.customviews.emoji.EmojiVariantManager;
import awais.instagrabber.databinding.ActivityMainBinding;
import awais.instagrabber.fragments.PostViewV2Fragment;
import awais.instagrabber.fragments.main.FeedFragment;
@ -61,11 +67,14 @@ import awais.instagrabber.models.IntentModel;
import awais.instagrabber.models.SuggestionModel;
import awais.instagrabber.models.enums.SuggestionType;
import awais.instagrabber.services.ActivityCheckerService;
import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.FlavorTown;
import awais.instagrabber.utils.IntentUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.emoji.EmojiParser;
import static awais.instagrabber.utils.NavigationExtensions.setupWithNavController;
import static awais.instagrabber.utils.Utils.settingsHelper;
@ -139,6 +148,12 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
bindActivityCheckerService();
}
getSupportFragmentManager().addOnBackStackChangedListener(this);
// Initialise the internal map
AppExecutors.getInstance().tasksThread().execute(() -> {
EmojiParser.getInstance();
EmojiVariantManager.getInstance();
});
initEmojiCompat();
}
@Override
@ -373,7 +388,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
private void setupBottomNavigationBar(final boolean setDefaultFromSettings) {
int main_nav_ids = R.array.main_nav_ids;
final String cookie = settingsHelper.getString(Constants.COOKIE);
final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != 0;
if (!isLoggedIn) {
main_nav_ids = R.array.logged_out_main_nav_ids;
final int selectedItemId = binding.bottomNavView.getSelectedItemId();
@ -449,12 +464,24 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
if (navController == null) return;
NavigationUI.setupWithNavController(toolbar, navController);
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
if (destination.getId() == R.id.directMessagesThreadFragment && arguments != null) {
// Set the thread title earlier for better ux
final String title = arguments.getString("title");
final ActionBar actionBar = getSupportActionBar();
if (actionBar != null && !TextUtils.isEmpty(title)) {
actionBar.setTitle(title);
}
}
// below is a hack to check if we are at the end of the current stack, to setup the search view
binding.appBarLayout.setExpanded(true, true);
final int destinationId = destination.getId();
@SuppressLint("RestrictedApi") final Deque<NavBackStackEntry> backStack = navController.getBackStack();
setupMenu(backStack.size(), destinationId);
binding.bottomNavView.setVisibility(SHOW_BOTTOM_VIEW_DESTINATIONS.contains(destinationId) ? View.VISIBLE : View.GONE);
// explicitly hide keyboard when we navigate
final View view = getCurrentFocus();
Utils.hideKeyboard(view);
});
}
@ -637,4 +664,44 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
public CollapsingToolbarLayout getCollapsingToolbarView() {
return binding.collapsingToolbarLayout;
}
public AppBarLayout getAppbarLayout() {
return binding.appBarLayout;
}
public void removeLayoutTransition() {
binding.getRoot().setLayoutTransition(null);
}
public void setLayoutTransition() {
binding.getRoot().setLayoutTransition(new LayoutTransition());
}
private void initEmojiCompat() {
// Use a downloadable font for EmojiCompat
final FontRequest fontRequest = new FontRequest(
"com.google.android.gms.fonts",
"com.google.android.gms",
"Noto Color Emoji Compat",
R.array.com_google_android_gms_fonts_certs);
final EmojiCompat.Config config = new FontRequestEmojiCompatConfig(getApplicationContext(), fontRequest);
config.setReplaceAll(true)
// .setUseEmojiAsDefaultStyle(true)
.registerInitCallback(new EmojiCompat.InitCallback() {
@Override
public void onInitialized() {
Log.i(TAG, "EmojiCompat initialized");
}
@Override
public void onFailed(@Nullable Throwable throwable) {
Log.e(TAG, "EmojiCompat initialization failed", throwable);
}
});
EmojiCompat.init(config);
}
public Toolbar getToolbar() {
return binding.toolbar;
}
}

View File

@ -0,0 +1,407 @@
package awais.instagrabber.adapters;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.AdapterListUpdateCallback;
import androidx.recyclerview.widget.AsyncDifferConfig;
import androidx.recyclerview.widget.AsyncListDiffer;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemActionLogViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemAnimatedMediaViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemDefaultViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemLikeViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemLinkViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemMediaShareViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemMediaViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemPlaceholderViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemProfileViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemRavenMediaViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemReelShareViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemStoryShareViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemTextViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemVideoCallEventViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemVoiceMediaViewHolder;
import awais.instagrabber.customviews.emoji.Emoji;
import awais.instagrabber.databinding.LayoutDmActionLogBinding;
import awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmHeaderBinding;
import awais.instagrabber.databinding.LayoutDmLikeBinding;
import awais.instagrabber.databinding.LayoutDmLinkBinding;
import awais.instagrabber.databinding.LayoutDmMediaBinding;
import awais.instagrabber.databinding.LayoutDmMediaShareBinding;
import awais.instagrabber.databinding.LayoutDmProfileBinding;
import awais.instagrabber.databinding.LayoutDmRavenMediaBinding;
import awais.instagrabber.databinding.LayoutDmReelShareBinding;
import awais.instagrabber.databinding.LayoutDmStoryShareBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.databinding.LayoutDmVoiceMediaBinding;
import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.DateUtils;
public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final String TAG = DirectItemsAdapter.class.getSimpleName();
private List<DirectItem> items;
private DirectThread thread;
private DirectItemViewHolder selectedViewHolder;
private final User currentUser;
private final DirectItemCallback callback;
private final AsyncListDiffer<DirectItemOrHeader> differ;
private final DirectItemInternalLongClickListener longClickListener;
private static final DiffUtil.ItemCallback<DirectItemOrHeader> diffCallback = new DiffUtil.ItemCallback<DirectItemOrHeader>() {
@Override
public boolean areItemsTheSame(@NonNull final DirectItemOrHeader oldItem, @NonNull final DirectItemOrHeader newItem) {
final boolean bothHeaders = oldItem.isHeader() && newItem.isHeader();
final boolean bothItems = !oldItem.isHeader() && !newItem.isHeader();
boolean areSameType = bothHeaders || bothItems;
if (!areSameType) return false;
if (bothHeaders) {
return oldItem.date.equals(newItem.date);
}
if (oldItem.item != null && newItem.item != null) {
String oldClientContext = oldItem.item.getClientContext();
if (oldClientContext == null) {
oldClientContext = oldItem.item.getItemId();
}
String newClientContext = newItem.item.getClientContext();
if (newClientContext == null) {
newClientContext = newItem.item.getItemId();
}
return oldClientContext.equals(newClientContext);
}
return false;
}
@Override
public boolean areContentsTheSame(@NonNull final DirectItemOrHeader oldItem, @NonNull final DirectItemOrHeader newItem) {
final boolean bothHeaders = oldItem.isHeader() && newItem.isHeader();
final boolean bothItems = !oldItem.isHeader() && !newItem.isHeader();
boolean areSameType = bothHeaders || bothItems;
if (!areSameType) return false;
if (bothHeaders) {
return oldItem.date.equals(newItem.date);
}
final boolean timestampEqual = oldItem.item.getTimestamp() == newItem.item.getTimestamp();
final boolean bothPending = oldItem.item.isPending() == newItem.item.isPending();
final boolean reactionSame = Objects.equals(oldItem.item.getReactions(), newItem.item.getReactions());
return timestampEqual && bothPending && reactionSame;
}
};
public DirectItemsAdapter(@NonNull final User currentUser,
@NonNull final DirectThread thread,
@NonNull final DirectItemCallback callback,
@NonNull final DirectItemLongClickListener itemLongClickListener) {
this.currentUser = currentUser;
this.thread = thread;
this.callback = callback;
differ = new AsyncListDiffer<>(new AdapterListUpdateCallback(this),
new AsyncDifferConfig.Builder<>(diffCallback).build());
longClickListener = (position, viewHolder) -> {
if (selectedViewHolder != null) {
selectedViewHolder.setSelected(false);
}
selectedViewHolder = viewHolder;
viewHolder.setSelected(true);
itemLongClickListener.onLongClick(position);
};
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
if (type == -1) {
// header
return new HeaderViewHolder(LayoutDmHeaderBinding.inflate(layoutInflater, parent, false));
}
final LayoutDmBaseBinding baseBinding = LayoutDmBaseBinding.inflate(layoutInflater, parent, false);
final DirectItemType directItemType = DirectItemType.valueOf(type);
final DirectItemViewHolder itemViewHolder = getItemViewHolder(layoutInflater, baseBinding, directItemType);
itemViewHolder.setLongClickListener(longClickListener);
return itemViewHolder;
}
@NonNull
private DirectItemViewHolder getItemViewHolder(final LayoutInflater layoutInflater,
final LayoutDmBaseBinding baseBinding,
@NonNull final DirectItemType directItemType) {
switch (directItemType) {
case TEXT: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemTextViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case LIKE: {
final LayoutDmLikeBinding binding = LayoutDmLikeBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemLikeViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case LINK: {
final LayoutDmLinkBinding binding = LayoutDmLinkBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemLinkViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case ACTION_LOG: {
final LayoutDmActionLogBinding binding = LayoutDmActionLogBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemActionLogViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case VIDEO_CALL_EVENT: {
final LayoutDmActionLogBinding binding = LayoutDmActionLogBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemVideoCallEventViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case PLACEHOLDER: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemPlaceholderViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case ANIMATED_MEDIA: {
final LayoutDmAnimatedMediaBinding binding = LayoutDmAnimatedMediaBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemAnimatedMediaViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case VOICE_MEDIA: {
final LayoutDmVoiceMediaBinding binding = LayoutDmVoiceMediaBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemVoiceMediaViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case LOCATION:
case PROFILE: {
final LayoutDmProfileBinding binding = LayoutDmProfileBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemProfileViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case MEDIA: {
final LayoutDmMediaBinding binding = LayoutDmMediaBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemMediaViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case CLIP:
case FELIX_SHARE:
case MEDIA_SHARE: {
final LayoutDmMediaShareBinding binding = LayoutDmMediaShareBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemMediaShareViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case STORY_SHARE: {
final LayoutDmStoryShareBinding binding = LayoutDmStoryShareBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemStoryShareViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case REEL_SHARE: {
final LayoutDmReelShareBinding binding = LayoutDmReelShareBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemReelShareViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case RAVEN_MEDIA: {
final LayoutDmRavenMediaBinding binding = LayoutDmRavenMediaBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemRavenMediaViewHolder(baseBinding, binding, currentUser, thread, callback);
}
default: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemDefaultViewHolder(baseBinding, binding, currentUser, thread, callback);
}
}
}
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
final DirectItemOrHeader itemOrHeader = getItem(position);
if (itemOrHeader.isHeader()) {
((HeaderViewHolder) holder).bind(itemOrHeader.date);
return;
}
if (thread == null) return;
((DirectItemViewHolder) holder).bind(position, itemOrHeader.item);
}
protected DirectItemOrHeader getItem(int position) {
return differ.getCurrentList().get(position);
}
@Override
public int getItemCount() {
return differ.getCurrentList().size();
}
@Override
public int getItemViewType(final int position) {
final DirectItemOrHeader itemOrHeader = getItem(position);
if (itemOrHeader.isHeader()) {
return -1;
}
return itemOrHeader.item.getItemType().getId();
}
@Override
public long getItemId(final int position) {
final DirectItemOrHeader itemOrHeader = getItem(position);
if (itemOrHeader.isHeader()) {
return itemOrHeader.date.hashCode();
}
if (itemOrHeader.item.getClientContext() == null) {
return itemOrHeader.item.getItemId().hashCode();
}
return itemOrHeader.item.getClientContext().hashCode();
}
public void setThread(final DirectThread thread) {
if (thread == null) return;
this.thread = thread;
notifyDataSetChanged();
}
public void submitList(@Nullable final List<DirectItem> list) {
if (list == null) {
differ.submitList(null);
return;
}
differ.submitList(sectionAndSort(list));
this.items = list;
}
public void submitList(@Nullable final List<DirectItem> list, @Nullable final Runnable commitCallback) {
if (list == null) {
differ.submitList(null, commitCallback);
return;
}
differ.submitList(sectionAndSort(list), commitCallback);
this.items = list;
}
private List<DirectItemOrHeader> sectionAndSort(final List<DirectItem> list) {
final List<DirectItemOrHeader> itemOrHeaders = new ArrayList<>();
Date prevSectionDate = null;
for (int i = 0; i < list.size(); i++) {
final DirectItem item = list.get(i);
if (item == null) continue;
final DirectItemOrHeader prev = itemOrHeaders.isEmpty() ? null : itemOrHeaders.get(itemOrHeaders.size() - 1);
if (prev != null && prev.item != null && DateUtils.isSameDay(prev.item.getDate(), item.getDate())) {
// just add item
final DirectItemOrHeader itemOrHeader = new DirectItemOrHeader();
itemOrHeader.item = item;
itemOrHeaders.add(itemOrHeader);
if (i == list.size() - 1) {
// add header
final DirectItemOrHeader itemOrHeader2 = new DirectItemOrHeader();
itemOrHeader2.date = prevSectionDate;
itemOrHeaders.add(itemOrHeader2);
}
continue;
}
if (prevSectionDate != null) {
// add header
final DirectItemOrHeader itemOrHeader = new DirectItemOrHeader();
itemOrHeader.date = prevSectionDate;
itemOrHeaders.add(itemOrHeader);
}
// Add item
final DirectItemOrHeader itemOrHeader = new DirectItemOrHeader();
itemOrHeader.item = item;
itemOrHeaders.add(itemOrHeader);
prevSectionDate = DateUtils.dateAtZeroHours(item.getDate());
}
return itemOrHeaders;
}
public List<DirectItemOrHeader> getList() {
return differ.getCurrentList();
}
public List<DirectItem> getItems() {
return items;
}
@Override
public void onViewRecycled(@NonNull final RecyclerView.ViewHolder holder) {
if (holder instanceof DirectItemViewHolder) {
((DirectItemViewHolder) holder).cleanup();
}
}
@Override
public void onViewDetachedFromWindow(@NonNull final RecyclerView.ViewHolder holder) {
if (holder instanceof DirectItemViewHolder) {
((DirectItemViewHolder) holder).cleanup();
}
}
public DirectThread getThread() {
return thread;
}
public static class DirectItemOrHeader {
Date date;
public DirectItem item;
public boolean isHeader() {
return date != null;
}
@NonNull
@Override
public String toString() {
return "DirectItemOrHeader{" +
"date=" + date +
", item=" + (item != null ? item.getItemType() : null) +
'}';
}
}
public static class HeaderViewHolder extends RecyclerView.ViewHolder {
private final LayoutDmHeaderBinding binding;
public HeaderViewHolder(@NonNull final LayoutDmHeaderBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final Date date) {
if (date == null) {
binding.header.setText("");
return;
}
binding.header.setText(DateFormat.getDateFormat(itemView.getContext()).format(date));
}
}
public interface DirectItemCallback {
void onHashtagClick(String hashtag);
void onMentionClick(String mention);
void onLocationClick(long locationId);
void onURLClick(String url);
void onEmailClick(String email);
void onMediaClick(Media media);
void onStoryClick(DirectItemStoryShare storyShare);
void onReaction(DirectItem item, Emoji emoji);
void onReactionClick(DirectItem item, int position);
void onOptionSelect(DirectItem item, @IdRes int itemId);
}
public interface DirectItemInternalLongClickListener {
void onLongClick(int position, DirectItemViewHolder viewHolder);
}
public interface DirectItemLongClickListener {
void onLongClick(int position);
}
}

View File

@ -4,51 +4,67 @@ import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.AsyncDifferConfig;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import awais.instagrabber.adapters.viewholder.DirectMessageInboxItemViewHolder;
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
import awais.instagrabber.models.direct_messages.InboxThreadModel;
import java.util.List;
public final class DirectMessageInboxAdapter extends ListAdapter<InboxThreadModel, DirectMessageInboxItemViewHolder> {
import awais.instagrabber.adapters.viewholder.directmessages.DirectInboxItemViewHolder;
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
public final class DirectMessageInboxAdapter extends ListAdapter<DirectThread, DirectInboxItemViewHolder> {
private final OnItemClickListener onClickListener;
private static final DiffUtil.ItemCallback<InboxThreadModel> diffCallback = new DiffUtil.ItemCallback<InboxThreadModel>() {
private static final DiffUtil.ItemCallback<DirectThread> diffCallback = new DiffUtil.ItemCallback<DirectThread>() {
@Override
public boolean areItemsTheSame(@NonNull final InboxThreadModel oldItem, @NonNull final InboxThreadModel newItem) {
public boolean areItemsTheSame(@NonNull final DirectThread oldItem, @NonNull final DirectThread newItem) {
return oldItem.getThreadId().equals(newItem.getThreadId());
}
@Override
public boolean areContentsTheSame(@NonNull final InboxThreadModel oldItem, @NonNull final InboxThreadModel newItem) {
return oldItem.equals(newItem);
public boolean areContentsTheSame(@NonNull final DirectThread oldThread,
@NonNull final DirectThread newThread) {
final boolean titleEqual = oldThread.getThreadTitle().equals(newThread.getThreadTitle());
if (!titleEqual) return false;
final List<DirectItem> oldItems = oldThread.getItems();
final List<DirectItem> newItems = newThread.getItems();
if (oldItems == null || newItems == null) return false;
if (oldItems.size() != newItems.size()) return false;
final DirectItem oldItemFirst = oldThread.getFirstDirectItem();
final DirectItem newItemFirst = newThread.getFirstDirectItem();
if (oldItemFirst == null || newItemFirst == null) return false;
return oldItemFirst.getItemId().equals(newItemFirst.getItemId());
}
};
public DirectMessageInboxAdapter(final OnItemClickListener onClickListener) {
super(diffCallback);
super(new AsyncDifferConfig.Builder<>(diffCallback).build());
this.onClickListener = onClickListener;
}
@NonNull
@Override
public DirectMessageInboxItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
public DirectInboxItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final LayoutDmInboxItemBinding binding = LayoutDmInboxItemBinding.inflate(layoutInflater, parent, false);
return new DirectMessageInboxItemViewHolder(binding);
return new DirectInboxItemViewHolder(binding, onClickListener);
}
@Override
public void onBindViewHolder(@NonNull final DirectMessageInboxItemViewHolder holder, final int position) {
final InboxThreadModel threadModel = getItem(position);
if (onClickListener != null) {
holder.itemView.setOnClickListener((v) -> onClickListener.onItemClick(threadModel));
public void onBindViewHolder(@NonNull final DirectInboxItemViewHolder holder, final int position) {
final DirectThread thread = getItem(position);
holder.bind(thread);
}
holder.bind(threadModel);
@Override
public long getItemId(final int position) {
return getItem(position).getThreadId().hashCode();
}
public interface OnItemClickListener {
void onItemClick(final InboxThreadModel inboxThreadModel);
void onItemClick(final DirectThread thread);
}
}

View File

@ -1,152 +0,0 @@
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.List;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageActionLogViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageAnimatedMediaViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageDefaultViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageItemViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageLinkViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageMediaShareViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageMediaViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessagePlaceholderViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageProfileViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageRavenMediaViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageReelShareViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageStoryShareViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageTextViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageVideoCallEventViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageVoiceMediaViewHolder;
import awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmLinkBinding;
import awais.instagrabber.databinding.LayoutDmMediaBinding;
import awais.instagrabber.databinding.LayoutDmMediaShareBinding;
import awais.instagrabber.databinding.LayoutDmProfileBinding;
import awais.instagrabber.databinding.LayoutDmRavenMediaBinding;
import awais.instagrabber.databinding.LayoutDmStoryShareBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.databinding.LayoutDmVoiceMediaBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.enums.DirectItemType;
public final class DirectMessageItemsAdapter extends ListAdapter<DirectItemModel, DirectMessageItemViewHolder> {
private final List<ProfileModel> users;
private final List<ProfileModel> leftUsers;
private final View.OnClickListener onClickListener;
private final MentionClickListener mentionClickListener;
private static final DiffUtil.ItemCallback<DirectItemModel> diffCallback = new DiffUtil.ItemCallback<DirectItemModel>() {
@Override
public boolean areItemsTheSame(@NonNull final DirectItemModel oldItem, @NonNull final DirectItemModel newItem) {
return oldItem.getItemId().equals(newItem.getItemId());
}
@Override
public boolean areContentsTheSame(@NonNull final DirectItemModel oldItem, @NonNull final DirectItemModel newItem) {
return oldItem.getItemId().equals(newItem.getItemId());
}
};
public DirectMessageItemsAdapter(final List<ProfileModel> users,
final List<ProfileModel> leftUsers,
final View.OnClickListener onClickListener,
final MentionClickListener mentionClickListener) {
super(diffCallback);
this.users = users;
this.leftUsers = leftUsers;
this.onClickListener = onClickListener;
this.mentionClickListener = mentionClickListener;
}
@NonNull
@Override
public DirectMessageItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final DirectItemType directItemType = DirectItemType.valueOf(type);
final LayoutDmBaseBinding baseBinding = LayoutDmBaseBinding.inflate(layoutInflater, parent, false);
final ViewGroup itemViewParent = baseBinding.messageCard;
switch (directItemType) {
case LIKE:
case TEXT: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageTextViewHolder(baseBinding, binding, onClickListener, mentionClickListener);
}
case PLACEHOLDER: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessagePlaceholderViewHolder(baseBinding, binding, onClickListener);
}
case ACTION_LOG: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageActionLogViewHolder(baseBinding, binding, onClickListener);
}
case LINK: {
final LayoutDmLinkBinding binding = LayoutDmLinkBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageLinkViewHolder(baseBinding, binding, onClickListener);
}
case MEDIA: {
final LayoutDmMediaBinding binding = LayoutDmMediaBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageMediaViewHolder(baseBinding, binding, onClickListener);
}
case PROFILE: {
final LayoutDmProfileBinding binding = LayoutDmProfileBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageProfileViewHolder(baseBinding, binding, onClickListener);
}
case REEL_SHARE: {
final LayoutDmRavenMediaBinding binding = LayoutDmRavenMediaBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageReelShareViewHolder(baseBinding, binding, onClickListener, mentionClickListener);
}
case MEDIA_SHARE:
case FELIX_SHARE:
case CLIP: {
final LayoutDmMediaShareBinding binding = LayoutDmMediaShareBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageMediaShareViewHolder(baseBinding, binding, onClickListener);
}
case RAVEN_MEDIA: {
final LayoutDmRavenMediaBinding binding = LayoutDmRavenMediaBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageRavenMediaViewHolder(baseBinding, binding, onClickListener);
}
case STORY_SHARE: {
final LayoutDmStoryShareBinding binding = LayoutDmStoryShareBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageStoryShareViewHolder(baseBinding, binding, onClickListener);
}
case VOICE_MEDIA: {
final LayoutDmVoiceMediaBinding binding = LayoutDmVoiceMediaBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageVoiceMediaViewHolder(baseBinding, binding, onClickListener);
}
case ANIMATED_MEDIA: {
final LayoutDmAnimatedMediaBinding binding = LayoutDmAnimatedMediaBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageAnimatedMediaViewHolder(baseBinding, binding, onClickListener);
}
case VIDEO_CALL_EVENT: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageVideoCallEventViewHolder(baseBinding, binding, onClickListener);
}
default: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, itemViewParent, false);
return new DirectMessageDefaultViewHolder(baseBinding, binding, onClickListener);
}
}
}
@Override
public void onBindViewHolder(@NonNull final DirectMessageItemViewHolder holder, final int position) {
final DirectItemModel directItemModel = getItem(position);
holder.bind(directItemModel, users, leftUsers);
}
@Override
public int getItemViewType(final int position) {
return getItem(position).getItemType().getId();
}
}

View File

@ -1,47 +0,0 @@
package awais.instagrabber.adapters;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import awais.instagrabber.adapters.viewholder.FollowsViewHolder;
import awais.instagrabber.databinding.ItemFollowBinding;
import awais.instagrabber.models.ProfileModel;
public final class DirectMessageMembersAdapter extends RecyclerView.Adapter<FollowsViewHolder> {
private final ProfileModel[] profileModels;
private final List<Long> admins;
private final View.OnClickListener onClickListener;
public DirectMessageMembersAdapter(final ProfileModel[] profileModels,
final List<Long> admins,
final View.OnClickListener onClickListener) {
this.profileModels = profileModels;
this.admins = admins;
this.onClickListener = onClickListener;
}
@NonNull
@Override
public FollowsViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemFollowBinding binding = ItemFollowBinding.inflate(layoutInflater, parent, false);
return new FollowsViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final FollowsViewHolder holder, final int position) {
final ProfileModel model = profileModels[position];
holder.bind(model, admins, onClickListener);
}
@Override
public int getItemCount() {
return profileModels.length;
}
}

View File

@ -0,0 +1,117 @@
package awais.instagrabber.adapters;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import awais.instagrabber.adapters.viewholder.directmessages.DirectPendingUserViewHolder;
import awais.instagrabber.databinding.LayoutDmPendingUserItemBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
public final class DirectPendingUsersAdapter extends ListAdapter<DirectPendingUsersAdapter.PendingUser, DirectPendingUserViewHolder> {
private static final DiffUtil.ItemCallback<PendingUser> DIFF_CALLBACK = new DiffUtil.ItemCallback<PendingUser>() {
@Override
public boolean areItemsTheSame(@NonNull final PendingUser oldItem, @NonNull final PendingUser newItem) {
return oldItem.user.getPk() == newItem.user.getPk();
}
@Override
public boolean areContentsTheSame(@NonNull final PendingUser oldItem, @NonNull final PendingUser newItem) {
return Objects.equals(oldItem.user.getUsername(), newItem.user.getUsername()) &&
Objects.equals(oldItem.user.getFullName(), newItem.user.getFullName()) &&
Objects.equals(oldItem.requester, newItem.requester);
}
};
private final PendingUserCallback callback;
public DirectPendingUsersAdapter(final PendingUserCallback callback) {
super(DIFF_CALLBACK);
this.callback = callback;
setHasStableIds(true);
}
public void submitPendingRequests(final DirectThreadParticipantRequestsResponse requests) {
if (requests == null || requests.getUsers() == null) {
submitList(Collections.emptyList());
return;
}
submitList(parse(requests));
}
private List<PendingUser> parse(final DirectThreadParticipantRequestsResponse requests) {
final List<User> users = requests.getUsers();
final Map<Long, String> requesterUsernames = requests.getRequesterUsernames();
return users.stream()
.map(user -> new PendingUser(user, requesterUsernames.get(user.getPk())))
.collect(Collectors.toList());
}
@NonNull
@Override
public DirectPendingUserViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final LayoutDmPendingUserItemBinding binding = LayoutDmPendingUserItemBinding.inflate(layoutInflater, parent, false);
return new DirectPendingUserViewHolder(binding, callback);
}
@Override
public void onBindViewHolder(@NonNull final DirectPendingUserViewHolder holder, final int position) {
final PendingUser pendingUser = getItem(position);
holder.bind(position, pendingUser);
}
@Override
public long getItemId(final int position) {
final PendingUser item = getItem(position);
return item.user.getPk();
}
public static class PendingUser {
private final User user;
private final String requester;
private boolean inProgress;
public PendingUser(final User user, final String requester) {
this.user = user;
this.requester = requester;
}
public User getUser() {
return user;
}
public String getRequester() {
return requester;
}
public boolean isInProgress() {
return inProgress;
}
public PendingUser setInProgress(final boolean inProgress) {
this.inProgress = inProgress;
return this;
}
}
public interface PendingUserCallback {
void onClick(int position, PendingUser pendingUser);
void onApprove(int position, PendingUser pendingUser);
void onDeny(int position, PendingUser pendingUser);
}
}

View File

@ -0,0 +1,81 @@
package awais.instagrabber.adapters;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import java.util.List;
import awais.instagrabber.adapters.viewholder.directmessages.DirectReactionViewHolder;
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;
public final class DirectReactionsAdapter extends ListAdapter<DirectItemEmojiReaction, DirectReactionViewHolder> {
private static final DiffUtil.ItemCallback<DirectItemEmojiReaction> DIFF_CALLBACK = new DiffUtil.ItemCallback<DirectItemEmojiReaction>() {
@Override
public boolean areItemsTheSame(@NonNull final DirectItemEmojiReaction oldItem, @NonNull final DirectItemEmojiReaction newItem) {
return oldItem.getSenderId() == newItem.getSenderId();
}
@Override
public boolean areContentsTheSame(@NonNull final DirectItemEmojiReaction oldItem, @NonNull final DirectItemEmojiReaction newItem) {
return oldItem.getEmoji().equals(newItem.getEmoji());
}
};
private final long viewerId;
private final List<User> users;
private final String itemId;
private final OnReactionClickListener onReactionClickListener;
public DirectReactionsAdapter(final long viewerId,
final List<User> users,
final String itemId,
final OnReactionClickListener onReactionClickListener) {
super(DIFF_CALLBACK);
this.viewerId = viewerId;
this.users = users;
this.itemId = itemId;
this.onReactionClickListener = onReactionClickListener;
setHasStableIds(true);
}
@NonNull
@Override
public DirectReactionViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final LayoutDmUserItemBinding binding = LayoutDmUserItemBinding.inflate(layoutInflater, parent, false);
return new DirectReactionViewHolder(binding, viewerId, itemId, onReactionClickListener);
}
@Override
public void onBindViewHolder(@NonNull final DirectReactionViewHolder holder, final int position) {
final DirectItemEmojiReaction reaction = getItem(position);
if (reaction == null) return;
holder.bind(reaction, getUser(reaction.getSenderId()));
}
@Override
public long getItemId(final int position) {
return getItem(position).getSenderId();
}
@Nullable
private User getUser(final long pk) {
return users.stream()
.filter(user -> user.getPk() == pk)
.findFirst()
.orElse(null);
}
public interface OnReactionClickListener {
void onReactionClick(String itemId, DirectItemEmojiReaction reaction);
}
}

View File

@ -0,0 +1,183 @@
package awais.instagrabber.adapters;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import com.google.common.collect.ImmutableList;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.directmessages.DirectUserViewHolder;
import awais.instagrabber.databinding.ItemFavSectionHeaderBinding;
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
import awais.instagrabber.repositories.responses.User;
public final class DirectUsersAdapter extends ListAdapter<DirectUsersAdapter.DirectUserOrHeader, RecyclerView.ViewHolder> {
private static final int VIEW_TYPE_HEADER = 0;
private static final int VIEW_TYPE_USER = 1;
private static final DiffUtil.ItemCallback<DirectUserOrHeader> DIFF_CALLBACK = new DiffUtil.ItemCallback<DirectUserOrHeader>() {
@Override
public boolean areItemsTheSame(@NonNull final DirectUserOrHeader oldItem, @NonNull final DirectUserOrHeader newItem) {
final boolean bothHeaders = oldItem.isHeader() && newItem.isHeader();
final boolean bothItems = !oldItem.isHeader() && !newItem.isHeader();
boolean areSameType = bothHeaders || bothItems;
if (!areSameType) return false;
if (bothHeaders) {
return oldItem.headerTitle == newItem.headerTitle;
}
if (oldItem.user != null && newItem.user != null) {
return oldItem.user.getPk() == newItem.user.getPk();
}
return false;
}
@Override
public boolean areContentsTheSame(@NonNull final DirectUserOrHeader oldItem, @NonNull final DirectUserOrHeader newItem) {
final boolean bothHeaders = oldItem.isHeader() && newItem.isHeader();
final boolean bothItems = !oldItem.isHeader() && !newItem.isHeader();
boolean areSameType = bothHeaders || bothItems;
if (!areSameType) return false;
if (bothHeaders) {
return oldItem.headerTitle == newItem.headerTitle;
}
if (oldItem.user != null && newItem.user != null) {
return oldItem.user.getUsername().equals(newItem.user.getUsername()) &&
oldItem.user.getFullName().equals(newItem.user.getFullName());
}
return false;
}
};
private final long inviterId;
private final OnDirectUserClickListener onClickListener;
private final OnDirectUserLongClickListener onLongClickListener;
private List<Long> adminUserIds;
public DirectUsersAdapter(final long inviterId,
final OnDirectUserClickListener onClickListener,
final OnDirectUserLongClickListener onLongClickListener) {
super(DIFF_CALLBACK);
this.inviterId = inviterId;
this.onClickListener = onClickListener;
this.onLongClickListener = onLongClickListener;
setHasStableIds(true);
}
public void submitUsers(final List<User> users, final List<User> leftUsers) {
if (users == null && leftUsers == null) return;
final List<DirectUserOrHeader> userOrHeaders = combineLists(users, leftUsers);
submitList(userOrHeaders);
}
private List<DirectUserOrHeader> combineLists(final List<User> users, final List<User> leftUsers) {
final ImmutableList.Builder<DirectUserOrHeader> listBuilder = ImmutableList.builder();
if (users != null && !users.isEmpty()) {
listBuilder.add(new DirectUserOrHeader(R.string.members));
users.stream()
.map(DirectUserOrHeader::new)
.forEach(listBuilder::add);
}
if (leftUsers != null && !leftUsers.isEmpty()) {
listBuilder.add(new DirectUserOrHeader(R.string.dms_left_users));
leftUsers.stream()
.map(DirectUserOrHeader::new)
.forEach(listBuilder::add);
}
return listBuilder.build();
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
switch (viewType) {
case VIEW_TYPE_USER:
final LayoutDmUserItemBinding binding = LayoutDmUserItemBinding.inflate(layoutInflater, parent, false);
return new DirectUserViewHolder(binding, onClickListener, onLongClickListener);
case VIEW_TYPE_HEADER:
default:
final ItemFavSectionHeaderBinding headerBinding = ItemFavSectionHeaderBinding.inflate(layoutInflater, parent, false);
return new HeaderViewHolder(headerBinding);
}
}
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof HeaderViewHolder) {
((HeaderViewHolder) holder).bind(getItem(position).headerTitle);
return;
}
if (holder instanceof DirectUserViewHolder) {
final User user = getItem(position).user;
((DirectUserViewHolder) holder).bind(position,
user,
user != null && adminUserIds != null && adminUserIds.contains(user.getPk()),
user != null && user.getPk() == inviterId,
false,
false);
}
}
@Override
public int getItemViewType(final int position) {
final DirectUserOrHeader item = getItem(position);
return item.isHeader() ? VIEW_TYPE_HEADER : VIEW_TYPE_USER;
}
@Override
public long getItemId(final int position) {
final DirectUserOrHeader item = getItem(position);
return item.isHeader() ? item.headerTitle : item.user.getPk();
}
public void setAdminUserIds(final List<Long> adminUserIds) {
this.adminUserIds = adminUserIds;
notifyDataSetChanged();
}
public static class DirectUserOrHeader {
int headerTitle;
User user;
public DirectUserOrHeader(final int headerTitle) {
this.headerTitle = headerTitle;
}
public DirectUserOrHeader(final User user) {
this.user = user;
}
boolean isHeader() {
return headerTitle > 0;
}
}
public static class HeaderViewHolder extends RecyclerView.ViewHolder {
private final ItemFavSectionHeaderBinding binding;
public HeaderViewHolder(@NonNull final ItemFavSectionHeaderBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(@StringRes final int headerTitle) {
binding.getRoot().setText(headerTitle);
}
}
public interface OnDirectUserClickListener {
void onClick(int position, User user, boolean selected);
}
public interface OnDirectUserLongClickListener {
boolean onLongClick(int position, User user);
}
}

View File

@ -10,7 +10,8 @@ import androidx.recyclerview.widget.ListAdapter;
import awais.instagrabber.adapters.viewholder.TopicClusterViewHolder;
import awais.instagrabber.databinding.ItemDiscoverTopicBinding;
import awais.instagrabber.models.TopicCluster;
import awais.instagrabber.repositories.responses.discover.TopicCluster;
import awais.instagrabber.utils.ResponseBodyUtils;
public class DiscoverTopicsAdapter extends ListAdapter<TopicCluster, TopicClusterViewHolder> {
private static final DiffUtil.ItemCallback<TopicCluster> DIFF_CALLBACK = new DiffUtil.ItemCallback<TopicCluster>() {
@ -21,7 +22,8 @@ public class DiscoverTopicsAdapter extends ListAdapter<TopicCluster, TopicCluste
@Override
public boolean areContentsTheSame(@NonNull final TopicCluster oldItem, @NonNull final TopicCluster newItem) {
return oldItem.getCoverMedia().getDisplayUrl().equals(newItem.getCoverMedia().getDisplayUrl())
final String oldThumbUrl = ResponseBodyUtils.getThumbUrl(oldItem.getCoverMedia());
return oldThumbUrl != null && oldThumbUrl.equals(ResponseBodyUtils.getThumbUrl(newItem.getCoverMedia()))
&& oldItem.getTitle().equals(newItem.getTitle());
}
};
@ -38,7 +40,7 @@ public class DiscoverTopicsAdapter extends ListAdapter<TopicCluster, TopicCluste
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);
return new TopicClusterViewHolder(binding, onTopicClickListener, null);
}
@Override

View File

@ -11,6 +11,7 @@ import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import awais.instagrabber.adapters.viewholder.FeedGridItemViewHolder;
@ -22,43 +23,44 @@ 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;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.repositories.responses.Caption;
import awais.instagrabber.repositories.responses.Media;
public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.ViewHolder> {
public final class FeedAdapterV2 extends ListAdapter<Media, RecyclerView.ViewHolder> {
private static final String TAG = "FeedAdapterV2";
private final FeedItemCallback feedItemCallback;
private final SelectionModeCallback selectionModeCallback;
private final Set<Integer> selectedPositions = new HashSet<>();
private final Set<FeedModel> selectedFeedModels = new HashSet<>();
private final Set<Media> selectedFeedModels = new HashSet<>();
private PostsLayoutPreferences layoutPreferences;
private boolean selectionModeActive = false;
private static final DiffUtil.ItemCallback<FeedModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<FeedModel>() {
private static final DiffUtil.ItemCallback<Media> DIFF_CALLBACK = new DiffUtil.ItemCallback<Media>() {
@Override
public boolean areItemsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
public boolean areItemsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {
return oldItem.getPk().equals(newItem.getPk());
}
@Override
public boolean areContentsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
boolean result = oldItem.getPostId().equals(newItem.getPostId());
if (TextUtils.isEmpty(oldItem.getPostCaption())) {
return result ? TextUtils.isEmpty(newItem.getPostCaption()) : false;
}
else {
return result ? oldItem.getPostCaption().equals(newItem.getPostCaption()) : false;
public boolean areContentsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {
final Caption oldItemCaption = oldItem.getCaption();
final Caption newItemCaption = newItem.getCaption();
return oldItem.getPk().equals(newItem.getPk()) && Objects.equals(getCaptionText(oldItemCaption), getCaptionText(newItemCaption));
}
private String getCaptionText(final Caption caption) {
if (caption == null) return null;
return caption.getText();
}
};
private final AdapterSelectionCallback adapterSelectionCallback = new AdapterSelectionCallback() {
@Override
public boolean onPostLongClick(final int position, final FeedModel feedModel) {
public boolean onPostLongClick(final int position, final Media feedModel) {
if (!selectionModeActive) {
selectionModeActive = true;
notifyDataSetChanged();
@ -76,7 +78,7 @@ public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.Vie
}
@Override
public void onPostClick(final int position, final FeedModel feedModel) {
public void onPostClick(final int position, final Media feedModel) {
if (!selectionModeActive) return;
if (selectedPositions.contains(position)) {
selectedPositions.remove(position);
@ -147,7 +149,7 @@ public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.Vie
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, final int position) {
final FeedModel feedModel = getItem(position);
final Media feedModel = getItem(position);
if (feedModel == null) return;
switch (layoutPreferences.getType()) {
case LINEAR:
@ -168,7 +170,7 @@ public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.Vie
@Override
public int getItemViewType(final int position) {
return getItem(position).getItemType().getId();
return getItem(position).getMediaType().getId();
}
public void setLayoutPreferences(@NonNull final PostsLayoutPreferences layoutPreferences) {
@ -205,43 +207,43 @@ public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.Vie
// }
public interface FeedItemCallback {
void onPostClick(final FeedModel feedModel,
void onPostClick(final Media feedModel,
final View profilePicView,
final View mainPostImage);
void onProfilePicClick(final FeedModel feedModel,
void onProfilePicClick(final Media feedModel,
final View profilePicView);
void onNameClick(final FeedModel feedModel,
void onNameClick(final Media feedModel,
final View profilePicView);
void onLocationClick(final FeedModel feedModel);
void onLocationClick(final Media feedModel);
void onMentionClick(final String mention);
void onHashtagClick(final String hashtag);
void onCommentsClick(final FeedModel feedModel);
void onCommentsClick(final Media feedModel);
void onDownloadClick(final FeedModel feedModel, final int childPosition);
void onDownloadClick(final Media feedModel, final int childPosition);
void onEmailClick(final String emailId);
void onURLClick(final String url);
void onSliderClick(FeedModel feedModel, int position);
void onSliderClick(Media feedModel, int position);
}
public interface AdapterSelectionCallback {
boolean onPostLongClick(final int position, FeedModel feedModel);
boolean onPostLongClick(final int position, Media feedModel);
void onPostClick(final int position, FeedModel feedModel);
void onPostClick(final int position, Media feedModel);
}
public interface SelectionModeCallback {
void onSelectionStart();
void onSelectionChange(final Set<FeedModel> selectedFeedModels);
void onSelectionChange(final Set<Media> selectedFeedModels);
void onSelectionEnd();
}

View File

@ -2,20 +2,21 @@ package awais.instagrabber.adapters;
import android.view.View;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.repositories.responses.Media;
public class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback {
@Override
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {}
public void onPostClick(final Media media, final View profilePicView, final View mainPostImage) {}
@Override
public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {}
public void onProfilePicClick(final Media media, final View profilePicView) {}
@Override
public void onNameClick(final FeedModel feedModel, final View profilePicView) {}
public void onNameClick(final Media media, final View profilePicView) {}
@Override
public void onLocationClick(final FeedModel feedModel) {}
public void onLocationClick(final Media media) {}
@Override
public void onMentionClick(final String mention) {}
@ -24,10 +25,10 @@ public class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback {
public void onHashtagClick(final String hashtag) {}
@Override
public void onCommentsClick(final FeedModel feedModel) {}
public void onCommentsClick(final Media media) {}
@Override
public void onDownloadClick(final FeedModel feedModel, final int childPosition) {}
public void onDownloadClick(final Media media, final int childPosition) {}
@Override
public void onEmailClick(final String emailId) {}
@ -36,5 +37,5 @@ public class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback {
public void onURLClick(final String url) {}
@Override
public void onSliderClick(final FeedModel feedModel, final int position) {}
public void onSliderClick(final Media media, final int position) {}
}

View File

@ -0,0 +1,94 @@
package awais.instagrabber.adapters;
import android.graphics.Bitmap;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import java.util.Collection;
import java.util.List;
import awais.instagrabber.adapters.viewholder.FilterViewHolder;
import awais.instagrabber.databinding.ItemFilterBinding;
import awais.instagrabber.fragments.imageedit.filters.filters.Filter;
import jp.co.cyberagent.android.gpuimage.filter.GPUImageFilter;
public class FiltersAdapter extends ListAdapter<Filter<?>, FilterViewHolder> {
private static final DiffUtil.ItemCallback<Filter<?>> DIFF_CALLBACK = new DiffUtil.ItemCallback<Filter<?>>() {
@Override
public boolean areItemsTheSame(@NonNull final Filter<?> oldItem, @NonNull final Filter<?> newItem) {
return oldItem.getType().equals(newItem.getType());
}
@Override
public boolean areContentsTheSame(@NonNull final Filter<?> oldItem, @NonNull final Filter<?> newItem) {
return oldItem.getType().equals(newItem.getType());
}
};
private final Bitmap bitmap;
private final OnFilterClickListener onFilterClickListener;
private final Collection<GPUImageFilter> filters;
private final String originalKey;
private int selectedPosition = 0;
public FiltersAdapter(final Collection<GPUImageFilter> filters,
final String originalKey,
final Bitmap bitmap,
final OnFilterClickListener onFilterClickListener) {
super(DIFF_CALLBACK);
this.filters = filters;
this.originalKey = originalKey;
this.bitmap = bitmap;
this.onFilterClickListener = onFilterClickListener;
setHasStableIds(true);
}
@NonNull
@Override
public FilterViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemFilterBinding binding = ItemFilterBinding.inflate(layoutInflater, parent, false);
return new FilterViewHolder(binding, filters, onFilterClickListener);
}
@Override
public void onBindViewHolder(@NonNull final FilterViewHolder holder, final int position) {
holder.bind(position, originalKey, bitmap, getItem(position), selectedPosition == position);
}
@Override
public long getItemId(final int position) {
return getItem(position).getLabel();
}
public void setSelected(final int position) {
final int prev = this.selectedPosition;
this.selectedPosition = position;
notifyItemChanged(position);
notifyItemChanged(prev);
}
public void setSelectedFilter(final GPUImageFilter instance) {
final List<Filter<?>> currentList = getCurrentList();
int index = -1;
for (int i = 0; i < currentList.size(); i++) {
final Filter<?> filter = currentList.get(i);
final GPUImageFilter filterInstance = filter.getInstance();
if (filterInstance.getClass() == instance.getClass()) {
index = i;
break;
}
}
if (index < 0) return;
setSelected(index);
}
public interface OnFilterClickListener {
void onClick(int position, Filter<?> filter);
}
}

View File

@ -1,6 +1,5 @@
package awais.instagrabber.adapters;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -12,13 +11,13 @@ import java.util.List;
import awais.instagrabber.adapters.viewholder.FollowsViewHolder;
import awais.instagrabber.databinding.ItemFollowBinding;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.repositories.responses.User;
public final class LikesAdapter extends RecyclerView.Adapter<FollowsViewHolder> {
private final List<ProfileModel> profileModels;
private final List<User> profileModels;
private final View.OnClickListener onClickListener;
public LikesAdapter(final List<ProfileModel> profileModels,
public LikesAdapter(final List<User> profileModels,
final View.OnClickListener onClickListener) {
this.profileModels = profileModels;
this.onClickListener = onClickListener;
@ -34,7 +33,7 @@ public final class LikesAdapter extends RecyclerView.Adapter<FollowsViewHolder>
@Override
public void onBindViewHolder(@NonNull final FollowsViewHolder holder, final int position) {
final ProfileModel model = profileModels.get(position);
final User model = profileModels.get(position);
holder.bind(model, null, onClickListener);
}

View File

@ -0,0 +1,111 @@
package awais.instagrabber.adapters;
import android.net.Uri;
import android.util.Log;
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 com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;
import com.facebook.drawee.controller.BaseControllerListener;
import com.facebook.imagepipeline.common.ResizeOptions;
import com.facebook.imagepipeline.image.ImageInfo;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import java.io.File;
import awais.instagrabber.databinding.ItemMediaBinding;
import awais.instagrabber.utils.MediaController.MediaEntry;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public class MediaItemsAdapter extends ListAdapter<MediaEntry, MediaItemsAdapter.MediaItemViewHolder> {
private static final DiffUtil.ItemCallback<MediaEntry> diffCallback = new DiffUtil.ItemCallback<MediaEntry>() {
@Override
public boolean areItemsTheSame(@NonNull final MediaEntry oldItem, @NonNull final MediaEntry newItem) {
return oldItem.imageId == newItem.imageId;
}
@Override
public boolean areContentsTheSame(@NonNull final MediaEntry oldItem, @NonNull final MediaEntry newItem) {
return oldItem.imageId == newItem.imageId;
}
};
private final OnItemClickListener onItemClickListener;
public MediaItemsAdapter(final OnItemClickListener onItemClickListener) {
super(diffCallback);
this.onItemClickListener = onItemClickListener;
}
@NonNull
@Override
public MediaItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemMediaBinding binding = ItemMediaBinding.inflate(layoutInflater, parent, false);
return new MediaItemViewHolder(binding, onItemClickListener);
}
@Override
public void onBindViewHolder(@NonNull final MediaItemViewHolder holder, final int position) {
holder.bind(getItem(position));
}
public static class MediaItemViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = MediaItemViewHolder.class.getSimpleName();
private static final int size = Utils.displayMetrics.widthPixels / 3;
private final ItemMediaBinding binding;
private final OnItemClickListener onItemClickListener;
public MediaItemViewHolder(@NonNull final ItemMediaBinding binding,
final OnItemClickListener onItemClickListener) {
super(binding.getRoot());
this.binding = binding;
this.onItemClickListener = onItemClickListener;
}
public void bind(final MediaEntry item) {
final Uri uri = Uri.fromFile(new File(item.path));
if (onItemClickListener != null) {
itemView.setOnClickListener(v -> onItemClickListener.onItemClick(item));
}
final BaseControllerListener<ImageInfo> controllerListener = new BaseControllerListener<ImageInfo>() {
@Override
public void onFailure(final String id, final Throwable throwable) {
Log.e(TAG, "onFailure: ", throwable);
}
};
final ImageRequest request = ImageRequestBuilder
.newBuilderWithSource(uri)
.setLocalThumbnailPreviewsEnabled(true)
.setProgressiveRenderingEnabled(false)
.setResizeOptions(ResizeOptions.forDimensions(size, size))
.build();
final PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder()
.setImageRequest(request)
.setControllerListener(controllerListener);
binding.item.setController(builder.build());
if (item.isVideo && item.duration >= 0) {
final String timeString = TextUtils.millisToTimeString(item.duration);
binding.duration.setVisibility(View.VISIBLE);
binding.duration.setText(timeString);
} else {
binding.duration.setVisibility(View.GONE);
}
}
}
public interface OnItemClickListener {
void onItemClick(MediaEntry entry);
}
}

View File

@ -83,7 +83,7 @@ public final class NotificationsAdapter extends ListAdapter<NotificationModel, N
else if (o1.getType() == NotificationType.REQUEST) return -1;
else if (o2.getType() == NotificationType.REQUEST) return 1;
// timestamp
return o1.getTimestamp() > o2.getTimestamp() ? -1 : (o1.getTimestamp() == o2.getTimestamp() ? 0 : 1);
return Long.compare(o2.getTimestamp(), o1.getTimestamp());
});
return listCopy;
}

View File

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

View File

@ -1,45 +0,0 @@
package awais.instagrabber.adapters;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import awais.instagrabber.adapters.viewholder.PostViewHolder;
import awais.instagrabber.databinding.ItemPostBinding;
import awais.instagrabber.models.PostModel;
public final class PostsAdapter extends MultiSelectListAdapter<PostModel, PostViewHolder> {
private static final DiffUtil.ItemCallback<PostModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<PostModel>() {
@Override
public boolean areItemsTheSame(@NonNull final PostModel oldItem, @NonNull final PostModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
@Override
public boolean areContentsTheSame(@NonNull final PostModel oldItem, @NonNull final PostModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
};
public PostsAdapter(final OnItemClickListener<PostModel> clickListener,
final OnItemLongClickListener<PostModel> longClickListener) {
super(DIFF_CALLBACK, clickListener, longClickListener);
}
@NonNull
@Override
public PostViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemPostBinding binding = ItemPostBinding.inflate(layoutInflater, parent, false);
return new PostViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final PostViewHolder holder, final int position) {
final PostModel postModel = getItem(position);
holder.bind(postModel, position, getInternalOnItemClickListener(), getInternalOnLongItemClickListener());
}
}

View File

@ -1,57 +0,0 @@
package awais.instagrabber.adapters;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.PostMediaViewHolder;
import awais.instagrabber.databinding.ItemChildPostBinding;
import awais.instagrabber.models.BasePostModel;
import awais.instagrabber.models.ViewerPostModel;
public final class PostsMediaAdapter extends RecyclerView.Adapter<PostMediaViewHolder> {
private final View.OnClickListener clickListener;
private ViewerPostModel[] postModels;
public PostsMediaAdapter(final ViewerPostModel[] postModels, final View.OnClickListener clickListener) {
this.postModels = postModels;
this.clickListener = clickListener;
}
@NonNull
@Override
public PostMediaViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
layoutInflater.inflate(R.layout.item_child_post, parent, false);
final ItemChildPostBinding binding = ItemChildPostBinding.inflate(layoutInflater, parent, false);
return new PostMediaViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final PostMediaViewHolder holder, final int position) {
final ViewerPostModel postModel = postModels[position];
holder.bind(postModel, position, clickListener);
}
public void setData(final ViewerPostModel[] postModels) {
this.postModels = postModels;
notifyDataSetChanged();
}
public ViewerPostModel getItemAt(final int position) {
return postModels == null ? null : postModels[position];
}
@Override
public int getItemCount() {
return postModels == null ? 0 : postModels.length;
}
public BasePostModel[] getPostModels() {
return postModels;
}
}

View File

@ -0,0 +1,60 @@
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.repositories.responses.saved.SavedCollection;
import awais.instagrabber.utils.ResponseBodyUtils;
public class SavedCollectionsAdapter extends ListAdapter<SavedCollection, TopicClusterViewHolder> {
private static final DiffUtil.ItemCallback<SavedCollection> DIFF_CALLBACK = new DiffUtil.ItemCallback<SavedCollection>() {
@Override
public boolean areItemsTheSame(@NonNull final SavedCollection oldItem, @NonNull final SavedCollection newItem) {
return oldItem.getId().equals(newItem.getId());
}
@Override
public boolean areContentsTheSame(@NonNull final SavedCollection oldItem, @NonNull final SavedCollection newItem) {
if (oldItem.getCoverMedias() != null && newItem.getCoverMedias() != null
&& oldItem.getCoverMedias().size() == newItem.getCoverMedias().size()) {
return oldItem.getCoverMedias().get(0).getId().equals(newItem.getCoverMedias().get(0).getId());
}
else if (oldItem.getCoverMedia() != null && newItem.getCoverMedia() != null) {
return oldItem.getCoverMedia().getId().equals(newItem.getCoverMedia().getId());
}
return false;
}
};
private final OnCollectionClickListener onCollectionClickListener;
public SavedCollectionsAdapter(final OnCollectionClickListener onCollectionClickListener) {
super(DIFF_CALLBACK);
this.onCollectionClickListener = onCollectionClickListener;
}
@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, null, onCollectionClickListener);
}
@Override
public void onBindViewHolder(@NonNull final TopicClusterViewHolder holder, final int position) {
final SavedCollection topicCluster = getItem(position);
holder.bind(topicCluster);
}
public interface OnCollectionClickListener {
void onCollectionClick(SavedCollection savedCollection, View root, View cover, View title, int titleColor, int backgroundColor);
}
}

View File

@ -14,25 +14,25 @@ import awais.instagrabber.customviews.VerticalDragHelper;
import awais.instagrabber.databinding.ItemSliderPhotoBinding;
import awais.instagrabber.databinding.LayoutExoCustomControlsBinding;
import awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding;
import awais.instagrabber.models.PostChild;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.Media;
public final class SliderItemsAdapter extends ListAdapter<PostChild, SliderItemViewHolder> {
public final class SliderItemsAdapter extends ListAdapter<Media, SliderItemViewHolder> {
private final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener;
private final boolean loadVideoOnItemClick;
private final SliderCallback sliderCallback;
private final LayoutExoCustomControlsBinding controlsBinding;
private static final DiffUtil.ItemCallback<PostChild> DIFF_CALLBACK = new DiffUtil.ItemCallback<PostChild>() {
private static final DiffUtil.ItemCallback<Media> DIFF_CALLBACK = new DiffUtil.ItemCallback<Media>() {
@Override
public boolean areItemsTheSame(@NonNull final PostChild oldItem, @NonNull final PostChild newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
public boolean areItemsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {
return oldItem.getPk().equals(newItem.getPk());
}
@Override
public boolean areContentsTheSame(@NonNull final PostChild oldItem, @NonNull final PostChild newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
public boolean areContentsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {
return oldItem.getPk().equals(newItem.getPk());
}
};
@ -66,14 +66,14 @@ public final class SliderItemsAdapter extends ListAdapter<PostChild, SliderItemV
@Override
public void onBindViewHolder(@NonNull final SliderItemViewHolder holder, final int position) {
final PostChild model = getItem(position);
holder.bind(model, position, sliderCallback);
final Media media = getItem(position);
holder.bind(media, position, sliderCallback);
}
@Override
public int getItemViewType(final int position) {
final PostChild viewerPostModel = getItem(position);
return viewerPostModel.getItemType().getId();
final Media media = getItem(position);
return media.getMediaType().getId();
}
// @NonNull

View File

@ -0,0 +1,151 @@
package awais.instagrabber.adapters;
import android.view.LayoutInflater;
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.List;
import java.util.Objects;
import java.util.Set;
import awais.instagrabber.adapters.DirectUsersAdapter.OnDirectUserClickListener;
import awais.instagrabber.adapters.viewholder.directmessages.DirectUserViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.RecipientThreadViewHolder;
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
public final class UserSearchResultsAdapter extends ListAdapter<RankedRecipient, RecyclerView.ViewHolder> {
private static final int VIEW_TYPE_USER = 0;
private static final int VIEW_TYPE_THREAD = 1;
private static final DiffUtil.ItemCallback<RankedRecipient> DIFF_CALLBACK = new DiffUtil.ItemCallback<RankedRecipient>() {
@Override
public boolean areItemsTheSame(@NonNull final RankedRecipient oldItem, @NonNull final RankedRecipient newItem) {
final boolean bothUsers = oldItem.getUser() != null && newItem.getUser() != null;
if (!bothUsers) return false;
final boolean bothThreads = oldItem.getThread() != null && newItem.getThread() != null;
if (!bothThreads) return false;
if (bothUsers) {
return oldItem.getUser().getPk() == newItem.getUser().getPk();
}
return Objects.equals(oldItem.getThread().getThreadId(), newItem.getThread().getThreadId());
}
@Override
public boolean areContentsTheSame(@NonNull final RankedRecipient oldItem, @NonNull final RankedRecipient newItem) {
final boolean bothUsers = oldItem.getUser() != null && newItem.getUser() != null;
if (bothUsers) {
return Objects.equals(oldItem.getUser().getUsername(), newItem.getUser().getUsername()) &&
Objects.equals(oldItem.getUser().getFullName(), newItem.getUser().getFullName());
}
return Objects.equals(oldItem.getThread().getThreadTitle(), newItem.getThread().getThreadTitle());
}
};
private final boolean showSelection;
private final Set<RankedRecipient> selectedRecipients;
private final OnDirectUserClickListener onUserClickListener;
private final OnRecipientClickListener onRecipientClickListener;
public UserSearchResultsAdapter(final boolean showSelection,
final OnRecipientClickListener onRecipientClickListener) {
super(DIFF_CALLBACK);
this.showSelection = showSelection;
selectedRecipients = showSelection ? new HashSet<>() : null;
this.onRecipientClickListener = onRecipientClickListener;
this.onUserClickListener = (position, user, selected) -> {
if (onRecipientClickListener != null) {
onRecipientClickListener.onClick(position, RankedRecipient.of(user), selected);
}
};
setHasStableIds(true);
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final LayoutDmUserItemBinding binding = LayoutDmUserItemBinding.inflate(layoutInflater, parent, false);
if (viewType == VIEW_TYPE_USER) {
return new DirectUserViewHolder(binding, onUserClickListener, null);
}
return new RecipientThreadViewHolder(binding, onRecipientClickListener);
}
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
final RankedRecipient recipient = getItem(position);
final int itemViewType = getItemViewType(position);
if (itemViewType == VIEW_TYPE_USER) {
boolean isSelected = false;
if (selectedRecipients != null) {
isSelected = selectedRecipients.stream()
.anyMatch(rankedRecipient -> rankedRecipient.getUser() != null
&& rankedRecipient.getUser().getPk() == recipient.getUser().getPk());
}
((DirectUserViewHolder) holder).bind(position, recipient.getUser(), false, false, showSelection, isSelected);
return;
}
boolean isSelected = false;
if (selectedRecipients != null) {
isSelected = selectedRecipients.stream()
.anyMatch(rankedRecipient -> rankedRecipient.getThread() != null
&& Objects.equals(rankedRecipient.getThread().getThreadId(), recipient.getThread().getThreadId()));
}
((RecipientThreadViewHolder) holder).bind(position, recipient.getThread(), showSelection, isSelected);
}
@Override
public long getItemId(final int position) {
final RankedRecipient recipient = getItem(position);
if (recipient.getUser() != null) {
return recipient.getUser().getPk();
}
if (recipient.getThread() != null) {
return recipient.getThread().getThreadTitle().hashCode();
}
return 0;
}
@Override
public int getItemViewType(final int position) {
final RankedRecipient recipient = getItem(position);
return recipient.getUser() != null ? VIEW_TYPE_USER : VIEW_TYPE_THREAD;
}
public void setSelectedRecipient(final RankedRecipient recipient, final boolean selected) {
if (selectedRecipients == null || recipient == null || (recipient.getUser() == null && recipient.getThread() == null)) return;
final boolean isUser = recipient.getUser() != null;
int position = -1;
final List<RankedRecipient> currentList = getCurrentList();
for (int i = 0; i < currentList.size(); i++) {
final RankedRecipient temp = currentList.get(i);
if (isUser) {
if (temp.getUser() != null && temp.getUser().getPk() == recipient.getUser().getPk()) {
position = i;
break;
}
continue;
}
if (temp.getThread() != null && Objects.equals(temp.getThread().getThreadId(), recipient.getThread().getThreadId())) {
position = i;
break;
}
}
if (position < 0) return;
if (selected) {
selectedRecipients.add(recipient);
} else {
selectedRecipients.remove(recipient);
}
notifyItemChanged(position);
}
public interface OnRecipientClickListener {
void onClick(int position, RankedRecipient recipient, final boolean isSelected);
}
}

View File

@ -1,132 +0,0 @@
package awais.instagrabber.adapters.viewholder;
import android.content.Context;
import android.net.Uri;
import android.view.View;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.core.text.HtmlCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.view.SimpleDraweeView;
import com.facebook.imagepipeline.common.ResizeOptions;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.direct_messages.InboxThreadModel;
import awais.instagrabber.models.enums.DirectItemType;
public final class DirectMessageInboxItemViewHolder extends RecyclerView.ViewHolder {
private final LinearLayout multipleProfilePicsContainer;
private final SimpleDraweeView[] multipleProfilePics;
private final LayoutDmInboxItemBinding binding;
public DirectMessageInboxItemViewHolder(@NonNull final LayoutDmInboxItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
multipleProfilePicsContainer = binding.multiPicContainer;
final LinearLayout containerChild = (LinearLayout) multipleProfilePicsContainer.getChildAt(1);
multipleProfilePics = new SimpleDraweeView[]{
(SimpleDraweeView) multipleProfilePicsContainer.getChildAt(0),
(SimpleDraweeView) containerChild.getChildAt(0),
(SimpleDraweeView) containerChild.getChildAt(1)
};
binding.tvDate.setSelected(true);
binding.tvUsername.setSelected(true);
}
public void bind(final InboxThreadModel model) {
final DirectItemModel[] itemModels;
if (model == null || (itemModels = model.getItems()) == null) {
return;
}
itemView.setTag(model);
final ProfileModel[] users = model.getUsers();
if (users.length > 1) {
binding.ivProfilePic.setVisibility(View.GONE);
multipleProfilePicsContainer.setVisibility(View.VISIBLE);
for (int i = 0; i < Math.min(3, users.length); ++i) {
multipleProfilePics[i].setImageURI(users[i].getSdProfilePic());
}
} else {
final String uriString = users.length == 1 ? users[0].getSdProfilePic() : null;
if (uriString == null) {
binding.ivProfilePic.setVisibility(View.GONE);
} else {
binding.ivProfilePic.setVisibility(View.VISIBLE);
multipleProfilePicsContainer.setVisibility(View.GONE);
final ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(uriString))
.setResizeOptions(new ResizeOptions(50, 50))
.build();
binding.ivProfilePic.setController(
Fresco.newDraweeControllerBuilder()
.setOldController(binding.ivProfilePic.getController())
.setImageRequest(request)
.build()
);
}
}
binding.tvUsername.setText(model.getThreadTitle());
final int length = itemModels.length;
DirectItemModel lastItemModel = null;
if (length != 0) {
lastItemModel = itemModels[length - 1];
}
if (lastItemModel == null) {
return;
}
final DirectItemType itemType = lastItemModel.getItemType();
// binding.notTextType.setVisibility(itemType != DirectItemType.TEXT ? View.VISIBLE : View.GONE);
final Context context = itemView.getContext();
final CharSequence messageText;
switch (itemType) {
case TEXT:
case LIKE:
messageText = lastItemModel.getText();
break;
case LINK:
messageText = context.getString(R.string.direct_messages_sent_link);
break;
case MEDIA:
case MEDIA_SHARE:
case RAVEN_MEDIA:
case CLIP:
case FELIX_SHARE:
messageText = context.getString(R.string.direct_messages_sent_media);
break;
case ACTION_LOG:
final DirectItemModel.DirectItemActionLogModel logModel = lastItemModel.getActionLogModel();
messageText = logModel != null ? logModel.getDescription() : "...";
break;
case REEL_SHARE:
final DirectItemModel.DirectItemReelShareModel reelShare = lastItemModel.getReelShare();
if (reelShare == null)
messageText = context.getString(R.string.direct_messages_sent_media);
else {
final String reelType = reelShare.getType();
final int textRes;
if ("reply".equals(reelType))
textRes = R.string.direct_messages_replied_story;
else if ("mention".equals(reelType))
textRes = R.string.direct_messages_mention_story;
else if ("reaction".equals(reelType))
textRes = R.string.direct_messages_reacted_story;
else textRes = R.string.direct_messages_sent_media;
messageText = context.getString(textRes) + " : " + reelShare.getText();
}
break;
default:
messageText = "<i>Unsupported message</i>";
}
binding.tvComment.setText(HtmlCompat.fromHtml(messageText.toString(), HtmlCompat.FROM_HTML_MODE_COMPACT));
binding.tvDate.setText(lastItemModel.getDateTime());
binding.unread.setVisibility(model.getUnreadCount() > 0L ? View.VISIBLE : View.INVISIBLE);
}
}

View File

@ -11,6 +11,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;
import com.facebook.imagepipeline.common.ResizeOptions;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
@ -20,9 +21,10 @@ 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.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
import static awais.instagrabber.models.PostsLayoutPreferences.PostsLayoutType.STAGGERED_GRID;
@ -36,7 +38,7 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
}
public void bind(final int position,
@NonNull final FeedModel feedModel,
@NonNull final Media media,
@NonNull final PostsLayoutPreferences layoutPreferences,
final FeedAdapterV2.FeedItemCallback feedItemCallback,
final FeedAdapterV2.AdapterSelectionCallback adapterSelectionCallback,
@ -44,28 +46,117 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
final boolean selected) {
itemView.setOnClickListener(v -> {
if (!selectionModeActive && feedItemCallback != null) {
feedItemCallback.onPostClick(feedModel, binding.profilePic, binding.postImage);
feedItemCallback.onPostClick(media, binding.profilePic, binding.postImage);
return;
}
if (selectionModeActive && adapterSelectionCallback != null) {
adapterSelectionCallback.onPostClick(position, feedModel);
adapterSelectionCallback.onPostClick(position, media);
}
});
if (adapterSelectionCallback != null) {
itemView.setOnLongClickListener(v -> adapterSelectionCallback.onPostLongClick(position, feedModel));
itemView.setOnLongClickListener(v -> adapterSelectionCallback.onPostLongClick(position, media));
}
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();
final float aspectRatio = (float) media.getOriginalWidth() / media.getOriginalHeight();
binding.postImage.setAspectRatio(aspectRatio);
} else {
binding.postImage.setAspectRatio(1);
}
setUserDetails(media, layoutPreferences);
String thumbnailUrl = null;
final int typeIconRes;
switch (media.getMediaType()) {
case MEDIA_TYPE_IMAGE:
typeIconRes = -1;
thumbnailUrl = ResponseBodyUtils.getThumbUrl(media);
break;
case MEDIA_TYPE_VIDEO:
thumbnailUrl = ResponseBodyUtils.getThumbUrl(media);
typeIconRes = R.drawable.exo_icon_play;
break;
case MEDIA_TYPE_SLIDER:
final List<Media> sliderItems = media.getCarouselMedia();
if (sliderItems != null) {
final Media child = sliderItems.get(0);
if (child != null) {
thumbnailUrl = ResponseBodyUtils.getThumbUrl(child);
}
}
typeIconRes = R.drawable.ic_checkbox_multiple_blank_stroke;
break;
default:
typeIconRes = -1;
thumbnailUrl = null;
}
setThumbImage(thumbnailUrl);
if (typeIconRes <= 0) {
binding.typeIcon.setVisibility(View.GONE);
} else {
binding.typeIcon.setVisibility(View.VISIBLE);
binding.typeIcon.setImageResource(typeIconRes);
}
final DownloadedCheckerAsyncTask task = new DownloadedCheckerAsyncTask(result -> {
final List<Boolean> checkList = result.get(media.getPk());
if (checkList == null || checkList.isEmpty()) {
return;
}
switch (media.getMediaType()) {
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);
final List<Media> carouselMedia = media.getCarouselMedia();
boolean allDownloaded = checkList.size() == (carouselMedia == null ? 0 : carouselMedia.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(media);
}
private void setThumbImage(final String thumbnailUrl) {
if (TextUtils.isEmpty(thumbnailUrl)) {
binding.postImage.setController(null);
return;
}
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(thumbnailUrl))
.setResizeOptions(ResizeOptions.forDimensions(binding.postImage.getWidth(),
binding.postImage.getHeight()))
.setLocalThumbnailPreviewsEnabled(true)
.setProgressiveRenderingEnabled(true)
.build();
final PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder()
.setImageRequest(requestBuilder)
.setOldController(binding.postImage.getController());
binding.postImage.setController(builder.build());
}
private void setUserDetails(@NonNull final Media media,
@NonNull final PostsLayoutPreferences layoutPreferences) {
final User user = media.getUser();
if (layoutPreferences.isAvatarVisible()) {
binding.profilePic.setVisibility(TextUtils.isEmpty(feedModel.getProfileModel().getSdProfilePic()) ? View.GONE : View.VISIBLE);
binding.profilePic.setImageURI(feedModel.getProfileModel().getSdProfilePic());
if (user == null) {
binding.profilePic.setVisibility(View.GONE);
} else {
final String profilePicUrl = user.getProfilePicUrl();
if (TextUtils.isEmpty(profilePicUrl)) {
binding.profilePic.setVisibility(View.GONE);
} else {
binding.profilePic.setVisibility(View.VISIBLE);
binding.profilePic.setImageURI(profilePicUrl);
}
}
final ViewGroup.LayoutParams layoutParams = binding.profilePic.getLayoutParams();
@DimenRes final int dimenRes;
switch (layoutPreferences.getProfilePicSize()) {
@ -88,74 +179,19 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
binding.profilePic.setVisibility(View.GONE);
}
if (layoutPreferences.isNameVisible()) {
binding.name.setVisibility(TextUtils.isEmpty(feedModel.getProfileModel().getUsername()) ? View.GONE : View.VISIBLE);
binding.name.setText(feedModel.getProfileModel().getUsername());
if (user == null) {
binding.name.setVisibility(View.GONE);
} else {
final String username = user.getUsername();
if (username == null) {
binding.name.setVisibility(View.GONE);
} else {
binding.name.setVisibility(View.VISIBLE);
binding.name.setText(username);
}
}
} 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<PostChild> 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<Boolean> 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);
}
}

View File

@ -5,7 +5,7 @@ import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.adapters.FeedStoriesAdapter;
import awais.instagrabber.databinding.ItemHighlightBinding;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.repositories.responses.User;
public final class FeedStoryViewHolder extends RecyclerView.ViewHolder {
@ -28,10 +28,10 @@ public final class FeedStoryViewHolder extends RecyclerView.ViewHolder {
if (listener != null) listener.onFeedStoryLongClick(model, position);
return true;
});
final ProfileModel profileModel = model.getProfileModel();
final User profileModel = model.getProfileModel();
binding.title.setText(profileModel.getUsername());
binding.title.setAlpha(model.isFullyRead() ? 0.5F : 1.0F);
binding.icon.setImageURI(profileModel.getSdProfilePic());
binding.icon.setImageURI(profileModel.getProfilePicUrl());
binding.icon.setAlpha(model.isFullyRead() ? 0.5F : 1.0F);
if (model.isLive()) binding.icon.setStoriesBorder(2);

View File

@ -0,0 +1,72 @@
package awais.instagrabber.adapters.viewholder;
import android.graphics.Bitmap;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import awais.instagrabber.adapters.FiltersAdapter;
import awais.instagrabber.databinding.ItemFilterBinding;
import awais.instagrabber.fragments.imageedit.filters.filters.Filter;
import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.BitmapUtils;
import jp.co.cyberagent.android.gpuimage.GPUImage;
import jp.co.cyberagent.android.gpuimage.filter.GPUImageFilter;
public class FilterViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = FilterViewHolder.class.getSimpleName();
private final ItemFilterBinding binding;
private final Collection<GPUImageFilter> tuneFilters;
private final FiltersAdapter.OnFilterClickListener onFilterClickListener;
private final AppExecutors appExecutors;
public FilterViewHolder(@NonNull final ItemFilterBinding binding,
final Collection<GPUImageFilter> tuneFilters,
final FiltersAdapter.OnFilterClickListener onFilterClickListener) {
super(binding.getRoot());
this.binding = binding;
this.tuneFilters = tuneFilters;
this.onFilterClickListener = onFilterClickListener;
appExecutors = AppExecutors.getInstance();
}
public void bind(final int position, final String originalKey, final Bitmap originalBitmap, final Filter<?> item, final boolean isSelected) {
if (originalBitmap == null || item == null) return;
if (onFilterClickListener != null) {
itemView.setOnClickListener(v -> onFilterClickListener.onClick(position, item));
}
if (item.getLabel() != -1) {
binding.name.setVisibility(View.VISIBLE);
binding.name.setText(item.getLabel());
binding.name.setSelected(isSelected);
} else {
binding.name.setVisibility(View.GONE);
}
final String filterKey = item.getLabel() + "_" + originalKey;
// avoid resetting the bitmap
if (binding.preview.getTag() != null && binding.preview.getTag().equals(filterKey)) return;
binding.preview.setTag(filterKey);
final Bitmap bitmap = BitmapUtils.getBitmapFromMemCache(filterKey);
if (bitmap == null) {
final GPUImageFilter filter = item.getInstance();
appExecutors.tasksThread().submit(() -> {
GPUImage.getBitmapForMultipleFilters(
originalBitmap,
ImmutableList.<GPUImageFilter>builder().add(filter).addAll(tuneFilters).build(),
filteredBitmap -> {
BitmapUtils.addBitmapToMemoryCache(filterKey, filteredBitmap, true);
appExecutors.mainThread().execute(() -> binding.getRoot().post(() -> binding.preview.setImageBitmap(filteredBitmap)));
}
);
});
return;
}
binding.getRoot().post(() -> binding.preview.setImageBitmap(bitmap));
}
}

View File

@ -1,6 +1,5 @@
package awais.instagrabber.adapters.viewholder;
import android.util.Log;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
@ -9,7 +8,7 @@ import java.util.List;
import awais.instagrabber.databinding.ItemFollowBinding;
import awais.instagrabber.models.FollowModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.repositories.responses.User;
public final class FollowsViewHolder extends RecyclerView.ViewHolder {
@ -20,18 +19,18 @@ public final class FollowsViewHolder extends RecyclerView.ViewHolder {
this.binding = binding;
}
public void bind(final ProfileModel model,
public void bind(final User model,
final List<Long> admins,
final View.OnClickListener onClickListener) {
if (model == null) return;
itemView.setTag(model);
itemView.setOnClickListener(onClickListener);
binding.tvUsername.setText(model.getUsername());
binding.tvFullName.setText(model.getName());
if (admins != null && admins.contains(Long.parseLong(model.getId()))) {
binding.tvFullName.setText(model.getFullName());
if (admins != null && admins.contains(model.getPk())) {
binding.isAdmin.setVisibility(View.VISIBLE);
}
binding.ivProfilePic.setImageURI(model.getSdProfilePic());
binding.ivProfilePic.setImageURI(model.getProfilePicUrl());
}
public void bind(final FollowModel model,

View File

@ -1,9 +1,7 @@
package awais.instagrabber.adapters.viewholder;
import android.text.Spannable;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
@ -54,7 +52,7 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder {
subtext = model.getText();
break;
case AYML:
subtext = model.getPostId();
subtext = model.getPreviewPic();
break;
}
binding.tvSubComment.setText(model.getType() == NotificationType.AYML ? model.getText() : subtext);
@ -62,8 +60,7 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder {
binding.tvComment.setText(subtext);
binding.tvComment.setVisibility(TextUtils.isEmpty(subtext) ? View.GONE : View.VISIBLE);
binding.tvSubComment.setVisibility(model.getType() == NotificationType.AYML ? View.VISIBLE : View.GONE);
}
else if (text != -1) {
} else if (text != -1) {
binding.tvComment.setText(text);
binding.tvSubComment.setVisibility(subtext == null ? View.GONE : View.VISIBLE);
}
@ -81,8 +78,7 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder {
if (model.getType() == NotificationType.AYML) {
binding.ivPreviewPic.setVisibility(View.GONE);
}
else if (TextUtils.isEmpty(model.getPreviewPic())) {
} else if (TextUtils.isEmpty(model.getPreviewPic())) {
binding.ivPreviewPic.setVisibility(View.INVISIBLE);
} else {
binding.ivPreviewPic.setVisibility(View.VISIBLE);

View File

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

View File

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

View File

@ -6,7 +6,7 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.adapters.SliderItemsAdapter;
import awais.instagrabber.models.PostChild;
import awais.instagrabber.repositories.responses.Media;
public abstract class SliderItemViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = "FeedSliderItemViewHolder";
@ -15,7 +15,7 @@ public abstract class SliderItemViewHolder extends RecyclerView.ViewHolder {
super(itemView);
}
public abstract void bind(final PostChild model,
public abstract void bind(final Media media,
final int position,
final SliderItemsAdapter.SliderCallback sliderCallback);
}

View File

@ -2,6 +2,8 @@ package awais.instagrabber.adapters.viewholder;
import android.graphics.drawable.Animatable;
import android.net.Uri;
import android.view.GestureDetector;
import android.view.MotionEvent;
import androidx.annotation.NonNull;
@ -15,7 +17,8 @@ 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;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.utils.ResponseBodyUtils;
public class SliderPhotoViewHolder extends SliderItemViewHolder {
private static final String TAG = "FeedSliderPhotoViewHolder";
@ -30,11 +33,11 @@ public class SliderPhotoViewHolder extends SliderItemViewHolder {
this.onVerticalDragListener = onVerticalDragListener;
}
public void bind(@NonNull final PostChild model,
public void bind(@NonNull final Media model,
final int position,
final SliderItemsAdapter.SliderCallback sliderCallback) {
final ImageRequest requestBuilder = ImageRequestBuilder
.newBuilderWithSource(Uri.parse(model.getDisplayUrl()))
.newBuilderWithSource(Uri.parse(ResponseBodyUtils.getImageUrl(model)))
.setLocalThumbnailPreviewsEnabled(true)
.build();
binding.getRoot()
@ -57,11 +60,21 @@ public class SliderPhotoViewHolder extends SliderItemViewHolder {
}
}
})
.setLowResImageRequest(ImageRequest.fromUri(model.getThumbnailUrl()))
.setLowResImageRequest(ImageRequest.fromUri(ResponseBodyUtils.getThumbUrl(model)))
.build());
binding.getRoot().setOnClickListener(v -> {
// binding.getRoot().setOnClickListener(v -> {
// if (sliderCallback != null) {
// sliderCallback.onItemClicked(position);
// }
// });
binding.getRoot().setTapListener(new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(final MotionEvent e) {
if (sliderCallback != null) {
sliderCallback.onItemClicked(position);
return true;
}
return false;
}
});
final AnimatedZoomableController zoomableController = AnimatedZoomableController.newInstance();

View File

@ -5,15 +5,19 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import java.util.List;
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.repositories.responses.Media;
import awais.instagrabber.repositories.responses.VideoVersion;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
@ -57,7 +61,7 @@ public class SliderVideoViewHolder extends SliderItemViewHolder {
}
}
public void bind(@NonNull final PostChild model,
public void bind(@NonNull final Media media,
final int position,
final SliderItemsAdapter.SliderCallback sliderCallback) {
final float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
@ -82,7 +86,7 @@ public class SliderVideoViewHolder extends SliderItemViewHolder {
// 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());
final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, media.getOriginalHeight(), media.getOriginalWidth());
layoutParams.width = requiredWidth;
layoutParams.height = resultingHeight;
binding.playerView.requestLayout();
@ -103,13 +107,21 @@ public class SliderVideoViewHolder extends SliderItemViewHolder {
}
}
};
final float aspectRatio = (float) model.getWidth() / model.getHeight();
final float aspectRatio = (float) media.getOriginalWidth() / media.getOriginalHeight();
String videoUrl = null;
final List<VideoVersion> videoVersions = media.getVideoVersions();
if (videoVersions != null && !videoVersions.isEmpty()) {
final VideoVersion videoVersion = videoVersions.get(0);
if (videoVersion != null) {
videoUrl = videoVersion.getUrl();
}
}
videoPlayerViewHelper = new VideoPlayerViewHelper(binding.getRoot().getContext(),
binding,
model.getDisplayUrl(),
videoUrl,
vol,
aspectRatio,
model.getThumbnailUrl(),
ResponseBodyUtils.getThumbUrl(media),
loadVideoOnItemClick,
controlsBinding,
videoPlayerCallback);

View File

@ -1,6 +1,5 @@
package awais.instagrabber.adapters.viewholder;
import android.text.TextUtils;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
@ -34,7 +33,7 @@ public final class StoryListViewHolder extends RecyclerView.ViewHolder {
binding.tvDate.setText(model.getDateTime());
binding.tvUsername.setText(model.getProfileModel().getUsername());
binding.ivProfilePic.setImageURI(model.getProfileModel().getSdProfilePic());
binding.ivProfilePic.setImageURI(model.getProfileModel().getProfilePicUrl());
binding.ivProfilePic.setOnClickListener(v -> {
if (notificationClickListener == null) return;
notificationClickListener.onProfileClick(model.getProfileModel().getUsername());
@ -43,8 +42,7 @@ public final class StoryListViewHolder extends RecyclerView.ViewHolder {
if (model.getFirstStoryModel() != null) {
binding.ivPreviewPic.setVisibility(View.VISIBLE);
binding.ivPreviewPic.setImageURI(model.getFirstStoryModel().getThumbnail());
}
else binding.ivPreviewPic.setVisibility(View.INVISIBLE);
} else binding.ivPreviewPic.setVisibility(View.INVISIBLE);
float alpha = model.isFullyRead() ? 0.5F : 1.0F;
binding.ivProfilePic.setAlpha(alpha);

View File

@ -25,18 +25,24 @@ import java.util.concurrent.atomic.AtomicInteger;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DiscoverTopicsAdapter;
import awais.instagrabber.adapters.SavedCollectionsAdapter;
import awais.instagrabber.databinding.ItemDiscoverTopicBinding;
import awais.instagrabber.models.TopicCluster;
import awais.instagrabber.repositories.responses.discover.TopicCluster;
import awais.instagrabber.repositories.responses.saved.SavedCollection;
import awais.instagrabber.utils.ResponseBodyUtils;
public class TopicClusterViewHolder extends RecyclerView.ViewHolder {
private final ItemDiscoverTopicBinding binding;
private final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener;
private final SavedCollectionsAdapter.OnCollectionClickListener onCollectionClickListener;
public TopicClusterViewHolder(@NonNull final ItemDiscoverTopicBinding binding,
final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener) {
final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener,
final SavedCollectionsAdapter.OnCollectionClickListener onCollectionClickListener) {
super(binding.getRoot());
this.binding = binding;
this.onTopicClickListener = onTopicClickListener;
this.onCollectionClickListener = onCollectionClickListener;
}
public void bind(final TopicCluster topicCluster) {
@ -57,8 +63,12 @@ public class TopicClusterViewHolder extends RecyclerView.ViewHolder {
}
// binding.title.setTransitionName("title-" + topicCluster.getId());
binding.cover.setTransitionName("cover-" + topicCluster.getId());
final String thumbUrl = ResponseBodyUtils.getThumbUrl(topicCluster.getCoverMedia());
if (thumbUrl == null) {
binding.cover.setImageURI((String) null);
} else {
final ImageRequest imageRequest = ImageRequestBuilder
.newBuilderWithSource(Uri.parse(topicCluster.getCoverMedia().getDisplayUrl()))
.newBuilderWithSource(Uri.parse(thumbUrl))
.build();
final ImagePipeline imagePipeline = Fresco.getImagePipeline();
final DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline
@ -94,6 +104,72 @@ public class TopicClusterViewHolder extends RecyclerView.ViewHolder {
}
}, CallerThreadExecutor.getInstance());
binding.cover.setImageRequest(imageRequest);
}
binding.title.setText(topicCluster.getTitle());
}
public void bind(final SavedCollection topicCluster) {
if (topicCluster == null) {
return;
}
final AtomicInteger titleColor = new AtomicInteger(-1);
final AtomicInteger backgroundColor = new AtomicInteger(-1);
if (onCollectionClickListener != null) {
itemView.setOnClickListener(v -> onCollectionClickListener.onCollectionClick(
topicCluster,
binding.getRoot(),
binding.cover,
binding.title,
titleColor.get(),
backgroundColor.get()
));
}
// binding.title.setTransitionName("title-" + topicCluster.getId());
binding.cover.setTransitionName("cover-" + topicCluster.getId());
final String thumbUrl = ResponseBodyUtils.getThumbUrl(topicCluster.getCoverMedias() == null
? topicCluster.getCoverMedia()
: topicCluster.getCoverMedias().get(0));
if (thumbUrl == null) {
binding.cover.setImageURI((String) null);
} else {
final ImageRequest imageRequest = ImageRequestBuilder
.newBuilderWithSource(Uri.parse(thumbUrl))
.build();
final ImagePipeline imagePipeline = Fresco.getImagePipeline();
final DataSource<CloseableReference<CloseableImage>> 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());
}
}

View File

@ -9,7 +9,7 @@ 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.repositories.responses.User;
import awais.instagrabber.utils.Utils;
public final class ChildCommentViewHolder extends RecyclerView.ViewHolder {
@ -75,10 +75,10 @@ public final class ChildCommentViewHolder extends RecyclerView.ViewHolder {
}
private void setUser(final CommentModel comment) {
final ProfileModel profileModel = comment.getProfileModel();
final User profileModel = comment.getProfileModel();
if (profileModel == null) return;
binding.tvUsername.setText(profileModel.getUsername());
binding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
binding.ivProfilePic.setImageURI(profileModel.getProfilePicUrl());
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
}

View File

@ -9,7 +9,7 @@ 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.repositories.responses.User;
import awais.instagrabber.utils.Utils;
public final class ParentCommentViewHolder extends RecyclerView.ViewHolder {
@ -75,10 +75,10 @@ public final class ParentCommentViewHolder extends RecyclerView.ViewHolder {
}
private void setUser(final CommentModel comment) {
final ProfileModel profileModel = comment.getProfileModel();
final User profileModel = comment.getProfileModel();
if (profileModel == null) return;
binding.tvUsername.setText(profileModel.getUsername());
binding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
binding.ivProfilePic.setImageURI(profileModel.getProfilePicUrl());
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
}

View File

@ -0,0 +1,359 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.graphics.Typeface;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.view.SimpleDraweeView;
import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectMessageInboxAdapter.OnItemClickListener;
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemReelShare;
import awais.instagrabber.repositories.responses.directmessages.DirectItemVisualMedia;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDirectStory;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadLastSeenAt;
import awais.instagrabber.repositories.responses.directmessages.RavenExpiringMediaActionSummary;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
public final class DirectInboxItemViewHolder extends RecyclerView.ViewHolder {
// private static final String TAG = "DMInboxItemVH";
private final LayoutDmInboxItemBinding binding;
private final OnItemClickListener onClickListener;
private final List<SimpleDraweeView> multipleProfilePics;
private final int childSmallSize;
private final int childTinySize;
public DirectInboxItemViewHolder(@NonNull final LayoutDmInboxItemBinding binding,
final OnItemClickListener onClickListener) {
super(binding.getRoot());
this.binding = binding;
this.onClickListener = onClickListener;
multipleProfilePics = ImmutableList.of(
binding.multiPic1,
binding.multiPic2,
binding.multiPic3
);
childSmallSize = itemView.getResources().getDimensionPixelSize(R.dimen.dm_inbox_avatar_size_small);
childTinySize = itemView.getResources().getDimensionPixelSize(R.dimen.dm_inbox_avatar_size_tiny);
}
public void bind(final DirectThread thread) {
if (thread == null) return;
if (onClickListener != null) {
itemView.setOnClickListener((v) -> onClickListener.onItemClick(thread));
}
setProfilePics(thread);
setTitle(thread);
final List<DirectItem> items = thread.getItems();
if (items == null || items.isEmpty()) return;
final DirectItem item = thread.getFirstDirectItem();
if (item == null) return;
setDateTime(item);
setSubtitle(thread);
setReadState(thread);
}
private void setProfilePics(@NonNull final DirectThread thread) {
final List<User> users = thread.getUsers();
if (users.size() > 1) {
binding.profilePic.setVisibility(View.GONE);
binding.multiPicContainer.setVisibility(View.VISIBLE);
for (int i = 0; i < Math.min(3, users.size()); ++i) {
final User user = users.get(i);
final SimpleDraweeView view = multipleProfilePics.get(i);
view.setVisibility(user == null ? View.GONE : View.VISIBLE);
if (user == null) return;
final String profilePicUrl = user.getProfilePicUrl();
view.setImageURI(profilePicUrl);
setChildSize(view, users.size());
if (i == 1) {
updateConstraints(view, users.size());
}
view.requestLayout();
}
return;
}
binding.profilePic.setVisibility(View.VISIBLE);
binding.multiPicContainer.setVisibility(View.GONE);
final String profilePicUrl = users.size() == 1 ? users.get(0).getProfilePicUrl() : null;
if (profilePicUrl == null) {
binding.profilePic.setController(null);
return;
}
binding.profilePic.setImageURI(profilePicUrl);
}
private void updateConstraints(final SimpleDraweeView view, final int length) {
final ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) view.getLayoutParams();
if (length >= 2) {
layoutParams.endToEnd = ConstraintSet.PARENT_ID;
layoutParams.bottomToBottom = ConstraintSet.PARENT_ID;
}
if (length == 3) {
layoutParams.startToStart = ConstraintSet.PARENT_ID;
layoutParams.topToTop = ConstraintSet.PARENT_ID;
}
}
private void setChildSize(final SimpleDraweeView view, final int length) {
final int size = length == 3 ? childTinySize : childSmallSize;
final ConstraintLayout.LayoutParams viewLayoutParams = new ConstraintLayout.LayoutParams(size, size);
view.setLayoutParams(viewLayoutParams);
}
private void setTitle(@NonNull final DirectThread thread) {
final String threadTitle = thread.getThreadTitle();
binding.threadTitle.setText(threadTitle);
}
private void setSubtitle(@NonNull final DirectThread thread) {
// If there is an unopened raven media, give it highest priority
final DirectThreadDirectStory directStory = thread.getDirectStory();
final long viewerId = thread.getViewerId();
if (directStory != null && !directStory.getItems().isEmpty()) {
final DirectItem item = directStory.getItems().get(0);
final MediaItemType mediaType = item.getVisualMedia().getMedia().getMediaType();
final String username = getUsername(thread.getUsers(), item.getUserId(), viewerId);
final String subtitle = getMediaSpecificSubtitle(username, mediaType);
binding.subtitle.setText(subtitle);
return;
}
final DirectItem item = thread.getFirstDirectItem();
if (item == null) return;
final long senderId = item.getUserId();
final DirectItemType itemType = item.getItemType();
String subtitle = null;
final String username = getUsername(thread.getUsers(), senderId, viewerId);
String message = "";
if (itemType == null) {
message = "Unsupported message";
} else {
switch (itemType) {
case TEXT:
message = item.getText();
break;
case LIKE:
message = item.getLike();
break;
case LINK:
message = item.getLink().getText();
break;
case PLACEHOLDER:
message = item.getPlaceholder().getMessage();
break;
case MEDIA_SHARE:
subtitle = String.format("%s shared a post", username != null ? username : "");
break;
case ANIMATED_MEDIA:
subtitle = String.format("%s shared a gif", username != null ? username : "");
break;
case PROFILE:
subtitle = String.format("%s shared a profile: @%s", username != null ? username : "", item.getProfile().getUsername());
break;
case LOCATION:
subtitle = String.format("%s shared a location: %s", username != null ? username : "", item.getLocation().getName());
break;
case MEDIA: {
final MediaItemType mediaType = item.getMedia().getMediaType();
subtitle = getMediaSpecificSubtitle(username, mediaType);
break;
}
case STORY_SHARE: {
final String reelType = item.getStoryShare().getReelType();
if (reelType == null) {
subtitle = item.getStoryShare().getTitle();
} else {
String format = "%s shared a story by @%s";
if (reelType.equals("highlight_reel")) {
format = "%s shared a story highlight by @%s";
}
subtitle = String.format(format, username != null ? username : "",
item.getStoryShare().getMedia().getUser().getUsername());
}
break;
}
case VOICE_MEDIA:
subtitle = String.format("%s sent a voice message", username != null ? username : "");
break;
case ACTION_LOG:
subtitle = item.getActionLog().getDescription();
break;
case VIDEO_CALL_EVENT:
subtitle = item.getVideoCallEvent().getDescription();
break;
case CLIP:
subtitle = String.format("%s shared a clip by @%s", username != null ? username : "",
item.getClip().getClip().getUser().getUsername());
break;
case FELIX_SHARE:
subtitle = String.format("%s shared an IGTV video by @%s", username != null ? username : "",
item.getFelixShare().getVideo().getUser().getUsername());
break;
case RAVEN_MEDIA:
subtitle = getRavenMediaSubtitle(item, username);
break;
case REEL_SHARE:
final DirectItemReelShare reelShare = item.getReelShare();
if (reelShare == null) {
subtitle = "";
break;
}
final String reelType = reelShare.getType();
switch (reelType) {
case "reply":
if (viewerId == item.getUserId()) {
subtitle = String.format("You replied to their story: %s", reelShare.getText());
} else {
subtitle = String.format("%s replied to your story: %s", username != null ? username : "", reelShare.getText());
}
break;
case "mention":
if (viewerId == item.getUserId()) {
// You mentioned the other person
final long mentionedUserId = item.getReelShare().getMentionedUserId();
final String otherUsername = getUsername(thread.getUsers(), mentionedUserId, viewerId);
subtitle = String.format("You mentioned @%s in your story", otherUsername);
} else {
// They mentioned you
subtitle = String.format("%s mentioned you in their story", username != null ? username : "");
}
break;
case "reaction":
if (viewerId == item.getUserId()) {
subtitle = String.format("You reacted to their story: %s", reelShare.getText());
} else {
subtitle = String.format("%s reacted to your story: %s", username != null ? username : "", reelShare.getText());
}
break;
default:
subtitle = "";
break;
}
break;
default:
message = "Unsupported message";
}
}
if (subtitle == null) {
if (thread.getUsers().size() > 1
|| (thread.getUsers().size() == 1 && senderId == viewerId)) {
subtitle = String.format("%s: %s", username != null ? username : "", message);
} else {
subtitle = message;
}
}
binding.subtitle.setText(subtitle != null ? subtitle : "");
}
private String getMediaSpecificSubtitle(final String username, final MediaItemType mediaType) {
final String userSharedAnImage = String.format("%s shared an image", username != null ? username : "");
final String userSharedAVideo = String.format("%s shared a video", username != null ? username : "");
final String userSentAMessage = String.format("%s sent a message", username != null ? username : "");
String subtitle;
switch (mediaType) {
case MEDIA_TYPE_IMAGE:
subtitle = userSharedAnImage;
break;
case MEDIA_TYPE_VIDEO:
subtitle = userSharedAVideo;
break;
default:
subtitle = userSentAMessage;
break;
}
return subtitle;
}
private String getRavenMediaSubtitle(final DirectItem item,
final String username) {
String subtitle = "";
final DirectItemVisualMedia visualMedia = item.getVisualMedia();
final RavenExpiringMediaActionSummary summary = visualMedia.getExpiringMediaActionSummary();
if (summary != null) {
final RavenExpiringMediaActionSummary.ActionType expiringMediaType = summary.getType();
int textRes = 0;
switch (expiringMediaType) {
case DELIVERED:
textRes = R.string.dms_inbox_raven_media_delivered;
break;
case SENT:
textRes = R.string.dms_inbox_raven_media_sent;
break;
case OPENED:
textRes = R.string.dms_inbox_raven_media_opened;
break;
case REPLAYED:
textRes = R.string.dms_inbox_raven_media_replayed;
break;
case SENDING:
textRes = R.string.dms_inbox_raven_media_sending;
break;
case BLOCKED:
textRes = R.string.dms_inbox_raven_media_blocked;
break;
case SUGGESTED:
textRes = R.string.dms_inbox_raven_media_suggested;
break;
case SCREENSHOT:
textRes = R.string.dms_inbox_raven_media_screenshot;
break;
case CANNOT_DELIVER:
textRes = R.string.dms_inbox_raven_media_cant_deliver;
break;
}
if (textRes > 0) {
subtitle += itemView.getContext().getString(textRes);
}
return subtitle;
}
final MediaItemType mediaType = visualMedia.getMedia().getMediaType();
subtitle = getMediaSpecificSubtitle(username, mediaType);
return subtitle;
}
private String getUsername(final List<User> users,
final long userId,
final long viewerId) {
if (userId == viewerId) {
return "You";
}
final Optional<User> senderOptional = users.stream()
.filter(Objects::nonNull)
.filter(user -> user.getPk() == userId)
.findFirst();
return senderOptional.map(User::getUsername).orElse(null);
}
private void setDateTime(@NonNull final DirectItem item) {
final long timestamp = item.getTimestamp() / 1000;
final String dateTimeString = TextUtils.getRelativeDateTimeString(itemView.getContext(), timestamp);
binding.tvDate.setText(dateTimeString);
}
private void setReadState(@NonNull final DirectThread thread) {
final DirectItem item = thread.getItems().get(0);
final Map<Long, DirectThreadLastSeenAt> lastSeenAtMap = thread.getLastSeenAt();
final boolean read = ResponseBodyUtils.isRead(item, lastSeenAtMap, Collections.singletonList(thread.getViewerId()), thread.getDirectStory());
binding.unread.setVisibility(read ? View.GONE : View.VISIBLE);
binding.threadTitle.setTypeface(binding.threadTitle.getTypeface(), read ? Typeface.NORMAL : Typeface.BOLD);
binding.subtitle.setTypeface(binding.subtitle.getTypeface(), read ? Typeface.NORMAL : Typeface.BOLD);
}
}

View File

@ -0,0 +1,100 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.graphics.Typeface;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmActionLogBinding;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemActionLog;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.TextUtils;
public class DirectItemActionLogViewHolder extends DirectItemViewHolder {
private static final String TAG = DirectItemActionLogViewHolder.class.getSimpleName();
private final LayoutDmActionLogBinding binding;
public DirectItemActionLogViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
final LayoutDmActionLogBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
binding.tvMessage.setMovementMethod(LinkMovementMethod.getInstance());
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final DirectItemActionLog actionLog = directItemModel.getActionLog();
final String text = actionLog.getDescription();
final SpannableStringBuilder sb = new SpannableStringBuilder(text);
final List<DirectItemActionLog.TextRange> bold = actionLog.getBold();
if (bold != null && !bold.isEmpty()) {
for (final DirectItemActionLog.TextRange textRange : bold) {
final StyleSpan boldStyleSpan = new StyleSpan(Typeface.BOLD);
sb.setSpan(boldStyleSpan, textRange.getStart(), textRange.getEnd(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
}
final List<DirectItemActionLog.TextRange> textAttributes = actionLog.getTextAttributes();
if (textAttributes != null && !textAttributes.isEmpty()) {
for (final DirectItemActionLog.TextRange textAttribute : textAttributes) {
if (!TextUtils.isEmpty(textAttribute.getColor())) {
final ForegroundColorSpan colorSpan = new ForegroundColorSpan(itemView.getResources().getColor(R.color.deep_orange_400));
sb.setSpan(colorSpan, textAttribute.getStart(), textAttribute.getEnd(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
if (!TextUtils.isEmpty(textAttribute.getIntent())) {
final ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(@NonNull final View widget) {
handleDeepLink(textAttribute.getIntent());
}
};
sb.setSpan(clickableSpan, textAttribute.getStart(), textAttribute.getEnd(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
}
}
binding.tvMessage.setText(sb);
}
@Override
protected boolean allowMessageDirectionGravity() {
return false;
}
@Override
protected boolean showUserDetailsInGroup() {
return false;
}
@Override
protected boolean showMessageInfo() {
return false;
}
@Override
protected boolean allowLongClick() {
return false;
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,68 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import androidx.recyclerview.widget.ItemTouchHelper;
import com.facebook.drawee.backends.pipeline.Fresco;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.repositories.responses.AnimatedMediaFixedHeight;
import awais.instagrabber.repositories.responses.AnimatedMediaImages;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemAnimatedMedia;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.NumberUtils;
public class DirectItemAnimatedMediaViewHolder extends DirectItemViewHolder {
private final LayoutDmAnimatedMediaBinding binding;
public DirectItemAnimatedMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmAnimatedMediaBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem item, final MessageDirection messageDirection) {
final DirectItemAnimatedMedia animatedMediaModel = item.getAnimatedMedia();
final AnimatedMediaImages images = animatedMediaModel.getImages();
if (images == null) return;
final AnimatedMediaFixedHeight fixedHeight = images.getFixedHeight();
if (fixedHeight == null) return;
final String url = fixedHeight.getWebp();
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
fixedHeight.getHeight(),
fixedHeight.getWidth(),
mediaImageMaxHeight,
mediaImageMaxWidth
);
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();
binding.ivAnimatedMessage.setController(Fresco.newDraweeControllerBuilder()
.setUri(url)
.setAutoPlayAnimations(true)
.build());
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,44 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
public class DirectItemDefaultViewHolder extends DirectItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectItemDefaultViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmTextBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final Context context = itemView.getContext();
binding.tvMessage.setText(context.getText(R.string.dms_inbox_raven_message_unknown));
}
@Override
protected boolean allowLongClick() {
return false;
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,36 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmLikeBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
public class DirectItemLikeViewHolder extends DirectItemViewHolder {
public DirectItemLikeViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmLikeBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {}
@Override
protected boolean canForward() {
return false;
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,83 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmLinkBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemLink;
import awais.instagrabber.repositories.responses.directmessages.DirectItemLinkContext;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.TextUtils;
public class DirectItemLinkViewHolder extends DirectItemViewHolder {
private final LayoutDmLinkBinding binding;
public DirectItemLinkViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
final LayoutDmLinkBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
final int width = windowWidth - margin - dmRadiusSmall;
final ViewGroup.LayoutParams layoutParams = binding.preview.getLayoutParams();
layoutParams.width = width;
binding.preview.requestLayout();
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem item, final MessageDirection messageDirection) {
final DirectItemLink link = item.getLink();
final DirectItemLinkContext linkContext = link.getLinkContext();
final String linkImageUrl = linkContext.getLinkImageUrl();
if (TextUtils.isEmpty(linkImageUrl)) {
binding.preview.setVisibility(View.GONE);
} else {
binding.preview.setVisibility(View.VISIBLE);
binding.preview.setImageURI(linkImageUrl);
}
if (TextUtils.isEmpty(linkContext.getLinkTitle())) {
binding.title.setVisibility(View.GONE);
} else {
binding.title.setVisibility(View.VISIBLE);
binding.title.setText(linkContext.getLinkTitle());
}
if (TextUtils.isEmpty(linkContext.getLinkSummary())) {
binding.summary.setVisibility(View.GONE);
} else {
binding.summary.setVisibility(View.VISIBLE);
binding.summary.setText(linkContext.getLinkSummary());
}
if (TextUtils.isEmpty(linkContext.getLinkUrl())) {
binding.url.setVisibility(View.GONE);
} else {
binding.url.setVisibility(View.VISIBLE);
binding.url.setText(linkContext.getLinkUrl());
}
binding.text.setText(link.getText());
setupListeners(linkContext);
}
private void setupListeners(final DirectItemLinkContext linkContext) {
setupRamboTextListeners(binding.text);
final View.OnClickListener onClickListener = v -> openURL(linkContext.getLinkUrl());
binding.preview.setOnClickListener(onClickListener);
// binding.preview.setOnLongClickListener(v -> itemView.performLongClick());
binding.title.setOnClickListener(onClickListener);
binding.summary.setOnClickListener(onClickListener);
binding.url.setOnClickListener(onClickListener);
}
@Override
protected boolean showBackground() {
return true;
}
}

View File

@ -0,0 +1,180 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.Pair;
import androidx.recyclerview.widget.ItemTouchHelper;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.RoundingParams;
import java.util.Objects;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmMediaShareBinding;
import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.Caption;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemClip;
import awais.instagrabber.repositories.responses.directmessages.DirectItemFelixShare;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
public class DirectItemMediaShareViewHolder extends DirectItemViewHolder {
private static final String TAG = DirectItemMediaShareViewHolder.class.getSimpleName();
private final LayoutDmMediaShareBinding binding;
private final RoundingParams incomingRoundingParams;
private final RoundingParams outgoingRoundingParams;
private DirectItemType itemType;
public DirectItemMediaShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmMediaShareBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
incomingRoundingParams = RoundingParams.fromCornersRadii(dmRadiusSmall, dmRadius, dmRadius, dmRadius);
outgoingRoundingParams = RoundingParams.fromCornersRadii(dmRadius, dmRadiusSmall, dmRadius, dmRadius);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem item, final MessageDirection messageDirection) {
binding.topBg.setBackgroundResource(messageDirection == MessageDirection.INCOMING
? R.drawable.bg_media_share_top_incoming
: R.drawable.bg_media_share_top_outgoing);
Media media = getMedia(item);
if (media == null) return;
itemView.post(() -> {
setupUser(media);
setupTitle(media);
setupCaption(media);
});
itemView.post(() -> {
final MediaItemType mediaType = media.getMediaType();
setupTypeIndicator(mediaType);
if (mediaType == MediaItemType.MEDIA_TYPE_SLIDER) {
setupPreview(media.getCarouselMedia().get(0), messageDirection);
return;
}
setupPreview(media, messageDirection);
});
itemView.setOnClickListener(v -> openMedia(media));
}
private void setupTypeIndicator(final MediaItemType mediaType) {
final boolean showTypeIcon = mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER;
if (!showTypeIcon) {
binding.typeIcon.setVisibility(View.GONE);
} else {
binding.typeIcon.setVisibility(View.VISIBLE);
binding.typeIcon.setImageResource(mediaType == MediaItemType.MEDIA_TYPE_VIDEO
? R.drawable.ic_video_24
: R.drawable.ic_checkbox_multiple_blank_stroke);
}
}
private void setupPreview(@NonNull final Media media,
final MessageDirection messageDirection) {
final String url = ResponseBodyUtils.getThumbUrl(media.getImageVersions2());
if (Objects.equals(url, binding.mediaPreview.getTag())) {
return;
}
final RoundingParams roundingParams = messageDirection == MessageDirection.INCOMING ? incomingRoundingParams : outgoingRoundingParams;
binding.mediaPreview.setHierarchy(new GenericDraweeHierarchyBuilder(itemView.getResources())
.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP)
.setRoundingParams(roundingParams)
.build());
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
media.getOriginalHeight(),
media.getOriginalWidth(),
mediaImageMaxHeight,
mediaImageMaxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.mediaPreview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.mediaPreview.requestLayout();
binding.mediaPreview.setTag(url);
binding.mediaPreview.setImageURI(url);
}
private void setupCaption(@NonNull final Media media) {
final Caption caption = media.getCaption();
if (caption != null) {
binding.caption.setVisibility(View.VISIBLE);
binding.caption.setText(caption.getText());
binding.caption.setEllipsize(TextUtils.TruncateAt.END);
binding.caption.setMaxLines(2);
} else {
binding.caption.setVisibility(View.GONE);
}
}
private void setupTitle(@NonNull final Media media) {
final String title = media.getTitle();
if (!TextUtils.isEmpty(title)) {
binding.title.setVisibility(View.VISIBLE);
binding.title.setText(title);
} else {
binding.title.setVisibility(View.GONE);
}
}
private void setupUser(@NonNull final Media media) {
final User user = media.getUser();
if (user != null) {
binding.username.setVisibility(View.VISIBLE);
binding.profilePic.setVisibility(View.VISIBLE);
binding.username.setText(user.getUsername());
binding.profilePic.setImageURI(user.getProfilePicUrl());
} else {
binding.username.setVisibility(View.GONE);
binding.profilePic.setVisibility(View.GONE);
}
}
@Nullable
private Media getMedia(@NonNull final DirectItem item) {
Media media = null;
itemType = item.getItemType();
if (itemType == DirectItemType.MEDIA_SHARE) {
media = item.getMediaShare();
} else if (itemType == DirectItemType.CLIP) {
final DirectItemClip clip = item.getClip();
if (clip == null) return null;
media = clip.getClip();
} else if (itemType == DirectItemType.FELIX_SHARE) {
final DirectItemFelixShare felixShare = item.getFelixShare();
if (felixShare == null) return null;
media = felixShare.getVideo();
}
return media;
}
@Override
protected int getReactionsTranslationY() {
return reactionTranslationYType2;
}
@Override
public int getSwipeDirection() {
if (itemType != null && (itemType == DirectItemType.CLIP || itemType == DirectItemType.FELIX_SHARE)) {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
return super.getSwipeDirection();
}
}

View File

@ -0,0 +1,75 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.RoundingParams;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmMediaBinding;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
public class DirectItemMediaViewHolder extends DirectItemViewHolder {
private final LayoutDmMediaBinding binding;
private final RoundingParams incomingRoundingParams;
private final RoundingParams outgoingRoundingParams;
public DirectItemMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmMediaBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
incomingRoundingParams = RoundingParams.fromCornersRadii(dmRadiusSmall, dmRadius, dmRadius, dmRadius);
outgoingRoundingParams = RoundingParams.fromCornersRadii(dmRadius, dmRadiusSmall, dmRadius, dmRadius);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final RoundingParams roundingParams = messageDirection == MessageDirection.INCOMING ? incomingRoundingParams : outgoingRoundingParams;
binding.mediaPreview.setHierarchy(new GenericDraweeHierarchyBuilder(itemView.getResources())
.setRoundingParams(roundingParams)
.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP)
.build());
final Media media = directItemModel.getMedia();
itemView.setOnClickListener(v -> openMedia(media));
final MediaItemType modelMediaType = media.getMediaType();
binding.typeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO || modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER
? View.VISIBLE
: View.GONE);
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
media.getOriginalHeight(),
media.getOriginalWidth(),
mediaImageMaxHeight,
mediaImageMaxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.mediaPreview.getLayoutParams();
final int width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.width = width;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.mediaPreview.requestLayout();
binding.bgTime.getLayoutParams().width = width;
binding.bgTime.requestLayout();
final ImageVersions2 imageVersions2 = media.getImageVersions2();
if (imageVersions2 == null) return;
final String thumbUrl = ResponseBodyUtils.getThumbUrl(imageVersions2);
binding.mediaPreview.setImageURI(thumbUrl);
}
}

View File

@ -0,0 +1,47 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
public class DirectItemPlaceholderViewHolder extends DirectItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectItemPlaceholderViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
final LayoutDmTextBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final String text = String.format("%s: %s", directItemModel.getPlaceholder().getTitle(), directItemModel.getPlaceholder().getMessage());
binding.tvMessage.setText(text);
}
@Override
protected boolean showBackground() {
return true;
}
@Override
protected boolean allowLongClick() {
return false;
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,126 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.res.Resources;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.RoundingParams;
import com.facebook.drawee.view.SimpleDraweeView;
import com.google.common.collect.ImmutableList;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmProfileBinding;
import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
public class DirectItemProfileViewHolder extends DirectItemViewHolder {
private final LayoutDmProfileBinding binding;
private final ImmutableList<SimpleDraweeView> previewViews;
public DirectItemProfileViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmProfileBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
previewViews = ImmutableList.of(
binding.preview1,
binding.preview2,
binding.preview3,
binding.preview4,
binding.preview5,
binding.preview6
);
final Resources resources = itemView.getResources();
binding.preview4.setHierarchy(new GenericDraweeHierarchyBuilder(resources)
.setRoundingParams(RoundingParams.fromCornersRadii(0, 0, 0, dmRadius))
.build());
binding.preview6.setHierarchy(new GenericDraweeHierarchyBuilder(resources)
.setRoundingParams(RoundingParams.fromCornersRadii(0, 0, dmRadius, 0))
.build());
}
@Override
public void bindItem(@NonNull final DirectItem item,
final MessageDirection messageDirection) {
binding.getRoot().setBackgroundResource(messageDirection == MessageDirection.INCOMING
? R.drawable.bg_speech_bubble_incoming
: R.drawable.bg_speech_bubble_outgoing);
if (item.getItemType() == DirectItemType.PROFILE) {
setProfile(item);
} else if (item.getItemType() == DirectItemType.LOCATION) {
setLocation(item);
} else {
return;
}
for (final SimpleDraweeView previewView : previewViews) {
previewView.setImageURI((String) null);
}
final List<Media> previewMedias = item.getPreviewMedias();
if (previewMedias.size() <= 0) {
binding.firstRow.setVisibility(View.GONE);
binding.secondRow.setVisibility(View.GONE);
return;
}
if (previewMedias.size() <= 3) {
binding.firstRow.setVisibility(View.VISIBLE);
binding.secondRow.setVisibility(View.GONE);
}
for (int i = 0; i < previewMedias.size(); i++) {
final Media previewMedia = previewMedias.get(i);
if (previewMedia == null) continue;
final ImageVersions2 imageVersions2 = previewMedia.getImageVersions2();
final String url = ResponseBodyUtils.getThumbUrl(imageVersions2);
if (url == null) continue;
previewViews.get(i).setImageURI(url);
}
}
private void setProfile(@NonNull final DirectItem item) {
final User profile = item.getProfile();
if (profile == null) return;
binding.profilePic.setImageURI(profile.getProfilePicUrl());
binding.username.setText(profile.getUsername());
binding.fullName.setText(profile.getFullName());
binding.isVerified.setVisibility(profile.isVerified() ? View.VISIBLE : View.GONE);
itemView.setOnClickListener(v -> openProfile(profile.getUsername()));
}
private void setLocation(@NonNull final DirectItem item) {
final Location location = item.getLocation();
if (location == null) return;
binding.profilePic.setVisibility(View.GONE);
binding.username.setText(location.getName());
final String address = location.getAddress();
if (!TextUtils.isEmpty(address)) {
binding.fullName.setText(address);
binding.fullName.setVisibility(View.VISIBLE);
} else {
binding.fullName.setVisibility(View.GONE);
}
binding.isVerified.setVisibility(View.GONE);
itemView.setOnClickListener(v -> openLocation(location.getPk()));
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,192 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.RoundingParams;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmRavenMediaBinding;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.enums.RavenMediaViewMode;
import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemVisualMedia;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder {
private final LayoutDmRavenMediaBinding binding;
private final int maxWidth;
public DirectItemRavenMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmRavenMediaBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
maxWidth = windowWidth - margin - dmRadiusSmall;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final DirectItemVisualMedia visualMedia = directItemModel.getVisualMedia();
final Media media = visualMedia.getMedia();
if (media == null) return;
setExpiryInfo(visualMedia);
setPreview(visualMedia, messageDirection);
final boolean expired = media.getPk() == null;
if (expired) return;
itemView.setOnClickListener(v -> openMedia(media));
/*final boolean isExpired = visualMedia == null || (mediaModel = visualMedia.getMedia()) == null ||
TextUtils.isEmpty(mediaModel.getThumbUrl()) && mediaModel.getPk() < 1;
RavenExpiringMediaActionSummary mediaActionSummary = null;
if (visualMedia != null) {
mediaActionSummary = visualMedia.getExpiringMediaActionSummary();
}
binding.mediaExpiredIcon.setVisibility(isExpired ? View.VISIBLE : View.GONE);
int textRes = R.string.dms_inbox_raven_media_unknown;
if (isExpired) textRes = R.string.dms_inbox_raven_media_expired;
if (!isExpired) {
if (mediaActionSummary != null) {
final ActionType expiringMediaType = mediaActionSummary.getType();
if (expiringMediaType == ActionType.DELIVERED)
textRes = R.string.dms_inbox_raven_media_delivered;
else if (expiringMediaType == ActionType.SENT)
textRes = R.string.dms_inbox_raven_media_sent;
else if (expiringMediaType == ActionType.OPENED)
textRes = R.string.dms_inbox_raven_media_opened;
else if (expiringMediaType == ActionType.REPLAYED)
textRes = R.string.dms_inbox_raven_media_replayed;
else if (expiringMediaType == ActionType.SENDING)
textRes = R.string.dms_inbox_raven_media_sending;
else if (expiringMediaType == ActionType.BLOCKED)
textRes = R.string.dms_inbox_raven_media_blocked;
else if (expiringMediaType == ActionType.SUGGESTED)
textRes = R.string.dms_inbox_raven_media_suggested;
else if (expiringMediaType == ActionType.SCREENSHOT)
textRes = R.string.dms_inbox_raven_media_screenshot;
else if (expiringMediaType == ActionType.CANNOT_DELIVER)
textRes = R.string.dms_inbox_raven_media_cant_deliver;
}
final RavenMediaViewMode ravenMediaViewMode = visualMedia.getViewType();
if (ravenMediaViewMode == RavenMediaViewMode.PERMANENT || ravenMediaViewMode == RavenMediaViewMode.REPLAYABLE) {
final MediaItemType mediaType = mediaModel.getMediaType();
textRes = -1;
binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER
? View.VISIBLE
: View.GONE);
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
mediaModel.getHeight(),
mediaModel.getWidth(),
maxHeight,
maxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.ivMediaPreview.requestLayout();
binding.ivMediaPreview.setImageURI(mediaModel.getThumbUrl());
}
}
if (textRes != -1) {
binding.tvMessage.setText(context.getText(textRes));
binding.tvMessage.setVisibility(View.VISIBLE);
}*/
}
private void setExpiryInfo(final DirectItemVisualMedia visualMedia) {
final Media media = visualMedia.getMedia();
final RavenMediaViewMode viewMode = visualMedia.getViewMode();
if (viewMode != RavenMediaViewMode.PERMANENT) {
final MediaItemType mediaType = media.getMediaType();
final boolean expired = media.getPk() == null;
final String info;
switch (mediaType) {
case MEDIA_TYPE_IMAGE:
if (expired) {
info = "Image has expired";
break;
}
info = "Image will expire when seen";
break;
case MEDIA_TYPE_VIDEO:
if (expired) {
info = "Video has expired";
break;
}
info = "Video will expire when seen";
break;
default:
if (expired) {
info = "Message has expired";
break;
}
info = "Message will expire when seen";
break;
}
binding.expiryInfo.setVisibility(View.VISIBLE);
binding.expiryInfo.setText(info);
return;
}
binding.expiryInfo.setVisibility(View.GONE);
}
private void setPreview(final DirectItemVisualMedia visualMedia,
final MessageDirection messageDirection) {
final Media media = visualMedia.getMedia();
final boolean expired = media.getPk() == null;
if (expired) {
binding.preview.setVisibility(View.GONE);
binding.typeIcon.setVisibility(View.GONE);
return;
}
final RoundingParams roundingParams = messageDirection == MessageDirection.INCOMING
? RoundingParams.fromCornersRadii(dmRadiusSmall, dmRadius, dmRadius, dmRadius)
: RoundingParams.fromCornersRadii(dmRadius, dmRadiusSmall, dmRadius, dmRadius);
binding.preview.setHierarchy(new GenericDraweeHierarchyBuilder(itemView.getResources())
.setRoundingParams(roundingParams)
.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP)
.build());
final MediaItemType modelMediaType = media.getMediaType();
binding.typeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO || modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER
? View.VISIBLE
: View.GONE);
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
media.getOriginalHeight(),
media.getOriginalWidth(),
mediaImageMaxHeight,
maxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.preview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.preview.requestLayout();
final ImageVersions2 imageVersions2 = media.getImageVersions2();
if (imageVersions2 == null) return;
final String thumbUrl = ResponseBodyUtils.getThumbUrl(imageVersions2);
binding.preview.setImageURI(thumbUrl);
}
@Override
protected boolean allowLongClick() {
return false; // disabling until confirmed
}
}

View File

@ -0,0 +1,175 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.Gravity;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.RoundingParams;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmReelShareBinding;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemReelShare;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
public class DirectItemReelShareViewHolder extends DirectItemViewHolder {
private final LayoutDmReelShareBinding binding;
public DirectItemReelShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmReelShareBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem item, final MessageDirection messageDirection) {
final DirectItemReelShare reelShare = item.getReelShare();
final String type = reelShare.getType();
if (type == null) return;
final boolean isSelf = isSelf(item);
final Media media = reelShare.getMedia();
if (media == null) return;
final User user = media.getUser();
if (user == null) return;
final boolean expired = media.getMediaType() == null;
if (expired) {
binding.preview.setVisibility(View.GONE);
binding.typeIcon.setVisibility(View.GONE);
binding.quoteLine.setVisibility(View.GONE);
binding.reaction.setVisibility(View.GONE);
} else {
binding.preview.setVisibility(View.VISIBLE);
binding.typeIcon.setVisibility(View.VISIBLE);
binding.quoteLine.setVisibility(View.VISIBLE);
binding.reaction.setVisibility(View.VISIBLE);
}
setGravity(messageDirection, expired);
if (type.equals("reply")) {
setReply(messageDirection, reelShare, isSelf);
}
if (type.equals("reaction")) {
setReaction(messageDirection, reelShare, isSelf, expired);
}
if (type.equals("mention")) {
setMention(isSelf);
}
if (!expired) {
setPreview(media);
itemView.setOnClickListener(v -> openMedia(media));
}
}
private void setGravity(final MessageDirection messageDirection, final boolean expired) {
final boolean isIncoming = messageDirection == MessageDirection.INCOMING;
binding.shareInfo.setGravity(isIncoming ? Gravity.START : Gravity.END);
if (!expired) {
binding.quoteLine.setVisibility(isIncoming ? View.VISIBLE : View.GONE);
binding.quoteLineEnd.setVisibility(isIncoming ? View.GONE : View.VISIBLE);
}
final ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) binding.quoteLine.getLayoutParams();
layoutParams.horizontalBias = isIncoming ? 0 : 1;
final ConstraintLayout.LayoutParams messageLayoutParams = (ConstraintLayout.LayoutParams) binding.message.getLayoutParams();
messageLayoutParams.startToStart = isIncoming ? ConstraintLayout.LayoutParams.PARENT_ID : ConstraintLayout.LayoutParams.UNSET;
messageLayoutParams.endToEnd = isIncoming ? ConstraintLayout.LayoutParams.UNSET : ConstraintLayout.LayoutParams.PARENT_ID;
messageLayoutParams.setMarginStart(isIncoming ? messageInfoPaddingSmall : 0);
messageLayoutParams.setMarginEnd(isIncoming ? 0 : messageInfoPaddingSmall);
final ConstraintLayout.LayoutParams reactionLayoutParams = (ConstraintLayout.LayoutParams) binding.reaction.getLayoutParams();
final int previewId = binding.preview.getId();
if (isIncoming) {
reactionLayoutParams.startToEnd = previewId;
reactionLayoutParams.endToEnd = previewId;
reactionLayoutParams.startToStart = ConstraintLayout.LayoutParams.UNSET;
reactionLayoutParams.endToStart = ConstraintLayout.LayoutParams.UNSET;
} else {
reactionLayoutParams.startToStart = previewId;
reactionLayoutParams.endToStart = previewId;
reactionLayoutParams.startToEnd = ConstraintLayout.LayoutParams.UNSET;
reactionLayoutParams.endToEnd = ConstraintLayout.LayoutParams.UNSET;
}
}
private void setReply(final MessageDirection messageDirection,
final DirectItemReelShare reelShare,
final boolean isSelf) {
final String info = isSelf ? "You replied to their story" : "They replied to your story";
binding.shareInfo.setText(info);
binding.reaction.setVisibility(View.GONE);
final String text = reelShare.getText();
if (TextUtils.isEmpty(text)) {
binding.message.setVisibility(View.GONE);
return;
}
setMessage(messageDirection, text);
}
private void setReaction(final MessageDirection messageDirection,
final DirectItemReelShare reelShare,
final boolean isSelf,
final boolean expired) {
final String info = isSelf ? "You reacted to their story" : "They reacted to your story";
binding.shareInfo.setText(info);
binding.message.setVisibility(View.GONE);
final String text = reelShare.getText();
if (TextUtils.isEmpty(text)) {
binding.reaction.setVisibility(View.GONE);
return;
}
if (expired) {
setMessage(messageDirection, text);
return;
}
binding.reaction.setVisibility(View.VISIBLE);
binding.reaction.setText(text);
}
private void setMention(final boolean isSelf) {
final String info = isSelf ? "You mentioned them in your story" : "Mentioned you in their story";
binding.shareInfo.setText(info);
binding.message.setVisibility(View.GONE);
binding.reaction.setVisibility(View.GONE);
}
private void setMessage(final MessageDirection messageDirection, final String text) {
binding.message.setVisibility(View.VISIBLE);
binding.message.setBackgroundResource(messageDirection == MessageDirection.INCOMING
? R.drawable.bg_speech_bubble_incoming
: R.drawable.bg_speech_bubble_outgoing);
binding.message.setText(text);
}
private void setPreview(final Media media) {
final MediaItemType mediaType = media.getMediaType();
if (mediaType == null) return;
binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER
? View.VISIBLE : View.GONE);
final RoundingParams roundingParams = RoundingParams.fromCornersRadii(dmRadiusSmall, dmRadiusSmall, dmRadiusSmall, dmRadiusSmall);
binding.preview.setHierarchy(new GenericDraweeHierarchyBuilder(itemView.getResources())
.setRoundingParams(roundingParams)
.build());
final ImageVersions2 imageVersions2 = media.getImageVersions2();
if (imageVersions2 == null) return;
final String thumbUrl = ResponseBodyUtils.getThumbUrl(imageVersions2);
binding.preview.setImageURI(thumbUrl);
}
@Override
protected boolean canForward() {
return false;
}
}

View File

@ -0,0 +1,119 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import androidx.recyclerview.widget.ItemTouchHelper;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.RoundingParams;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmStoryShareBinding;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
public class DirectItemStoryShareViewHolder extends DirectItemViewHolder {
private final LayoutDmStoryShareBinding binding;
public DirectItemStoryShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmStoryShareBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem item, final MessageDirection messageDirection) {
String format = "@%s's story";
final String reelType = item.getStoryShare().getReelType();
if (reelType == null || item.getStoryShare().getMedia() == null) {
setExpiredStoryInfo(item);
return;
}
if (reelType.equals("highlight_reel")) {
format = "@%s's story highlight";
}
final User user = item.getStoryShare().getMedia().getUser();
final String info = String.format(format, user != null ? user.getUsername() : "");
binding.shareInfo.setText(info);
binding.text.setVisibility(View.GONE);
binding.ivMediaPreview.setController(null);
final DirectItemStoryShare storyShare = item.getStoryShare();
if (storyShare == null) return;
setText(storyShare);
final Media media = storyShare.getMedia();
setupPreview(messageDirection, media);
itemView.setOnClickListener(v -> openStory(storyShare));
}
private void setupPreview(final MessageDirection messageDirection, final Media storyShareMedia) {
final MediaItemType mediaType = storyShareMedia.getMediaType();
binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO ? View.VISIBLE : View.GONE);
final RoundingParams roundingParams = messageDirection == MessageDirection.INCOMING
? RoundingParams.fromCornersRadii(dmRadiusSmall, dmRadius, dmRadius, dmRadius)
: RoundingParams.fromCornersRadii(dmRadius, dmRadiusSmall, dmRadius, dmRadius);
binding.ivMediaPreview.setHierarchy(new GenericDraweeHierarchyBuilder(itemView.getResources())
.setRoundingParams(roundingParams)
.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP)
.build());
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
storyShareMedia.getOriginalHeight(),
storyShareMedia.getOriginalWidth(),
mediaImageMaxHeight,
mediaImageMaxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.ivMediaPreview.requestLayout();
final ImageVersions2 imageVersions2 = storyShareMedia.getImageVersions2();
if (imageVersions2 == null) return;
final String thumbUrl = ResponseBodyUtils.getThumbUrl(imageVersions2);
binding.ivMediaPreview.setImageURI(thumbUrl);
}
private void setText(final DirectItemStoryShare storyShare) {
final String text = storyShare.getText();
if (!TextUtils.isEmpty(text)) {
binding.text.setText(text);
binding.text.setVisibility(View.VISIBLE);
return;
}
binding.text.setVisibility(View.GONE);
}
private void setExpiredStoryInfo(final DirectItem item) {
binding.shareInfo.setText(item.getStoryShare().getTitle());
binding.text.setVisibility(View.VISIBLE);
binding.text.setText(item.getStoryShare().getMessage());
binding.ivMediaPreview.setVisibility(View.GONE);
binding.typeIcon.setVisibility(View.GONE);
}
@Override
protected boolean canForward() {
return false;
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,38 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import androidx.annotation.NonNull;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
public class DirectItemTextViewHolder extends DirectItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectItemTextViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmTextBinding binding,
final User currentUser,
final DirectThread thread,
@NonNull final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final String text = directItemModel.getText();
if (text == null) return;
binding.tvMessage.setText(text);
setupRamboTextListeners(binding.tvMessage);
}
@Override
protected boolean showBackground() {
return true;
}
}

View File

@ -0,0 +1,90 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.ClickableSpan;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmActionLogBinding;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemActionLog;
import awais.instagrabber.repositories.responses.directmessages.DirectItemVideoCallEvent;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.TextUtils;
public class DirectItemVideoCallEventViewHolder extends DirectItemViewHolder {
private final LayoutDmActionLogBinding binding;
public DirectItemVideoCallEventViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
final LayoutDmActionLogBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final DirectItemVideoCallEvent videoCallEvent = directItemModel.getVideoCallEvent();
final String text = videoCallEvent.getDescription();
final SpannableStringBuilder sb = new SpannableStringBuilder(text);
final List<DirectItemActionLog.TextRange> textAttributes = videoCallEvent.getTextAttributes();
if (textAttributes != null && !textAttributes.isEmpty()) {
for (final DirectItemActionLog.TextRange textAttribute : textAttributes) {
if (!TextUtils.isEmpty(textAttribute.getColor())) {
final ForegroundColorSpan colorSpan = new ForegroundColorSpan(itemView.getResources().getColor(R.color.deep_orange_400));
sb.setSpan(colorSpan, textAttribute.getStart(), textAttribute.getEnd(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
if (!TextUtils.isEmpty(textAttribute.getIntent())) {
final ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(@NonNull final View widget) {
}
};
sb.setSpan(clickableSpan, textAttribute.getStart(), textAttribute.getEnd(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
}
}
binding.tvMessage.setMaxLines(1);
binding.tvMessage.setText(sb);
}
@Override
protected boolean allowMessageDirectionGravity() {
return false;
}
@Override
protected boolean showUserDetailsInGroup() {
return false;
}
@Override
protected boolean showMessageInfo() {
return false;
}
@Override
protected boolean allowLongClick() {
return false;
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -0,0 +1,592 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.annotation.SuppressLint;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.text.format.DateFormat;
import android.view.Gravity;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewPropertyAnimator;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.widget.ImageViewCompat;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import androidx.transition.TransitionManager;
import com.google.android.material.transition.MaterialFade;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.stream.Collectors;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemInternalLongClickListener;
import awais.instagrabber.customviews.DirectItemContextMenu;
import awais.instagrabber.customviews.DirectItemFrameLayout;
import awais.instagrabber.customviews.RamboTextViewV2;
import awais.instagrabber.customviews.helpers.SwipeAndRestoreItemTouchHelperCallback.SwipeableViewHolder;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;
import awais.instagrabber.repositories.responses.directmessages.DirectItemReactions;
import awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.DeepLinkParser;
import awais.instagrabber.utils.ResponseBodyUtils;
public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder implements SwipeableViewHolder {
private static final String TAG = DirectItemViewHolder.class.getSimpleName();
// private static final List<Integer> THREAD_CHANGING_OPTIONS = ImmutableList.of(R.id.unsend);
private final LayoutDmBaseBinding binding;
private final User currentUser;
private final DirectThread thread;
private final int groupMessageWidth;
private final List<Long> userIds;
private final DirectItemCallback callback;
private final int reactionAdjustMargin;
private final AccelerateDecelerateInterpolator accelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator();
protected final int margin;
protected final int dmRadius;
protected final int dmRadiusSmall;
protected final int messageInfoPaddingSmall;
protected final int mediaImageMaxHeight;
protected final int windowWidth;
protected final int mediaImageMaxWidth;
protected final int reactionTranslationYType1;
protected final int reactionTranslationYType2;
private boolean selected = false;
private DirectItemInternalLongClickListener longClickListener;
private DirectItem item;
private ViewPropertyAnimator shrinkGrowAnimator;
private MessageDirection messageDirection;
// private View.OnLayoutChangeListener layoutChangeListener;
public DirectItemViewHolder(@NonNull final LayoutDmBaseBinding binding,
@NonNull final User currentUser,
@NonNull final DirectThread thread,
@NonNull final DirectItemCallback callback) {
super(binding.getRoot());
this.binding = binding;
this.currentUser = currentUser;
this.thread = thread;
this.callback = callback;
userIds = thread.getUsers()
.stream()
.map(User::getPk)
.collect(Collectors.toList());
binding.ivProfilePic.setVisibility(thread.isGroup() ? View.VISIBLE : View.GONE);
binding.ivProfilePic.setOnClickListener(null);
final Resources resources = itemView.getResources();
margin = resources.getDimensionPixelSize(R.dimen.dm_message_item_margin);
final int avatarSize = resources.getDimensionPixelSize(R.dimen.dm_message_item_avatar_size);
dmRadius = resources.getDimensionPixelSize(R.dimen.dm_message_card_radius);
dmRadiusSmall = resources.getDimensionPixelSize(R.dimen.dm_message_card_radius_small);
messageInfoPaddingSmall = resources.getDimensionPixelSize(R.dimen.dm_message_info_padding_small);
windowWidth = resources.getDisplayMetrics().widthPixels;
mediaImageMaxHeight = resources.getDimensionPixelSize(R.dimen.dm_media_img_max_height);
reactionAdjustMargin = resources.getDimensionPixelSize(R.dimen.dm_reaction_adjust_margin);
final int groupWidthCorrection = avatarSize + messageInfoPaddingSmall * 3;
mediaImageMaxWidth = windowWidth - margin - (thread.isGroup() ? groupWidthCorrection : messageInfoPaddingSmall * 2);
// messageInfoPaddingSmall is used cuz it's also 4dp, 1 avatar margin + 2 paddings = 3
groupMessageWidth = windowWidth - margin - groupWidthCorrection;
reactionTranslationYType1 = resources.getDimensionPixelSize(R.dimen.dm_reaction_translation_y_type_1);
reactionTranslationYType2 = resources.getDimensionPixelSize(R.dimen.dm_reaction_translation_y_type_2);
}
public void bind(final int position, final DirectItem item) {
this.item = item;
messageDirection = isSelf(item) ? MessageDirection.OUTGOING : MessageDirection.INCOMING;
// Asynchronous binding causes some weird behaviour
// itemView.post(() -> bindBase(item, messageDirection, position));
// itemView.post(() -> bindItem(item, messageDirection));
// itemView.post(() -> setupLongClickListener(position, messageDirection));
bindBase(item, messageDirection, position);
bindItem(item, messageDirection);
setupLongClickListener(position, messageDirection);
}
private void bindBase(final DirectItem item, final MessageDirection messageDirection, final int position) {
final FrameLayout.LayoutParams containerLayoutParams = (FrameLayout.LayoutParams) binding.container.getLayoutParams();
final DirectItemType itemType = item.getItemType();
setMessageDirectionGravity(messageDirection, containerLayoutParams);
setGroupUserDetails(item, messageDirection);
setBackground(messageDirection);
setMessageInfo(item, messageDirection);
if (itemType == DirectItemType.REEL_SHARE) {
containerLayoutParams.setMarginStart(0);
containerLayoutParams.setMarginEnd(0);
}
if (itemType == DirectItemType.TEXT || itemType == DirectItemType.LINK) {
binding.messageInfo.setPadding(0, 0, dmRadius, dmRadiusSmall);
} else {
if (showMessageInfo()) {
binding.messageInfo.setPadding(0, 0, messageInfoPaddingSmall, dmRadiusSmall);
}
}
setupReply(item, messageDirection);
setReactions(item, position);
if (item.getRepliedToMessage() == null && item.showForwardAttribution()) {
setForwardInfo(messageDirection);
}
}
private void setBackground(final MessageDirection messageDirection) {
if (showBackground()) {
binding.background.setBackgroundResource(messageDirection == MessageDirection.INCOMING ? R.drawable.bg_speech_bubble_incoming
: R.drawable.bg_speech_bubble_outgoing);
return;
}
binding.background.setBackgroundResource(0);
}
private void setGroupUserDetails(final DirectItem item, final MessageDirection messageDirection) {
if (showUserDetailsInGroup()) {
binding.ivProfilePic.setVisibility(messageDirection == MessageDirection.INCOMING && thread.isGroup() ? View.VISIBLE : View.GONE);
binding.tvUsername.setVisibility(messageDirection == MessageDirection.INCOMING && thread.isGroup() ? View.VISIBLE : View.GONE);
if (messageDirection == MessageDirection.INCOMING && thread.isGroup()) {
final User user = getUser(item.getUserId(), thread.getUsers());
if (user != null) {
binding.tvUsername.setText(user.getUsername());
binding.ivProfilePic.setImageURI(user.getProfilePicUrl());
}
ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) binding.chatMessageLayout.getLayoutParams();
layoutParams.matchConstraintMaxWidth = groupMessageWidth;
binding.chatMessageLayout.setLayoutParams(layoutParams);
}
return;
}
binding.ivProfilePic.setVisibility(View.GONE);
binding.tvUsername.setVisibility(View.GONE);
}
private void setMessageDirectionGravity(final MessageDirection messageDirection,
final FrameLayout.LayoutParams containerLayoutParams) {
if (allowMessageDirectionGravity()) {
containerLayoutParams.setMarginStart(messageDirection == MessageDirection.OUTGOING ? margin : 0);
containerLayoutParams.setMarginEnd(messageDirection == MessageDirection.INCOMING ? margin : 0);
containerLayoutParams.gravity = messageDirection == MessageDirection.INCOMING ? Gravity.START : Gravity.END;
return;
}
containerLayoutParams.gravity = Gravity.CENTER;
}
private void setMessageInfo(final DirectItem item, final MessageDirection messageDirection) {
if (showMessageInfo()) {
binding.messageInfo.setVisibility(View.VISIBLE);
binding.deliveryStatus.setVisibility(messageDirection == MessageDirection.OUTGOING ? View.VISIBLE : View.GONE);
binding.messageTime.setText(DateFormat.getTimeFormat(itemView.getContext()).format(item.getDate()));
if (messageDirection == MessageDirection.OUTGOING) {
if (item.isPending()) {
binding.deliveryStatus.setImageResource(R.drawable.ic_check_24);
} else {
final boolean read = ResponseBodyUtils.isRead(item, thread.getLastSeenAt(), userIds, null);
binding.deliveryStatus.setImageResource(R.drawable.ic_check_all_24);
ImageViewCompat.setImageTintList(
binding.deliveryStatus,
ColorStateList.valueOf(itemView.getResources().getColor(read ? R.color.blue_500 : R.color.grey_500))
);
}
}
return;
}
binding.messageInfo.setVisibility(View.GONE);
}
private void setupReply(final DirectItem item, final MessageDirection messageDirection) {
if (item.getRepliedToMessage() != null) {
setReply(item, messageDirection, thread.getUsers());
} else {
binding.quoteLine.setVisibility(View.GONE);
binding.replyContainer.setVisibility(View.GONE);
binding.replyInfo.setVisibility(View.GONE);
}
}
private void setReply(final DirectItem item,
final MessageDirection messageDirection,
final List<User> users) {
final DirectItem replied = item.getRepliedToMessage();
final DirectItemType itemType = replied.getItemType();
String text = null;
String url = null;
switch (itemType) {
case TEXT:
text = replied.getText();
break;
case LINK:
text = replied.getLink().getText();
break;
case PLACEHOLDER:
text = replied.getPlaceholder().getMessage();
break;
case MEDIA:
url = ResponseBodyUtils.getThumbUrl(replied.getMedia().getImageVersions2());
break;
case RAVEN_MEDIA:
url = ResponseBodyUtils.getThumbUrl(replied.getVisualMedia().getMedia().getImageVersions2());
break;
case VOICE_MEDIA:
text = "Voice message";
break;
case MEDIA_SHARE:
Media mediaShare = replied.getMediaShare();
if (mediaShare.getMediaType() == MediaItemType.MEDIA_TYPE_SLIDER) {
mediaShare = mediaShare.getCarouselMedia().get(0);
}
url = ResponseBodyUtils.getThumbUrl(mediaShare.getImageVersions2());
break;
case REEL_SHARE:
text = replied.getReelShare().getText();
break;
// Below types cannot be replied to
// case LIKE:
// text = "❤️";
// break;
// case PROFILE:
// text = "@" + replied.getProfile().getUsername();
// break;
// case CLIP:
// url = ResponseBodyUtils.getThumbUrl(replied.getClip().getClip().getImageVersions2());
// break;
// case FELIX_SHARE:
// url = ResponseBodyUtils.getThumbUrl(replied.getFelixShare().getVideo().getImageVersions2());
// break;
// case STORY_SHARE:
// final DirectItemMedia media = replied.getStoryShare().getMedia();
// if (media == null) break;
// url = ResponseBodyUtils.getThumbUrl(media.getImageVersions2());
// break;
// case LOCATION
}
if (text == null && url == null) {
binding.quoteLine.setVisibility(View.GONE);
binding.replyContainer.setVisibility(View.GONE);
binding.replyInfo.setVisibility(View.GONE);
return;
}
setReplyGravity(messageDirection);
final String info = setReplyInfo(item, replied, users);
binding.replyInfo.setVisibility(View.VISIBLE);
binding.replyInfo.setText(info);
binding.quoteLine.setVisibility(View.VISIBLE);
binding.replyContainer.setVisibility(View.VISIBLE);
if (url != null) {
binding.replyText.setVisibility(View.GONE);
binding.replyImage.setVisibility(View.VISIBLE);
binding.replyImage.setImageURI(url);
return;
}
binding.replyImage.setVisibility(View.GONE);
final Drawable background = binding.replyText.getBackground().mutate();
final Resources resources = itemView.getResources();
background.setTint(replied.getUserId() != currentUser.getPk()
? resources.getColor(R.color.grey_600)
: resources.getColor(R.color.deep_purple_400));
binding.replyText.setBackgroundDrawable(background);
binding.replyText.setVisibility(View.VISIBLE);
binding.replyText.setText(text);
}
private String setReplyInfo(final DirectItem item,
final DirectItem replied,
final List<User> users) {
final long repliedToUserId = replied.getUserId();
if (repliedToUserId == item.getUserId() && item.getUserId() == currentUser.getPk()) {
// User replied to own message
return "You replied to yourself";
}
if (repliedToUserId == item.getUserId()) {
// opposite user replied to their own message
return "Replied to themself";
}
final User user = getUser(repliedToUserId, users);
final String repliedToUsername = user != null ? user.getUsername() : "";
if (item.getUserId() == currentUser.getPk()) {
return !thread.isGroup() ? "You replied" : String.format("You replied to %s", repliedToUsername);
}
if (repliedToUserId == currentUser.getPk()) {
return "Replied to you";
}
return String.format("Replied to %s", repliedToUsername);
}
private void setForwardInfo(final MessageDirection direction) {
binding.replyInfo.setVisibility(View.VISIBLE);
binding.replyInfo.setText(direction == MessageDirection.OUTGOING ? "You forwarded a message" : "Forwarded a message");
}
private void setReplyGravity(final MessageDirection messageDirection) {
final boolean isIncoming = messageDirection == MessageDirection.INCOMING;
final ConstraintLayout.LayoutParams quoteLineLayoutParams = (ConstraintLayout.LayoutParams) binding.quoteLine.getLayoutParams();
final ConstraintLayout.LayoutParams replyContainerLayoutParams = (ConstraintLayout.LayoutParams) binding.replyContainer.getLayoutParams();
final ConstraintLayout.LayoutParams replyInfoLayoutParams = (ConstraintLayout.LayoutParams) binding.replyInfo.getLayoutParams();
final int profilePicId = binding.ivProfilePic.getId();
final int replyContainerId = binding.replyContainer.getId();
final int quoteLineId = binding.quoteLine.getId();
quoteLineLayoutParams.startToEnd = isIncoming ? profilePicId : replyContainerId;
quoteLineLayoutParams.endToStart = isIncoming ? replyContainerId : ConstraintLayout.LayoutParams.UNSET;
quoteLineLayoutParams.endToEnd = isIncoming ? ConstraintLayout.LayoutParams.UNSET : ConstraintLayout.LayoutParams.PARENT_ID;
replyContainerLayoutParams.startToEnd = isIncoming ? quoteLineId : profilePicId;
replyContainerLayoutParams.endToEnd = isIncoming ? ConstraintLayout.LayoutParams.PARENT_ID : ConstraintLayout.LayoutParams.UNSET;
replyContainerLayoutParams.endToStart = isIncoming ? ConstraintLayout.LayoutParams.UNSET : quoteLineId;
replyInfoLayoutParams.startToEnd = isIncoming ? quoteLineId : ConstraintLayout.LayoutParams.UNSET;
replyInfoLayoutParams.endToStart = isIncoming ? ConstraintLayout.LayoutParams.UNSET : quoteLineId;
}
private void setReactions(final DirectItem item, final int position) {
binding.getRoot().post(() -> {
MaterialFade materialFade = new MaterialFade();
materialFade.addTarget(binding.emojis);
TransitionManager.beginDelayedTransition(binding.getRoot(), materialFade);
final DirectItemReactions reactions = item.getReactions();
final List<DirectItemEmojiReaction> emojis = reactions != null ? reactions.getEmojis() : null;
if (emojis == null || emojis.isEmpty()) {
binding.container.setPadding(messageInfoPaddingSmall, messageInfoPaddingSmall, messageInfoPaddingSmall, messageInfoPaddingSmall);
binding.reactionsWrapper.setVisibility(View.GONE);
return;
}
binding.reactionsWrapper.setVisibility(View.VISIBLE);
binding.reactionsWrapper.setTranslationY(getReactionsTranslationY());
binding.container.setPadding(messageInfoPaddingSmall, messageInfoPaddingSmall, messageInfoPaddingSmall, reactionAdjustMargin);
binding.emojis.setEmojis(emojis.stream()
.map(DirectItemEmojiReaction::getEmoji)
.collect(Collectors.toList()));
// binding.emojis.setEmojis(ImmutableList.of("😣",
// "😖",
// "😫",
// "😩",
// "🥺",
// "😢",
// "😭",
// "😤",
// "😠",
// "😡",
// "🤬"));
binding.emojis.setOnClickListener(v -> callback.onReactionClick(item, position));
// final List<DirectUser> reactedUsers = emojis.stream()
// .map(DirectItemEmojiReaction::getSenderId)
// .distinct()
// .map(userId -> getUser(userId, users))
// .collect(Collectors.toList());
// for (final DirectUser user : reactedUsers) {
// if (user == null) continue;
// final ProfilePicView profilePicView = new ProfilePicView(itemView.getContext());
// profilePicView.setSize(ProfilePicView.Size.TINY);
// profilePicView.setImageURI(user.getProfilePicUrl());
// binding.reactions.addView(profilePicView);
// }
});
}
protected boolean isSelf(final DirectItem directItem) {
return directItem.getUserId() == currentUser.getPk();
}
public void setItemView(final View view) {
this.binding.message.addView(view);
}
public abstract void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection);
@Nullable
protected User getUser(final long userId, final List<User> users) {
if (userId == currentUser.getPk()) {
return currentUser;
}
if (users == null) return null;
for (final User user : users) {
if (userId != user.getPk()) continue;
return user;
}
return null;
}
protected boolean allowMessageDirectionGravity() {
return true;
}
protected boolean showUserDetailsInGroup() {
return true;
}
protected boolean showBackground() {
return false;
}
protected boolean showMessageInfo() {
return true;
}
protected boolean allowLongClick() {
return true;
}
protected boolean allowReaction() {
return true;
}
protected boolean canForward() {
return true;
}
protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {
return null;
}
protected int getReactionsTranslationY() {
return reactionTranslationYType1;
}
@CallSuper
public void cleanup() {
// if (layoutChangeListener != null) {
// binding.container.removeOnLayoutChangeListener(layoutChangeListener);
// }
}
protected void setupRamboTextListeners(@NonNull final RamboTextViewV2 textView) {
textView.addOnHashtagListener(autoLinkItem -> callback.onHashtagClick(autoLinkItem.getOriginalText().trim()));
textView.addOnMentionClickListener(autoLinkItem -> openProfile(autoLinkItem.getOriginalText().trim()));
textView.addOnEmailClickListener(autoLinkItem -> callback.onEmailClick(autoLinkItem.getOriginalText().trim()));
textView.addOnURLClickListener(autoLinkItem -> openURL(autoLinkItem.getOriginalText().trim()));
}
protected void openProfile(final String username) {
callback.onMentionClick(username);
}
protected void openLocation(final long locationId) {
callback.onLocationClick(locationId);
}
protected void openURL(final String url) {
callback.onURLClick(url);
}
protected void openMedia(final Media media) {
callback.onMediaClick(media);
}
protected void openStory(final DirectItemStoryShare storyShare) {
callback.onStoryClick(storyShare);
}
protected void handleDeepLink(final String deepLinkText) {
if (deepLinkText == null) return;
final DeepLinkParser.DeepLink deepLink = DeepLinkParser.parse(deepLinkText);
if (deepLink == null) return;
switch (deepLink.getType()) {
case USER:
callback.onMentionClick(deepLink.getValue());
break;
}
}
@SuppressLint("ClickableViewAccessibility")
private void setupLongClickListener(final int position, final MessageDirection messageDirection) {
if (!allowLongClick()) return;
binding.getRoot().setOnItemLongClickListener(new DirectItemFrameLayout.OnItemLongClickListener() {
@Override
public void onLongClickStart(final View view) {
itemView.post(() -> shrink());
}
@Override
public void onLongClickCancel(final View view) {
itemView.post(() -> grow());
}
@Override
public void onLongClick(final View view, final float x, final float y) {
// if (longClickListener == null) return false;
// longClickListener.onLongClick(position, this);
itemView.post(() -> grow());
setSelected(true);
showLongClickOptions(new Point((int) x, (int) y), messageDirection);
}
});
}
private void showLongClickOptions(final Point location, final MessageDirection messageDirection) {
final List<DirectItemContextMenu.MenuItem> longClickOptions = getLongClickOptions();
final ImmutableList.Builder<DirectItemContextMenu.MenuItem> builder = ImmutableList.builder();
if (longClickOptions != null) {
builder.addAll(longClickOptions);
}
if (canForward()) {
builder.add(new DirectItemContextMenu.MenuItem(R.id.forward, R.string.forward));
}
if (thread.getInputMode() != 1 && messageDirection == MessageDirection.OUTGOING) {
builder.add(new DirectItemContextMenu.MenuItem(R.id.unsend, R.string.dms_inbox_unsend));
}
final boolean showReactions = thread.getInputMode() != 1 && allowReaction();
final ImmutableList<DirectItemContextMenu.MenuItem> menuItems = builder.build();
if (!showReactions && menuItems.isEmpty()) return;
final DirectItemContextMenu menu = new DirectItemContextMenu(itemView.getContext(), showReactions, menuItems);
menu.setOnDismissListener(() -> setSelected(false));
menu.setOnReactionClickListener(emoji -> callback.onReaction(item, emoji));
menu.setOnOptionSelectListener(itemId -> callback.onOptionSelect(item, itemId));
menu.show(itemView, location);
}
public void setLongClickListener(final DirectItemInternalLongClickListener longClickListener) {
this.longClickListener = longClickListener;
}
public void setSelected(final boolean selected) {
this.selected = selected;
}
private void shrink() {
if (shrinkGrowAnimator != null) {
shrinkGrowAnimator.cancel();
}
shrinkGrowAnimator = itemView.animate()
.scaleX(0.8f)
.scaleY(0.8f)
.setInterpolator(accelerateDecelerateInterpolator)
.setDuration(ViewConfiguration.getLongPressTimeout() - ViewConfiguration.getTapTimeout());
shrinkGrowAnimator.start();
}
private void grow() {
if (shrinkGrowAnimator != null) {
shrinkGrowAnimator.cancel();
}
shrinkGrowAnimator = itemView.animate()
.scaleX(1f)
.scaleY(1f)
.setInterpolator(accelerateDecelerateInterpolator)
.setDuration(200)
.withEndAction(() -> shrinkGrowAnimator = null);
shrinkGrowAnimator.start();
}
@Override
public int getSwipeDirection() {
if (item == null || messageDirection == null) return ItemTouchHelper.ACTION_STATE_IDLE;
return messageDirection == MessageDirection.OUTGOING ? ItemTouchHelper.START : ItemTouchHelper.END;
}
public enum MessageDirection {
INCOMING,
OUTGOING
}
}

View File

@ -0,0 +1,190 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import com.google.android.exoplayer2.ExoPlaybackException;
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;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.common.primitives.Floats;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmVoiceMediaBinding;
import awais.instagrabber.repositories.responses.Audio;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemVoiceMedia;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.TextUtils;
import static com.google.android.exoplayer2.C.TIME_UNSET;
public class DirectItemVoiceMediaViewHolder extends DirectItemViewHolder {
private static final String TAG = "DirectItemVoiceMediaVH";
private final LayoutDmVoiceMediaBinding binding;
private final DefaultDataSourceFactory dataSourceFactory;
private SimpleExoPlayer player;
private Handler handler;
private Runnable positionChecker;
private Player.EventListener listener;
public DirectItemVoiceMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmVoiceMediaBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
this.dataSourceFactory = new DefaultDataSourceFactory(binding.getRoot().getContext(), "instagram");
setItemView(binding.getRoot());
binding.voiceMedia.getLayoutParams().width = mediaImageMaxWidth;
}
@Override
public void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection) {
final DirectItemVoiceMedia voiceMedia = directItemModel.getVoiceMedia();
if (voiceMedia == null) return;
final Media media = voiceMedia.getMedia();
if (media == null) return;
final Audio audio = media.getAudio();
if (audio == null) return;
final List<Float> waveformData = audio.getWaveformData();
binding.waveformSeekBar.setSample(Floats.toArray(waveformData));
binding.waveformSeekBar.setEnabled(false);
final String text = String.format("%s/%s", TextUtils.millisToTimeString(0), TextUtils.millisToTimeString(audio.getDuration()));
binding.duration.setText(text);
final AudioItemState audioItemState = new AudioItemState();
player = new SimpleExoPlayer.Builder(itemView.getContext()).build();
player.setVolume(1);
player.setPlayWhenReady(true);
player.setRepeatMode(Player.REPEAT_MODE_OFF);
handler = new Handler();
final long initialDelay = 0;
final long recurringDelay = 60;
positionChecker = new Runnable() {
@Override
public void run() {
if (handler != null) {
handler.removeCallbacks(this);
}
if (player == null) return;
final long currentPosition = player.getCurrentPosition();
final long duration = player.getDuration();
// Log.d(TAG, "currentPosition: " + currentPosition + ", duration: " + duration);
if (duration == TIME_UNSET) return;
// final float progress = ((float) currentPosition / duration /* * 100 */);
final int progress = (int) ((float) currentPosition / duration * 100);
// Log.d(TAG, "progress: " + progress);
final String text = String.format("%s/%s", TextUtils.millisToTimeString(currentPosition), TextUtils.millisToTimeString(duration));
binding.duration.setText(text);
binding.waveformSeekBar.setProgress(progress);
if (handler != null) {
handler.postDelayed(this, recurringDelay);
}
}
};
player.addListener(listener = new Player.EventListener() {
@Override
public void onPlaybackStateChanged(final int state) {
if (!audioItemState.isPrepared() && state == Player.STATE_READY) {
binding.playPause.setIconResource(R.drawable.ic_round_pause_24);
audioItemState.setPrepared(true);
binding.playPause.setVisibility(View.VISIBLE);
binding.progressBar.setVisibility(View.GONE);
if (handler != null) {
handler.postDelayed(positionChecker, initialDelay);
}
return;
}
if (state == Player.STATE_ENDED) {
// binding.waveformSeekBar.setProgressInPercentage(0);
binding.waveformSeekBar.setProgress(0);
binding.playPause.setIconResource(R.drawable.ic_round_play_arrow_24);
if (handler != null) {
handler.removeCallbacks(positionChecker);
}
}
}
@Override
public void onPlayerError(final ExoPlaybackException error) {
Log.e(TAG, "onPlayerError: ", error);
}
});
final ProgressiveMediaSource.Factory sourceFactory = new ProgressiveMediaSource.Factory(dataSourceFactory);
final MediaItem mediaItem = MediaItem.fromUri(audio.getAudioSrc());
final ProgressiveMediaSource mediaSource = sourceFactory.createMediaSource(mediaItem);
player.setMediaSource(mediaSource);
binding.playPause.setOnClickListener(v -> {
if (player == null) return;
if (!audioItemState.isPrepared()) {
player.prepare();
binding.playPause.setVisibility(View.GONE);
binding.progressBar.setVisibility(View.VISIBLE);
return;
}
if (player.isPlaying()) {
binding.playPause.setIconResource(R.drawable.ic_round_play_arrow_24);
player.pause();
return;
}
binding.playPause.setIconResource(R.drawable.ic_round_pause_24);
if (player.getPlaybackState() == Player.STATE_ENDED) {
player.seekTo(0);
if (handler != null) {
handler.postDelayed(positionChecker, initialDelay);
}
}
binding.waveformSeekBar.setEnabled(true);
player.play();
});
}
@Override
public void cleanup() {
super.cleanup();
if (handler != null && positionChecker != null) {
handler.removeCallbacks(positionChecker);
handler = null;
positionChecker = null;
}
if (player != null) {
player.release();
if (listener != null) {
player.removeListener(listener);
}
player = null;
}
}
@Override
protected boolean canForward() {
return false;
}
private static class AudioItemState {
private boolean prepared;
private AudioItemState() {}
public boolean isPrepared() {
return prepared;
}
public void setPrepared(final boolean prepared) {
this.prepared = prepared;
}
}
}

View File

@ -1,32 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.core.text.HtmlCompat;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT;
public class DirectMessageActionLogViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectMessageActionLogViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmTextBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final String text = directItemModel.getActionLogModel().getDescription();
binding.tvMessage.setText(HtmlCompat.fromHtml("<small>" + text + "</small>", FROM_HTML_MODE_COMPACT));
binding.tvMessage.setVisibility(View.VISIBLE);
}
}

View File

@ -1,57 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
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 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 = Utils.displayMetrics.widthPixels - Utils.convertDpToPx(64) - getItemMargin();
setItemView(binding.getRoot());
setupForAnimatedMedia();
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final DirectItemModel.DirectItemAnimatedMediaModel animatedMediaModel = directItemModel.getAnimatedMediaModel();
final String url = animatedMediaModel.getWebpUrl();
final Pair<Integer, Integer> 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();
binding.ivAnimatedMessage.setController(Fresco.newDraweeControllerBuilder()
.setUri(url)
.setAutoPlayAnimations(true)
.build());
}
}

View File

@ -1,30 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.Context;
import android.view.View;
import androidx.annotation.NonNull;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
public class DirectMessageDefaultViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectMessageDefaultViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmTextBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final Context context = itemView.getContext();
binding.tvMessage.setText(context.getText(R.string.dms_inbox_raven_message_unknown));
}
}

View File

@ -1,100 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public abstract class DirectMessageItemViewHolder extends RecyclerView.ViewHolder {
private static final int MESSAGE_INCOMING = 69;
private static final int MESSAGE_OUTGOING = 420;
private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel(
CookieUtils.getUserIdFromCookie(Utils.settingsHelper.getString(Constants.COOKIE)));
private final LayoutDmBaseBinding binding;
private final int itemMargin;
public DirectMessageItemViewHolder(@NonNull final LayoutDmBaseBinding binding, @NonNull final View.OnClickListener onClickListener) {
super(binding.getRoot());
this.binding = binding;
binding.ivProfilePic.setOnClickListener(onClickListener);
binding.messageCard.setOnClickListener(onClickListener);
// final String strDmYou = binding.getRoot().getContext().getString(R.string.direct_messages_you);
itemMargin = Utils.displayMetrics.widthPixels / 5;
}
public void bind(final DirectItemModel directItemModel, final List<ProfileModel> users, final List<ProfileModel> leftUsers) {
final ProfileModel user = getUser(directItemModel.getUserId(), users, leftUsers);
final int type = user == myProfileHolder ? MESSAGE_OUTGOING : MESSAGE_INCOMING;
final RecyclerView.LayoutParams itemViewLayoutParams = (RecyclerView.LayoutParams) itemView.getLayoutParams();
itemViewLayoutParams.setMargins(type == MESSAGE_OUTGOING ? itemMargin : 0, 0,
type == MESSAGE_INCOMING ? itemMargin : 0, 0);
final ViewGroup messageCardParent = (ViewGroup) binding.messageCard.getParent();
binding.contentContainer.setGravity(type == MESSAGE_INCOMING ? Gravity.START : Gravity.END);
CharSequence text = "?";
if (user != null && user != myProfileHolder) {
text = user.getUsername();
} else if (user == myProfileHolder) text = "";
text = (TextUtils.isEmpty(text) ? "" : text + " - ") + directItemModel.getDateTime();
binding.tvUsername.setText(text);
binding.tvUsername.setGravity(type == MESSAGE_INCOMING ? Gravity.START : Gravity.END);
binding.ivProfilePic.setVisibility(type == MESSAGE_INCOMING ? View.VISIBLE : View.GONE);
binding.ivProfilePic.setTag(user);
binding.likedContainer.setVisibility(directItemModel.isLiked() ? View.VISIBLE : View.GONE);
messageCardParent.setTag(directItemModel);
binding.messageCard.setTag(directItemModel);
if (type == MESSAGE_INCOMING && user != null) {
binding.ivProfilePic.setImageURI(user.getSdProfilePic());
}
bindItem(directItemModel);
}
public void setItemView(final View view) {
this.binding.messageCard.addView(view);
}
public int getItemMargin() {
return itemMargin;
}
public abstract void bindItem(final DirectItemModel directItemModel);
@Nullable
private ProfileModel getUser(final long userId, final List<ProfileModel> users, final List<ProfileModel> leftUsers) {
if (users != null) {
ProfileModel result = myProfileHolder;
for (final ProfileModel user : users) {
if (Long.toString(userId).equals(user.getId())) result = user;
}
if (leftUsers != null)
for (final ProfileModel leftUser : leftUsers) {
if (Long.toString(userId).equals(leftUser.getId())) result = leftUser;
}
return result;
}
return null;
}
protected void setupForAnimatedMedia() {
binding.messageCard.setCardElevation(0);
binding.messageCard.setCardBackgroundColor(itemView.getResources().getColor(android.R.color.transparent));
}
}

View File

@ -1,46 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import androidx.annotation.NonNull;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmLinkBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.utils.TextUtils;
public class DirectMessageLinkViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmLinkBinding binding;
public DirectMessageLinkViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmLinkBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final DirectItemModel.DirectItemLinkModel link = directItemModel.getLinkModel();
final DirectItemModel.DirectItemLinkContext linkContext = link.getLinkContext();
final String linkImageUrl = linkContext.getLinkImageUrl();
if (TextUtils.isEmpty(linkImageUrl)) {
binding.ivLinkPreview.setVisibility(View.GONE);
} else {
binding.ivLinkPreview.setImageURI(linkImageUrl);
}
if (TextUtils.isEmpty(linkContext.getLinkTitle())) {
binding.tvLinkTitle.setVisibility(View.GONE);
} else {
binding.tvLinkTitle.setText(linkContext.getLinkTitle());
}
if (TextUtils.isEmpty(linkContext.getLinkSummary())) {
binding.tvLinkSummary.setVisibility(View.GONE);
} else {
binding.tvLinkSummary.setText(linkContext.getLinkSummary());
}
binding.tvMessage.setText(TextUtils.getSpannableUrl(link.getText()));
}
}

View File

@ -1,64 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.text.HtmlCompat;
import androidx.core.util.Pair;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmMediaShareBinding;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemMediaModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.Utils;
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT;
public class DirectMessageMediaShareViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmMediaShareBinding binding;
private final int maxHeight;
private final int maxWidth;
public DirectMessageMediaShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmMediaShareBinding 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 * 0.8);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final Context context = itemView.getContext();
final DirectItemMediaModel mediaModel = directItemModel.getMediaModel();
final ProfileModel modelUser = mediaModel.getUser();
if (modelUser != null) {
binding.tvMessage.setText(HtmlCompat.fromHtml(
"<small>" + context.getString(R.string.dms_inbox_media_shared_from, modelUser.getUsername()) + "</small>",
FROM_HTML_MODE_COMPACT));
}
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
mediaModel.getHeight(),
mediaModel.getWidth(),
maxHeight,
maxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
binding.ivMediaPreview.requestLayout();
binding.ivMediaPreview.setImageURI(mediaModel.getThumbUrl());
final MediaItemType modelMediaType = mediaModel.getMediaType();
binding.typeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO
|| modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
}
}

View File

@ -1,52 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmMediaBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.Utils;
public class DirectMessageMediaViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmMediaBinding binding;
private final int maxHeight;
private final int maxWidth;
public DirectMessageMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmMediaBinding 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());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final DirectItemModel.DirectItemMediaModel mediaModel = directItemModel.getMediaModel();
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
mediaModel.getHeight(),
mediaModel.getWidth(),
maxHeight,
maxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.ivMediaPreview.requestLayout();
binding.ivMediaPreview.setImageURI(mediaModel.getThumbUrl());
final MediaItemType modelMediaType = mediaModel.getMediaType();
binding.typeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO || modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER
? View.VISIBLE
: View.GONE);
}
}

View File

@ -1,30 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.core.text.HtmlCompat;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT;
public class DirectMessagePlaceholderViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectMessagePlaceholderViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmTextBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
binding.tvMessage.setText(HtmlCompat.fromHtml(directItemModel.getText().toString(), FROM_HTML_MODE_COMPACT));
}
}

View File

@ -1,35 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import androidx.annotation.NonNull;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmProfileBinding;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.direct_messages.DirectItemModel;
public class DirectMessageProfileViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmProfileBinding binding;
public DirectMessageProfileViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmProfileBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
binding.btnOpenProfile.setOnClickListener(onClickListener);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final ProfileModel profileModel = directItemModel.getProfileModel();
if (profileModel == null) return;
binding.profileInfo.setImageURI(profileModel.getSdProfilePic());
binding.btnOpenProfile.setTag(profileModel);
binding.tvFullName.setText(profileModel.getName());
binding.profileInfoText.setText(profileModel.getUsername());
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
}
}

View File

@ -1,104 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmRavenMediaBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.enums.RavenExpiringMediaType;
import awais.instagrabber.models.enums.RavenMediaViewType;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public class DirectMessageRavenMediaViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmRavenMediaBinding binding;
private final int maxHeight;
private final int maxWidth;
public DirectMessageRavenMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmRavenMediaBinding 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 * 0.8);
binding.tvMessage.setVisibility(View.GONE);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final Context context = itemView.getContext();
final DirectItemModel.DirectItemRavenMediaModel ravenMediaModel = directItemModel.getRavenMediaModel();
DirectItemModel.DirectItemMediaModel mediaModel = directItemModel.getMediaModel();
final boolean isExpired = ravenMediaModel == null || (mediaModel = ravenMediaModel.getMedia()) == null ||
TextUtils.isEmpty(mediaModel.getThumbUrl()) && mediaModel.getPk() < 1;
DirectItemModel.RavenExpiringMediaActionSummaryModel mediaActionSummary = null;
if (ravenMediaModel != null) {
mediaActionSummary = ravenMediaModel.getExpiringMediaActionSummary();
}
binding.mediaExpiredIcon.setVisibility(isExpired ? View.VISIBLE : View.GONE);
int textRes = R.string.dms_inbox_raven_media_unknown;
if (isExpired) textRes = R.string.dms_inbox_raven_media_expired;
if (!isExpired) {
if (mediaActionSummary != null) {
final RavenExpiringMediaType expiringMediaType = mediaActionSummary.getType();
if (expiringMediaType == RavenExpiringMediaType.RAVEN_DELIVERED)
textRes = R.string.dms_inbox_raven_media_delivered;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SENT)
textRes = R.string.dms_inbox_raven_media_sent;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_OPENED)
textRes = R.string.dms_inbox_raven_media_opened;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_REPLAYED)
textRes = R.string.dms_inbox_raven_media_replayed;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SENDING)
textRes = R.string.dms_inbox_raven_media_sending;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_BLOCKED)
textRes = R.string.dms_inbox_raven_media_blocked;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SUGGESTED)
textRes = R.string.dms_inbox_raven_media_suggested;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SCREENSHOT)
textRes = R.string.dms_inbox_raven_media_screenshot;
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_CANNOT_DELIVER)
textRes = R.string.dms_inbox_raven_media_cant_deliver;
}
final RavenMediaViewType ravenMediaViewType = ravenMediaModel.getViewType();
if (ravenMediaViewType == RavenMediaViewType.PERMANENT || ravenMediaViewType == RavenMediaViewType.REPLAYABLE) {
final MediaItemType mediaType = mediaModel.getMediaType();
textRes = -1;
binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER
? View.VISIBLE
: View.GONE);
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
mediaModel.getHeight(),
mediaModel.getWidth(),
maxHeight,
maxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.ivMediaPreview.requestLayout();
binding.ivMediaPreview.setImageURI(mediaModel.getThumbUrl());
}
}
if (textRes != -1) {
binding.tvMessage.setText(context.getText(textRes));
binding.tvMessage.setVisibility(View.VISIBLE);
}
}
}

View File

@ -1,67 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmRavenMediaBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public class DirectMessageReelShareViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmRavenMediaBinding binding;
private final int maxHeight;
private final int maxWidth;
public DirectMessageReelShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmRavenMediaBinding binding,
final View.OnClickListener onClickListener,
final MentionClickListener mentionClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
maxHeight = itemView.getResources().getDimensionPixelSize(R.dimen.dm_media_img_max_height);
maxWidth = (int) (Utils.displayMetrics.widthPixels * 0.8);
binding.tvMessage.setMentionClickListener(mentionClickListener);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final DirectItemModel.DirectItemReelShareModel reelShare = directItemModel.getReelShare();
CharSequence text = reelShare.getText();
if (TextUtils.isEmpty(text)) {
binding.tvMessage.setVisibility(View.GONE);
} else {
if (TextUtils.hasMentions(text)) text = TextUtils.getMentionText(text); // for mentions
binding.tvMessage.setText(text);
}
final DirectItemModel.DirectItemMediaModel reelShareMedia = reelShare.getMedia();
final MediaItemType mediaType = reelShareMedia.getMediaType();
if (mediaType == null) {
binding.mediaExpiredIcon.setVisibility(View.VISIBLE);
} else {
binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO ||
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
reelShareMedia.getHeight(),
reelShareMedia.getWidth(),
maxHeight,
maxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.ivMediaPreview.requestLayout();
binding.ivMediaPreview.setImageURI(reelShareMedia.getThumbUrl());
}
}
}

View File

@ -1,67 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.text.HtmlCompat;
import androidx.core.util.Pair;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmStoryShareBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT;
public class DirectMessageStoryShareViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmStoryShareBinding binding;
private final int maxHeight;
private final int maxWidth;
public DirectMessageStoryShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmStoryShareBinding 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 * 0.8);
binding.tvMessage.setVisibility(View.GONE);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final DirectItemModel.DirectItemReelShareModel reelShare = directItemModel.getReelShare();
if (reelShare == null) {
binding.tvMessage.setText(HtmlCompat.fromHtml(directItemModel.getText().toString(), FROM_HTML_MODE_COMPACT));
binding.tvMessage.setVisibility(View.VISIBLE);
} else {
final String text = reelShare.getText();
if (!TextUtils.isEmpty(text)) {
binding.tvMessage.setText(text);
binding.tvMessage.setVisibility(View.VISIBLE);
} else {
final DirectItemModel.DirectItemMediaModel reelShareMedia = reelShare.getMedia();
final MediaItemType mediaType = reelShareMedia.getMediaType();
binding.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO ? View.VISIBLE : View.GONE);
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
reelShareMedia.getHeight(),
reelShareMedia.getWidth(),
maxHeight,
maxWidth
);
final ViewGroup.LayoutParams layoutParams = binding.ivMediaPreview.getLayoutParams();
layoutParams.width = widthHeight.first != null ? widthHeight.first : 0;
layoutParams.height = widthHeight.second != null ? widthHeight.second : 0;
binding.ivMediaPreview.requestLayout();
binding.ivMediaPreview.setImageURI(reelShareMedia.getThumbUrl());
}
}
}
}

View File

@ -1,43 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.Context;
import android.text.Spanned;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.utils.TextUtils;
public class DirectMessageTextViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectMessageTextViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmTextBinding binding,
final View.OnClickListener onClickListener,
final MentionClickListener mentionClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
this.binding.tvMessage.setMentionClickListener(mentionClickListener);
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final Context context = itemView.getContext();
CharSequence text = directItemModel.getText();
text = TextUtils.getSpannableUrl(text.toString()); // for urls
if (TextUtils.hasMentions(text)) text = TextUtils.getMentionText(text); // for mentions
if (text instanceof Spanned)
binding.tvMessage.setText(text, TextView.BufferType.SPANNABLE);
else if (text == "") {
binding.tvMessage.setText(context.getText(R.string.dms_inbox_raven_message_unknown));
} else binding.tvMessage.setText(text);
}
}

View File

@ -1,29 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import androidx.annotation.NonNull;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
public class DirectMessageVideoCallEventViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmTextBinding binding;
public DirectMessageVideoCallEventViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmTextBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
// todo add call event info
binding.tvMessage.setVisibility(View.VISIBLE);
binding.tvMessage.setBackgroundColor(0xFF_1F90E6);
}
}

View File

@ -1,77 +0,0 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmVoiceMediaBinding;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.utils.NumberUtils;
public class DirectMessageVoiceMediaViewHolder extends DirectMessageItemViewHolder {
private final LayoutDmVoiceMediaBinding binding;
private DirectItemModel.DirectItemVoiceMediaModel prevVoiceModel;
private ImageView prevPlayIcon;
public DirectMessageVoiceMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmVoiceMediaBinding binding,
final View.OnClickListener onClickListener) {
super(baseBinding, onClickListener);
this.binding = binding;
// todo pause / resume
// todo release prev audio, start new voice
binding.btnPlayVoice.setOnClickListener(v -> {
final Object tag = v.getTag();
final ImageView playIcon = (ImageView) ((ViewGroup) v).getChildAt(0);
final DirectItemModel.DirectItemVoiceMediaModel voiceMediaModel = (DirectItemModel.DirectItemVoiceMediaModel) tag;
final boolean voicePlaying = voiceMediaModel.isPlaying();
voiceMediaModel.setPlaying(!voicePlaying);
if (voiceMediaModel == prevVoiceModel) {
// todo pause / resume
} else {
// todo release prev audio, start new voice
if (prevVoiceModel != null) prevVoiceModel.setPlaying(false);
if (prevPlayIcon != null)
prevPlayIcon.setImageResource(android.R.drawable.ic_media_play);
}
if (voicePlaying) {
playIcon.setImageResource(android.R.drawable.ic_media_play);
} else {
playIcon.setImageResource(android.R.drawable.ic_media_pause);
}
prevVoiceModel = voiceMediaModel;
prevPlayIcon = playIcon;
});
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItemModel directItemModel) {
final DirectItemModel.DirectItemVoiceMediaModel voiceMediaModel = directItemModel.getVoiceMediaModel();
if (voiceMediaModel != null) {
final int[] waveformData = voiceMediaModel.getWaveformData();
if (waveformData != null) binding.waveformSeekBar.setSample(waveformData);
final long durationMs = voiceMediaModel.getDurationMs();
binding.tvVoiceDuration.setText(NumberUtils.millisToString(durationMs));
binding.waveformSeekBar.setProgress(voiceMediaModel.getProgress());
binding.waveformSeekBar.setProgressChangeListener((waveformSeekBar, progress, fromUser) -> {
// todo progress audio player
voiceMediaModel.setProgress(progress);
if (fromUser)
binding.tvVoiceDuration.setText(NumberUtils.millisToString(durationMs * progress / 100));
});
binding.btnPlayVoice.setTag(voiceMediaModel);
} else {
binding.waveformSeekBar.setProgress(0);
}
}
}

View File

@ -0,0 +1,89 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.graphics.drawable.Drawable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUser;
import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUserCallback;
import awais.instagrabber.customviews.VerticalImageSpan;
import awais.instagrabber.databinding.LayoutDmPendingUserItemBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.Utils;
public class DirectPendingUserViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = DirectPendingUserViewHolder.class.getSimpleName();
private final LayoutDmPendingUserItemBinding binding;
private final PendingUserCallback callback;
private final int drawableSize;
private VerticalImageSpan verifiedSpan;
public DirectPendingUserViewHolder(@NonNull final LayoutDmPendingUserItemBinding binding,
final PendingUserCallback callback) {
super(binding.getRoot());
this.binding = binding;
this.callback = callback;
drawableSize = Utils.convertDpToPx(24);
}
public void bind(final int position, final PendingUser pendingUser) {
if (pendingUser == null) return;
binding.getRoot().setOnClickListener(v -> {
if (callback == null) return;
callback.onClick(position, pendingUser);
});
setUsername(pendingUser);
binding.requester.setText(itemView.getResources().getString(R.string.added_by, pendingUser.getRequester()));
binding.profilePic.setImageURI(pendingUser.getUser().getProfilePicUrl());
if (pendingUser.isInProgress()) {
binding.approve.setVisibility(View.GONE);
binding.deny.setVisibility(View.GONE);
binding.progress.setVisibility(View.VISIBLE);
return;
}
binding.approve.setVisibility(View.VISIBLE);
binding.deny.setVisibility(View.VISIBLE);
binding.progress.setVisibility(View.GONE);
binding.approve.setOnClickListener(v -> {
if (callback == null) return;
callback.onApprove(position, pendingUser);
});
binding.deny.setOnClickListener(v -> {
if (callback == null) return;
callback.onDeny(position, pendingUser);
});
}
private void setUsername(final PendingUser pendingUser) {
final User user = pendingUser.getUser();
final SpannableStringBuilder sb = new SpannableStringBuilder(user.getUsername());
if (user.isVerified()) {
if (verifiedSpan == null) {
final Drawable verifiedDrawable = AppCompatResources.getDrawable(itemView.getContext(), R.drawable.verified);
if (verifiedDrawable != null) {
final Drawable drawable = verifiedDrawable.mutate();
drawable.setBounds(0, 0, drawableSize, drawableSize);
verifiedSpan = new VerticalImageSpan(drawable);
}
}
try {
if (verifiedSpan != null) {
sb.append(" ");
sb.setSpan(verifiedSpan, sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} catch (Exception e) {
Log.e(TAG, "bind: ", e);
}
}
binding.username.setText(sb);
}
}

View File

@ -0,0 +1,71 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectReactionsAdapter.OnReactionClickListener;
import awais.instagrabber.customviews.emoji.Emoji;
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;
import awais.instagrabber.utils.emoji.EmojiParser;
public class DirectReactionViewHolder extends RecyclerView.ViewHolder {
private final LayoutDmUserItemBinding binding;
private final long viewerId;
private final String itemId;
private final OnReactionClickListener onReactionClickListener;
private final EmojiParser emojiParser;
public DirectReactionViewHolder(final LayoutDmUserItemBinding binding,
final long viewerId,
final String itemId,
final OnReactionClickListener onReactionClickListener) {
super(binding.getRoot());
this.binding = binding;
this.viewerId = viewerId;
this.itemId = itemId;
this.onReactionClickListener = onReactionClickListener;
binding.info.setVisibility(View.GONE);
binding.secondaryImage.setVisibility(View.VISIBLE);
emojiParser = EmojiParser.getInstance();
}
public void bind(final DirectItemEmojiReaction reaction,
@Nullable final User user) {
itemView.setOnClickListener(v -> {
if (onReactionClickListener == null) return;
onReactionClickListener.onReactionClick(itemId, reaction);
});
setUser(user);
setReaction(reaction);
}
private void setReaction(final DirectItemEmojiReaction reaction) {
final Emoji emoji = emojiParser.getEmoji(reaction.getEmoji());
if (emoji == null) {
binding.secondaryImage.setImageDrawable(null);
return;
}
binding.secondaryImage.setImageDrawable(emoji.getDrawable());
}
private void setUser(final User user) {
if (user == null) {
binding.fullName.setText("");
binding.username.setText("");
binding.profilePic.setImageURI((String) null);
return;
}
binding.fullName.setText(user.getFullName());
if (user.getPk() == viewerId) {
binding.username.setText(R.string.tap_to_remove);
} else {
binding.username.setText(user.getUsername());
}
binding.profilePic.setImageURI(user.getProfilePicUrl());
}
}

View File

@ -0,0 +1,102 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.graphics.drawable.Drawable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectUsersAdapter.OnDirectUserClickListener;
import awais.instagrabber.adapters.DirectUsersAdapter.OnDirectUserLongClickListener;
import awais.instagrabber.customviews.VerticalImageSpan;
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.Utils;
public class DirectUserViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = DirectUserViewHolder.class.getSimpleName();
private final LayoutDmUserItemBinding binding;
private final OnDirectUserClickListener onClickListener;
private final OnDirectUserLongClickListener onLongClickListener;
private final int drawableSize;
private VerticalImageSpan verifiedSpan;
public DirectUserViewHolder(@NonNull final LayoutDmUserItemBinding binding,
final OnDirectUserClickListener onClickListener,
final OnDirectUserLongClickListener onLongClickListener) {
super(binding.getRoot());
this.binding = binding;
this.onClickListener = onClickListener;
this.onLongClickListener = onLongClickListener;
drawableSize = Utils.convertDpToPx(24);
}
public void bind(final int position,
final User user,
final boolean isAdmin,
final boolean isInviter,
final boolean showSelection,
final boolean isSelected) {
if (user == null) return;
binding.getRoot().setOnClickListener(v -> {
if (onClickListener == null) return;
onClickListener.onClick(position, user, isSelected);
});
binding.getRoot().setOnLongClickListener(v -> {
if (onLongClickListener == null) return false;
return onLongClickListener.onLongClick(position, user);
});
setFullName(user);
binding.username.setText(user.getUsername());
binding.profilePic.setImageURI(user.getProfilePicUrl());
setInfo(isAdmin, isInviter);
setSelection(showSelection, isSelected);
}
private void setFullName(final User user) {
final SpannableStringBuilder sb = new SpannableStringBuilder(user.getFullName());
if (user.isVerified()) {
if (verifiedSpan == null) {
final Drawable verifiedDrawable = AppCompatResources.getDrawable(itemView.getContext(), R.drawable.verified);
if (verifiedDrawable != null) {
final Drawable drawable = verifiedDrawable.mutate();
drawable.setBounds(0, 0, drawableSize, drawableSize);
verifiedSpan = new VerticalImageSpan(drawable);
}
}
try {
if (verifiedSpan != null) {
sb.append(" ");
sb.setSpan(verifiedSpan, sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} catch (Exception e) {
Log.e(TAG, "bind: ", e);
}
}
binding.fullName.setText(sb);
}
private void setInfo(final boolean isAdmin, final boolean isInviter) {
if (!isAdmin && !isInviter) {
binding.info.setVisibility(View.GONE);
return;
}
if (isAdmin) {
binding.info.setText(R.string.admin);
return;
}
binding.info.setText(R.string.inviter);
}
private void setSelection(final boolean showSelection, final boolean isSelected) {
binding.select.setVisibility(showSelection ? View.VISIBLE : View.GONE);
binding.getRoot().setSelected(isSelected);
}
}

View File

@ -0,0 +1,89 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.content.res.Resources;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.UserSearchResultsAdapter.OnRecipientClickListener;
import awais.instagrabber.databinding.LayoutDmUserItemBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
public class RecipientThreadViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = RecipientThreadViewHolder.class.getSimpleName();
private final LayoutDmUserItemBinding binding;
private final OnRecipientClickListener onThreadClickListener;
private final float translateAmount;
public RecipientThreadViewHolder(@NonNull final LayoutDmUserItemBinding binding,
final OnRecipientClickListener onThreadClickListener) {
super(binding.getRoot());
this.binding = binding;
this.onThreadClickListener = onThreadClickListener;
binding.info.setVisibility(View.GONE);
final Resources resources = itemView.getResources();
final int avatarSize = resources.getDimensionPixelSize(R.dimen.dm_inbox_avatar_size);
translateAmount = ((float) avatarSize) / 7;
}
public void bind(final int position,
final DirectThread thread,
final boolean showSelection,
final boolean isSelected) {
if (thread == null) return;
binding.getRoot().setOnClickListener(v -> {
if (onThreadClickListener == null) return;
onThreadClickListener.onClick(position, RankedRecipient.of(thread), isSelected);
});
binding.fullName.setText(thread.getThreadTitle());
setUsername(thread);
setProfilePic(thread);
setSelection(showSelection, isSelected);
}
private void setProfilePic(final DirectThread thread) {
final List<User> users = thread.getUsers();
binding.profilePic.setImageURI(users.get(0).getProfilePicUrl());
binding.profilePic.setScaleX(1);
binding.profilePic.setScaleY(1);
binding.profilePic.setTranslationX(0);
binding.profilePic.setTranslationY(0);
if (users.size() > 1) {
binding.profilePic2.setVisibility(View.VISIBLE);
binding.profilePic2.setImageURI(users.get(1).getProfilePicUrl());
binding.profilePic2.setTranslationX(translateAmount);
binding.profilePic2.setTranslationY(translateAmount);
final float scaleAmount = 0.75f;
binding.profilePic2.setScaleX(scaleAmount);
binding.profilePic2.setScaleY(scaleAmount);
binding.profilePic.setScaleX(scaleAmount);
binding.profilePic.setScaleY(scaleAmount);
binding.profilePic.setTranslationX(-translateAmount);
binding.profilePic.setTranslationY(-translateAmount);
return;
}
binding.profilePic2.setVisibility(View.GONE);
}
private void setUsername(final DirectThread thread) {
if (thread.isGroup()) {
binding.username.setVisibility(View.GONE);
return;
}
binding.username.setVisibility(View.VISIBLE);
// for a non-group thread, the thread title is the username so set the full name in the username text view
binding.username.setText(thread.getUsers().get(0).getFullName());
}
private void setSelection(final boolean showSelection, final boolean isSelected) {
binding.select.setVisibility(showSelection ? View.VISIBLE : View.GONE);
binding.getRoot().setSelected(isSelected);
}
}

View File

@ -11,9 +11,11 @@ import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.databinding.ItemFeedBottomBinding;
import awais.instagrabber.databinding.ItemFeedTopBinding;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.Caption;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.TextUtils;
import static android.text.TextUtils.TruncateAt.END;
@ -35,52 +37,60 @@ public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
this.feedItemCallback = feedItemCallback;
}
public void bind(final FeedModel feedModel) {
if (feedModel == null) {
public void bind(final Media media) {
if (media == null) {
return;
}
setupProfilePic(feedModel);
setupLocation(feedModel);
bottomBinding.tvPostDate.setText(feedModel.getPostDate());
setupComments(feedModel);
setupCaption(feedModel);
if (feedModel.getItemType() != MediaItemType.MEDIA_TYPE_SLIDER) {
bottomBinding.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(feedModel, -1));
setupProfilePic(media);
setupLocation(media);
bottomBinding.tvPostDate.setText(media.getDate());
setupComments(media);
setupCaption(media);
if (media.getMediaType() != MediaItemType.MEDIA_TYPE_SLIDER) {
bottomBinding.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(media, -1));
}
bindItem(feedModel);
bindItem(media);
}
private void setupComments(final FeedModel feedModel) {
final long commentsCount = feedModel.getCommentsCount();
private void setupComments(@NonNull final Media feedModel) {
final long commentsCount = feedModel.getCommentCount();
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 setupProfilePic(@NonNull final Media media) {
final User user = media.getUser();
if (user == null) {
topBinding.ivProfilePic.setVisibility(View.GONE);
topBinding.title.setVisibility(View.GONE);
return;
}
topBinding.ivProfilePic.setOnClickListener(v -> feedItemCallback.onProfilePicClick(media, topBinding.ivProfilePic));
topBinding.ivProfilePic.setImageURI(user.getProfilePicUrl());
setupTitle(media);
}
private void setupTitle(final FeedModel feedModel) {
private void setupTitle(@NonNull final Media media) {
// 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();
final User user = media.getUser();
final String title = "@" + user.getUsername();
topBinding.title.setText(title);
topBinding.title.setOnClickListener(v -> feedItemCallback.onNameClick(feedModel, topBinding.ivProfilePic));
topBinding.title.setOnClickListener(v -> feedItemCallback.onNameClick(media, topBinding.ivProfilePic));
}
private void setupCaption(final FeedModel feedModel) {
private void setupCaption(final Media media) {
bottomBinding.viewerCaption.clearOnMentionClickListeners();
bottomBinding.viewerCaption.clearOnHashtagClickListeners();
bottomBinding.viewerCaption.clearOnURLClickListeners();
bottomBinding.viewerCaption.clearOnEmailClickListeners();
final CharSequence postCaption = feedModel.getPostCaption();
final Caption caption = media.getCaption();
if (caption == null) {
bottomBinding.viewerCaption.setVisibility(View.GONE);
return;
}
final CharSequence postCaption = caption.getText();
final boolean captionEmpty = TextUtils.isEmpty(postCaption);
bottomBinding.viewerCaption.setVisibility(captionEmpty ? View.GONE : View.VISIBLE);
if (captionEmpty) return;
@ -103,8 +113,9 @@ public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
bottomBinding.viewerCaption.addOnURLClickListener(autoLinkItem -> feedItemCallback.onURLClick(autoLinkItem.getOriginalText()));
}
private void setupLocation(final FeedModel feedModel) {
final String locationName = feedModel.getLocationName();
private void setupLocation(@NonNull final Media media) {
final Location location = media.getLocation();
final String locationName = location.getName();
if (TextUtils.isEmpty(locationName)) {
topBinding.location.setVisibility(View.GONE);
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
@ -116,9 +127,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 -> feedItemCallback.onLocationClick(feedModel));
topBinding.location.setOnClickListener(v -> feedItemCallback.onLocationClick(media));
}
}
public abstract void bindItem(final FeedModel feedModel);
public abstract void bindItem(final Media media);
}

View File

@ -16,7 +16,8 @@ import com.facebook.imagepipeline.request.ImageRequestBuilder;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.databinding.ItemFeedPhotoBinding;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
public class FeedPhotoViewHolder extends FeedItemViewHolder {
@ -40,14 +41,12 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
}
@Override
public void bindItem(final FeedModel feedModel) {
if (feedModel == null) {
return;
}
public void bindItem(final Media media) {
if (media == null) return;
binding.getRoot().post(() -> {
setDimensions(feedModel);
final String thumbnailUrl = feedModel.getThumbnailUrl();
String url = feedModel.getDisplayUrl();
setDimensions(media);
final String thumbnailUrl = ResponseBodyUtils.getThumbUrl(media);
String url = ResponseBodyUtils.getImageUrl(media);
if (TextUtils.isEmpty(url)) url = thumbnailUrl;
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
// .setLocalThumbnailPreviewsEnabled(true)
@ -62,7 +61,7 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
@Override
public boolean onSingleTapConfirmed(final MotionEvent e) {
if (feedItemCallback != null) {
feedItemCallback.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.imageViewer);
feedItemCallback.onPostClick(media, binding.itemFeedTop.ivProfilePic, binding.imageViewer);
return true;
}
return false;
@ -71,8 +70,8 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
});
}
private void setDimensions(final FeedModel feedModel) {
final float aspectRatio = (float) feedModel.getImageWidth() / feedModel.getImageHeight();
private void setDimensions(final Media feedModel) {
final float aspectRatio = (float) feedModel.getOriginalWidth() / feedModel.getOriginalHeight();
binding.imageViewer.setAspectRatio(aspectRatio);
}
}

View File

@ -13,8 +13,7 @@ import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.adapters.SliderCallbackAdapter;
import awais.instagrabber.adapters.SliderItemsAdapter;
import awais.instagrabber.databinding.ItemFeedSliderBinding;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.PostChild;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.Utils;
@ -39,8 +38,8 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
}
@Override
public void bindItem(final FeedModel feedModel) {
final List<PostChild> sliderItems = feedModel.getSliderItems();
public void bindItem(final Media feedModel) {
final List<Media> sliderItems = feedModel.getCarouselMedia();
final int sliderItemLen = sliderItems != null ? sliderItems.size() : 0;
if (sliderItemLen <= 0) return;
final String text = "1/" + sliderItemLen;
@ -68,7 +67,7 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
adapter.submitList(sliderItems);
}
private void setDimensions(final View view, final PostChild model) {
private void setDimensions(final View view, final Media model) {
final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams();
int requiredWidth = layoutParams.width;
if (requiredWidth <= 0) {
@ -86,10 +85,10 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
setLayoutParamDimens(binding.mediaList, model);
}
private void setLayoutParamDimens(final View view, final PostChild model) {
private void setLayoutParamDimens(final View view, final Media model) {
final int requiredWidth = view.getMeasuredWidth();
final ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
final int spanHeight = NumberUtils.getResultingHeight(requiredWidth, model.getHeight(), model.getWidth());
final int spanHeight = NumberUtils.getResultingHeight(requiredWidth, model.getOriginalHeight(), model.getOriginalWidth());
layoutParams.height = spanHeight == 0 ? requiredWidth + 1 : spanHeight;
view.requestLayout();
}

View File

@ -12,13 +12,17 @@ import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import java.util.List;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.customviews.VideoPlayerCallbackAdapter;
import awais.instagrabber.customviews.VideoPlayerViewHelper;
import awais.instagrabber.databinding.ItemFeedVideoBinding;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.VideoVersion;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
@ -32,7 +36,7 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
private final DefaultDataSourceFactory dataSourceFactory;
private CacheDataSourceFactory cacheDataSourceFactory;
private FeedModel feedModel;
private Media media;
// private final Runnable loadRunnable = new Runnable() {
// @Override
@ -57,68 +61,55 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
}
@Override
public void bindItem(final FeedModel feedModel) {
public void bindItem(final Media media) {
// Log.d(TAG, "Binding post: " + feedModel.getPostId());
this.feedModel = feedModel;
binding.itemFeedBottom.tvVideoViews.setText(String.valueOf(feedModel.getViewCount()));
// showOrHideDetails(false);
this.media = media;
binding.itemFeedBottom.tvVideoViews.setText(String.valueOf(media.getViewCount()));
final float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() {
@Override
public void onThumbnailClick() {
feedItemCallback.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.videoPost.thumbnail);
feedItemCallback.onPostClick(media, binding.itemFeedTop.ivProfilePic, binding.videoPost.thumbnail);
}
@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());
final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, media.getOriginalHeight(), media.getOriginalWidth());
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 float aspectRatio = (float) media.getOriginalWidth() / media.getOriginalHeight();
String videoUrl = null;
final List<VideoVersion> videoVersions = media.getVideoVersions();
if (videoVersions != null && !videoVersions.isEmpty()) {
final VideoVersion videoVersion = videoVersions.get(0);
videoUrl = videoVersion.getUrl();
}
final VideoPlayerViewHelper videoPlayerViewHelper = new VideoPlayerViewHelper(binding.getRoot().getContext(),
binding.videoPost,
feedModel.getDisplayUrl(),
videoUrl,
vol,
aspectRatio,
feedModel.getThumbnailUrl(),
ResponseBodyUtils.getThumbUrl(media),
false,
null,
videoPlayerCallback);
binding.videoPost.thumbnail.post(() -> {
if (feedModel.getImageHeight() > 0.8 * Utils.displayMetrics.heightPixels) {
if (media.getOriginalHeight() > 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.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);
}
public FeedModel getCurrentFeedModel() {
return feedModel;
public Media getCurrentFeedModel() {
return media;
}
// public void stopPlaying() {

View File

@ -0,0 +1,74 @@
package awais.instagrabber.animations;
import android.graphics.PointF;
import android.view.animation.Interpolator;
public class CubicBezierInterpolator implements Interpolator {
public static final CubicBezierInterpolator DEFAULT = new CubicBezierInterpolator(0.25, 0.1, 0.25, 1);
public static final CubicBezierInterpolator EASE_OUT = new CubicBezierInterpolator(0, 0, .58, 1);
public static final CubicBezierInterpolator EASE_OUT_QUINT = new CubicBezierInterpolator(.23, 1, .32, 1);
public static final CubicBezierInterpolator EASE_IN = new CubicBezierInterpolator(.42, 0, 1, 1);
public static final CubicBezierInterpolator EASE_BOTH = new CubicBezierInterpolator(.42, 0, .58, 1);
protected PointF start;
protected PointF end;
protected PointF a = new PointF();
protected PointF b = new PointF();
protected PointF c = new PointF();
public CubicBezierInterpolator(PointF start, PointF end) throws IllegalArgumentException {
if (start.x < 0 || start.x > 1) {
throw new IllegalArgumentException("startX value must be in the range [0, 1]");
}
if (end.x < 0 || end.x > 1) {
throw new IllegalArgumentException("endX value must be in the range [0, 1]");
}
this.start = start;
this.end = end;
}
public CubicBezierInterpolator(float startX, float startY, float endX, float endY) {
this(new PointF(startX, startY), new PointF(endX, endY));
}
public CubicBezierInterpolator(double startX, double startY, double endX, double endY) {
this((float) startX, (float) startY, (float) endX, (float) endY);
}
@Override
public float getInterpolation(float time) {
return getBezierCoordinateY(getXForTime(time));
}
protected float getBezierCoordinateY(float time) {
c.y = 3 * start.y;
b.y = 3 * (end.y - start.y) - c.y;
a.y = 1 - c.y - b.y;
return time * (c.y + time * (b.y + time * a.y));
}
protected float getXForTime(float time) {
float x = time;
float z;
for (int i = 1; i < 14; i++) {
z = getBezierCoordinateX(x) - time;
if (Math.abs(z) < 1e-3) {
break;
}
x -= z / getXDerivate(x);
}
return x;
}
private float getXDerivate(float t) {
return c.x + t * (2 * b.x + 3 * a.x * t);
}
private float getBezierCoordinateX(float time) {
c.x = 3 * start.x;
b.x = 3 * (end.x - start.x) - c.x;
a.x = 1 - c.x - b.x;
return time * (c.x + time * (b.x + time * a.x));
}
}

View File

@ -0,0 +1,61 @@
package awais.instagrabber.animations;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.view.View;
// https://medium.com/better-programming/animated-fab-button-with-more-options-2dcf7118fff6
public class FabAnimation {
public static boolean rotateFab(final View v, boolean rotate) {
v.animate().setDuration(200)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
})
.rotation(rotate ? 135f : 0f);
return rotate;
}
public static void showIn(final View v) {
v.setVisibility(View.VISIBLE);
v.setAlpha(0f);
v.setTranslationY(v.getHeight());
v.animate()
.setDuration(200)
.translationY(0)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
})
.alpha(1f)
.start();
}
public static void showOut(final View v) {
v.setVisibility(View.VISIBLE);
v.setAlpha(1f);
v.setTranslationY(0);
v.animate()
.setDuration(200)
.translationY(v.getHeight())
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
v.setVisibility(View.GONE);
super.onAnimationEnd(animation);
}
}).alpha(0f)
.start();
}
public static void init(final View v) {
v.setVisibility(View.GONE);
v.setTranslationY(v.getHeight());
v.setAlpha(0f);
}
}

View File

@ -0,0 +1,84 @@
package awais.instagrabber.animations;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.graphics.Outline;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewOutlineProvider;
/**
* A {@link ViewOutlineProvider} that has helper functions to create reveal animations.
* This class should be extended so that subclasses can define the reveal shape as the
* animation progresses from 0 to 1.
*/
public abstract class RevealOutlineAnimation extends ViewOutlineProvider {
protected Rect mOutline;
protected float mOutlineRadius;
public RevealOutlineAnimation() {
mOutline = new Rect();
}
/**
* Returns whether elevation should be removed for the duration of the reveal animation.
*/
abstract boolean shouldRemoveElevationDuringAnimation();
/**
* Sets the progress, from 0 to 1, of the reveal animation.
*/
abstract void setProgress(float progress);
public ValueAnimator createRevealAnimator(final View revealView, boolean isReversed) {
ValueAnimator va =
isReversed ? ValueAnimator.ofFloat(1f, 0f) : ValueAnimator.ofFloat(0f, 1f);
final float elevation = revealView.getElevation();
va.addListener(new AnimatorListenerAdapter() {
private boolean mIsClippedToOutline;
private ViewOutlineProvider mOldOutlineProvider;
public void onAnimationStart(Animator animation) {
mIsClippedToOutline = revealView.getClipToOutline();
mOldOutlineProvider = revealView.getOutlineProvider();
revealView.setOutlineProvider(RevealOutlineAnimation.this);
revealView.setClipToOutline(true);
if (shouldRemoveElevationDuringAnimation()) {
revealView.setTranslationZ(-elevation);
}
}
public void onAnimationEnd(Animator animation) {
revealView.setOutlineProvider(mOldOutlineProvider);
revealView.setClipToOutline(mIsClippedToOutline);
if (shouldRemoveElevationDuringAnimation()) {
revealView.setTranslationZ(0);
}
}
});
va.addUpdateListener(v -> {
float progress = (Float) v.getAnimatedValue();
setProgress(progress);
revealView.invalidateOutline();
});
return va;
}
@Override
public void getOutline(View v, Outline outline) {
outline.setRoundRect(mOutline, mOutlineRadius);
}
public float getRadius() {
return mOutlineRadius;
}
public void getOutline(Rect out) {
out.set(mOutline);
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package awais.instagrabber.animations;
import android.graphics.Rect;
/**
* A {@link RevealOutlineAnimation} that provides an outline that interpolates between two radii
* and two {@link Rect}s.
* <p>
* An example usage of this provider is an outline that starts out as a circle and ends
* as a rounded rectangle.
*/
public class RoundedRectRevealOutlineProvider extends RevealOutlineAnimation {
private final float mStartRadius;
private final float mEndRadius;
private final Rect mStartRect;
private final Rect mEndRect;
public RoundedRectRevealOutlineProvider(float startRadius, float endRadius, Rect startRect, Rect endRect) {
mStartRadius = startRadius;
mEndRadius = endRadius;
mStartRect = startRect;
mEndRect = endRect;
}
@Override
public boolean shouldRemoveElevationDuringAnimation() {
return false;
}
@Override
public void setProgress(float progress) {
mOutlineRadius = (1 - progress) * mStartRadius + progress * mEndRadius;
mOutline.left = (int) ((1 - progress) * mStartRect.left + progress * mEndRect.left);
mOutline.top = (int) ((1 - progress) * mStartRect.top + progress * mEndRect.top);
mOutline.right = (int) ((1 - progress) * mStartRect.right + progress * mEndRect.right);
mOutline.bottom = (int) ((1 - progress) * mStartRect.bottom + progress * mEndRect.bottom);
}
}

View File

@ -0,0 +1,45 @@
package awais.instagrabber.animations;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
public class ScaleAnimation {
private final View view;
public ScaleAnimation(View view) {
this.view = view;
}
public void start() {
AnimatorSet set = new AnimatorSet();
ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 2.0f);
ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 2.0f);
set.setDuration(150);
set.setInterpolator(new AccelerateDecelerateInterpolator());
set.playTogether(scaleY, scaleX);
set.start();
}
public void stop() {
AnimatorSet set = new AnimatorSet();
ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1.0f);
// scaleY.setDuration(250);
// scaleY.setInterpolator(new DecelerateInterpolator());
ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1.0f);
// scaleX.setDuration(250);
// scaleX.setInterpolator(new DecelerateInterpolator());
set.setDuration(150);
set.setInterpolator(new AccelerateDecelerateInterpolator());
set.playTogether(scaleY, scaleX);
set.start();
}
}

View File

@ -17,7 +17,8 @@ import java.util.List;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.repositories.responses.FriendshipStatus;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.TextUtils;
@ -106,33 +107,22 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
if (childComment != null) {
final JSONObject owner = childComment.getJSONObject("owner");
final ProfileModel profileModel = new ProfileModel(false,
false,
false,
owner.getString(Constants.EXTRAS_ID),
final User user = new User(
owner.optLong(Constants.EXTRAS_ID, 0),
owner.getString(Constants.EXTRAS_USERNAME),
null,
null,
null,
false,
owner.getString("profile_pic_url"),
null,
0,
0,
0,
false,
false,
false,
false,
false);
new FriendshipStatus(false, false, false, false, false, false, false, false, false, false),
false, false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0, null, null);
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));
user));
}
}
}
@ -194,25 +184,16 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
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),
final User user = new User(
owner.optLong(Constants.EXTRAS_ID, 0),
owner.getString(Constants.EXTRAS_USERNAME),
null,
null,
null,
false,
owner.getString("profile_pic_url"),
null,
0,
0,
0,
false,
false,
false,
false,
false);
new FriendshipStatus(false, false, false, false, false, false, false, false, false, false),
owner.optBoolean("is_verified"),
false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0, null, null);
final JSONObject likedBy = comment.optJSONObject("edge_liked_by");
final String commentId = comment.getString(Constants.EXTRAS_ID);
final CommentModel commentModel = new CommentModel(commentId,
@ -220,7 +201,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
comment.getLong("created_at"),
likedBy != null ? likedBy.optLong("count", 0) : 0,
comment.getBoolean("viewer_has_liked"),
profileModel);
user);
if (i == 0 && !foundEndCursor.contains("tao_cursor"))
commentModel.setPageCursor(hasNextPage, TextUtils.isEmpty(foundEndCursor) ? null : foundEndCursor);
JSONObject tempJsonObject;
@ -245,24 +226,16 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
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),
final User childUser = new User(
tempJsonObject.optLong(Constants.EXTRAS_ID, 0),
tempJsonObject.getString(Constants.EXTRAS_USERNAME),
null,
null,
null,
false,
tempJsonObject.getString("profile_pic_url"),
null,
0,
0,
0,
false,
false,
false,
false,
false);
new FriendshipStatus(false, false, false, false, false, false, false, false, false, false),
tempJsonObject.optBoolean("is_verified"), false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0,
null, null);
tempJsonObject = childComment.optJSONObject("edge_liked_by");
childCommentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
@ -270,7 +243,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
childComment.getLong("created_at"),
tempJsonObject != null ? tempJsonObject.optLong("count", 0) : 0,
childComment.getBoolean("viewer_has_liked"),
childProfileModel));
childUser));
}
childCommentModels.get(childCommentsLen - 1).setPageCursor(childHasNextPage, childEndCursor);
commentModel.setChildCommentModels(childCommentModels);

View File

@ -0,0 +1,84 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import androidx.annotation.NonNull;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.Locale;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.webservices.DirectMessagesService;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class CreateThreadAction extends AsyncTask<Void, Void, Void> {
private static final String TAG = "CommentAction";
private final String cookie;
private final long userId;
private final OnTaskCompleteListener onTaskCompleteListener;
private final DirectMessagesService directMessagesService;
public CreateThreadAction(final String cookie, final long userId, final OnTaskCompleteListener onTaskCompleteListener) {
this.cookie = cookie;
this.userId = userId;
this.onTaskCompleteListener = onTaskCompleteListener;
directMessagesService = DirectMessagesService.getInstance(CookieUtils.getCsrfTokenFromCookie(cookie),
CookieUtils.getUserIdFromCookie(cookie),
Utils.settingsHelper.getString(Constants.DEVICE_UUID));
}
protected Void doInBackground(Void... lmao) {
final Call<DirectThread> createThreadRequest = directMessagesService.createThread(Collections.singletonList(userId), null);
createThreadRequest.enqueue(new Callback<DirectThread>() {
@Override
public void onResponse(@NonNull final Call<DirectThread> call, @NonNull final Response<DirectThread> response) {
if (!response.isSuccessful()) {
if (response.errorBody() != null) {
try {
final String string = response.errorBody().string();
final String msg = String.format(Locale.US,
"onResponse: url: %s, responseCode: %d, errorBody: %s",
call.request().url().toString(),
response.code(),
string);
Log.e(TAG, msg);
} catch (IOException e) {
Log.e(TAG, "onResponse: ", e);
}
}
Log.e(TAG, "onResponse: request was not successful and response error body was null");
}
onTaskCompleteListener.onTaskComplete(response.body());
if (response.body() == null) {
Log.e(TAG, "onResponse: thread is null");
}
}
@Override
public void onFailure(@NonNull final Call<DirectThread> call, @NonNull final Throwable t) {
onTaskCompleteListener.onTaskComplete(null);
}
});
return null;
}
// @Override
// protected void onPostExecute() {
// }
public interface OnTaskCompleteListener {
void onTaskComplete(final DirectThread thread);
}
}

View File

@ -1,10 +1,15 @@
package awais.instagrabber.asyncs;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import awais.instagrabber.customviews.helpers.PostFetcher;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.discover.TopicalExploreFeedResponse;
import awais.instagrabber.repositories.responses.WrappedMedia;
import awais.instagrabber.webservices.DiscoverService;
import awais.instagrabber.webservices.ServiceCallback;
@ -20,18 +25,28 @@ public class DiscoverPostFetchService implements PostFetcher.PostFetchService {
}
@Override
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
discoverService.topicalExplore(topicalExploreRequest, new ServiceCallback<DiscoverService.TopicalExploreResponse>() {
public void fetch(final FetchListener<List<Media>> fetchListener) {
discoverService.topicalExplore(topicalExploreRequest, new ServiceCallback<TopicalExploreFeedResponse>() {
@Override
public void onSuccess(final DiscoverService.TopicalExploreResponse result) {
public void onSuccess(final TopicalExploreFeedResponse result) {
if (result == null) {
onFailure(new RuntimeException("result is null"));
return;
}
moreAvailable = result.isMoreAvailable();
topicalExploreRequest.setMaxId(result.getNextMaxId());
final List<WrappedMedia> items = result.getItems();
final List<Media> posts;
if (items == null) {
posts = Collections.emptyList();
} else {
posts = items.stream()
.map(WrappedMedia::getMedia)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
if (fetchListener != null) {
fetchListener.onResult(result.getItems());
fetchListener.onResult(posts);
}
}
@ -46,7 +61,7 @@ public class DiscoverPostFetchService implements PostFetcher.PostFetchService {
@Override
public void reset() {
topicalExploreRequest.setMaxId(-1);
topicalExploreRequest.setMaxId(null);
}
@Override

View File

@ -6,10 +6,10 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.utils.DownloadUtils;
public final class DownloadedCheckerAsyncTask extends AsyncTask<FeedModel, Void, Map<String, List<Boolean>>> {
public final class DownloadedCheckerAsyncTask extends AsyncTask<Media, Void, Map<String, List<Boolean>>> {
private static final String TAG = "DownloadedCheckerAsyncTask";
private final OnCheckResultListener listener;
@ -19,13 +19,13 @@ public final class DownloadedCheckerAsyncTask extends AsyncTask<FeedModel, Void,
}
@Override
protected Map<String, List<Boolean>> doInBackground(final FeedModel... feedModels) {
protected Map<String, List<Boolean>> doInBackground(final Media... feedModels) {
if (feedModels == null) {
return null;
}
final Map<String, List<Boolean>> map = new HashMap<>();
for (final FeedModel feedModel : feedModels) {
map.put(feedModel.getPostId(), DownloadUtils.checkDownloaded(feedModel));
for (final Media media : feedModels) {
map.put(media.getPk(), DownloadUtils.checkDownloaded(media));
}
return map;
}

View File

@ -1,14 +1,11 @@
package awais.instagrabber.asyncs;
//import android.os.Handler;
//import android.util.Log;
import java.util.ArrayList;
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.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
@ -21,47 +18,40 @@ public class FeedPostFetchService implements PostFetcher.PostFetchService {
private static final String TAG = "FeedPostFetchService";
private final FeedService feedService;
private String nextCursor;
// private final Handler handler;
private boolean hasNextPage;
// private static final int DELAY_MILLIS = 500;
public FeedPostFetchService() {
feedService = FeedService.getInstance();
// handler = new Handler();
}
@Override
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
final List<FeedModel> feedModels = new ArrayList<>();
public void fetch(final FetchListener<List<Media>> fetchListener) {
final List<Media> feedModels = new ArrayList<>();
final String cookie = settingsHelper.getString(Constants.COOKIE);
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID);
feedModels.clear();
feedService.fetch(csrfToken, nextCursor, new ServiceCallback<PostsFetchResponse>() {
feedService.fetch(csrfToken, deviceUuid, nextCursor, new ServiceCallback<PostsFetchResponse>() {
@Override
public void onSuccess(final PostsFetchResponse result) {
if (result == null && feedModels.size() > 0) {
fetchListener.onResult(feedModels);
return;
}
else if (result == null) return;
} else if (result == null) return;
nextCursor = result.getNextCursor();
hasNextPage = result.hasNextPage();
feedModels.addAll(result.getFeedModels());
if (fetchListener != null) {
if (feedModels.size() < 15 && hasNextPage) {
// handler.postDelayed(() -> {
feedService.fetch(csrfToken, nextCursor, this);
// }, DELAY_MILLIS);
}
else {
// if (feedModels.size() < 15 && hasNextPage) {
// feedService.fetch(csrfToken, nextCursor, this);
// } else {
fetchListener.onResult(feedModels);
}
// }
}
}
@Override
public void onFailure(final Throwable t) {
// Log.e(TAG, "onFailure: ", t);
if (fetchListener != null) {
fetchListener.onFailure(t);
}

View File

@ -3,6 +3,8 @@ package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import androidx.annotation.NonNull;
import org.json.JSONObject;
import java.net.HttpURLConnection;
@ -12,6 +14,7 @@ import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public class GetActivityAsyncTask extends AsyncTask<String, Void, GetActivityAsyncTask.NotificationCounts> {
private static final String TAG = "GetActivityAsyncTask";
@ -35,14 +38,14 @@ public class GetActivityAsyncTask extends AsyncTask<String, Void, GetActivityAsy
if (cookiesArray == null) return null;
final String cookie = cookiesArray[0];
if (TextUtils.isEmpty(cookie)) return null;
final String uid = CookieUtils.getUserIdFromCookie(cookie);
final long uid = CookieUtils.getUserIdFromCookie(cookie);
final String url = "https://www.instagram.com/graphql/query/?query_hash=0f318e8cfff9cc9ef09f88479ff571fb"
+ "&variables={\"id\":\"" + uid + "\"}";
HttpURLConnection urlConnection = null;
try {
urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);
urlConnection.setRequestProperty("User-Agent", Utils.settingsHelper.getString(Constants.BROWSER_UA));
urlConnection.setRequestProperty("x-csrftoken", cookie.split("csrftoken=")[1].split(";")[0]);
urlConnection.connect();
if (urlConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
@ -117,6 +120,7 @@ public class GetActivityAsyncTask extends AsyncTask<String, Void, GetActivityAsy
return likesCount;
}
@NonNull
@Override
public String toString() {
return "NotificationCounts{" +

View File

@ -4,8 +4,8 @@ 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.repositories.responses.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.webservices.GraphQLService;
import awais.instagrabber.webservices.ServiceCallback;
@ -27,8 +27,8 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService {
}
@Override
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
final ServiceCallback cb = new ServiceCallback<PostsFetchResponse>() {
public void fetch(final FetchListener<List<Media>> fetchListener) {
final ServiceCallback<PostsFetchResponse> cb = new ServiceCallback<PostsFetchResponse>() {
@Override
public void onSuccess(final PostsFetchResponse result) {
if (result == null) return;

View File

@ -1,176 +0,0 @@
package awais.instagrabber.asyncs;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONObject;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import awais.instagrabber.models.ImageUploadOptions;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.NumberUtils;
public class ImageUploader extends AsyncTask<ImageUploadOptions, Void, ImageUploader.ImageUploadResponse> {
private static final String TAG = "ImageUploader";
private static final long LOWER = 1000000000L;
private static final long UPPER = 9999999999L;
private OnImageUploadCompleteListener listener;
protected ImageUploadResponse doInBackground(final ImageUploadOptions... imageUploadOptions) {
if (imageUploadOptions == null || imageUploadOptions.length == 0 || imageUploadOptions[0] == null) {
return null;
}
HttpURLConnection connection = null;
OutputStream out = null;
InputStream inputStream = null;
BufferedReader r = null;
ByteArrayOutputStream baos = null;
try {
final ImageUploadOptions options = imageUploadOptions[0];
final Bitmap bitmap = options.getBitmap();
baos = new ByteArrayOutputStream();
final boolean compressResult = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
if (!compressResult) {
Log.e(TAG, "Compress result was false!");
return null;
}
final byte[] bytes = baos.toByteArray();
final String contentLength = String.valueOf(bytes.length);
final Map<String, String> headers = new HashMap<>();
final String uploadId = String.valueOf(new Date().getTime());
final long random = NumberUtils.random(LOWER, UPPER + 1);
final String name = String.format("%s_0_%s", uploadId, random);
final String waterfallId = options.getWaterfallId() != null ? options.getWaterfallId() : UUID.randomUUID().toString();
headers.put("X-Entity-Type", "image/jpeg");
headers.put("Offset", "0");
headers.put("X_FB_PHOTO_WATERFALL_ID", waterfallId);
headers.put("X-Instagram-Rupload-Params", new JSONObject(createPhotoRuploadParams(options, uploadId)).toString());
headers.put("X-Entity-Name", name);
headers.put("X-Entity-Length", contentLength);
headers.put("Content-Type", "application/octet-stream");
headers.put("Content-Length", contentLength);
headers.put("Accept-Encoding", "gzip");
final String url = "https://www.instagram.com/rupload_igphoto/" + name + "/";
connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("POST");
connection.setUseCaches(false);
connection.setDoOutput(true);
NetworkUtils.setConnectionHeaders(connection, headers);
out = new BufferedOutputStream(connection.getOutputStream());
out.write(bytes);
out.flush();
final int responseCode = connection.getResponseCode();
Log.d(TAG, "response: " + responseCode);
final String responseCodeString = String.valueOf(responseCode);
final InputStream responseInputStream = responseCodeString.startsWith("4") || responseCodeString.startsWith("5")
? connection.getErrorStream() : connection.getInputStream();
r = new BufferedReader(new InputStreamReader(responseInputStream));
final StringBuilder builder = new StringBuilder();
for (String line = r.readLine(); line != null; line = r.readLine()) {
if (builder.length() != 0) {
builder.append("\n");
}
builder.append(line);
}
return new ImageUploadResponse(responseCode, new JSONObject(builder.toString()));
} catch (Exception ex) {
Log.e(TAG, "Image upload error:", ex);
} finally {
if (r != null) {
try {
r.close();
} catch (IOException ignored) {}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException ignored) {}
}
if (out != null) {
try {
out.close();
} catch (IOException ignored) {}
}
if (connection != null) {
connection.disconnect();
}
if (baos != null) {
try {
baos.close();
} catch (IOException ignored) {}
}
}
return null;
}
@Override
protected void onPostExecute(final ImageUploadResponse response) {
if (listener != null) {
listener.onImageUploadComplete(response);
}
}
private Map<String, String> createPhotoRuploadParams(final ImageUploadOptions options, final String uploadId) {
final Map<String, Integer> retryContext = new HashMap<>();
retryContext.put("num_step_auto_retry", 0);
retryContext.put("num_reupload", 0);
retryContext.put("num_step_manual_retry", 0);
final String retryContextString = new JSONObject(retryContext).toString();
final Map<String, String> params = new HashMap<>();
params.put("retry_context", retryContextString);
params.put("media_type", "1");
params.put("upload_id", uploadId);
params.put("xsharing_user_ids", "[]");
final Map<String, String> imageCompression = new HashMap<>();
imageCompression.put("lib_name", "moz");
imageCompression.put("lib_version", "3.1.m");
imageCompression.put("quality", "80");
params.put("image_compression", new JSONObject(imageCompression).toString());
if (options.isSidecar()) {
params.put("is_sidecar", "1");
}
return params;
}
public void setOnTaskCompleteListener(final OnImageUploadCompleteListener listener) {
if (listener != null) {
this.listener = listener;
}
}
public interface OnImageUploadCompleteListener {
void onImageUploadComplete(ImageUploadResponse response);
}
public static class ImageUploadResponse {
private int responseCode;
private JSONObject response;
public ImageUploadResponse(int responseCode, JSONObject response) {
this.responseCode = responseCode;
this.response = response;
}
public int getResponseCode() {
return responseCode;
}
public JSONObject getResponse() {
return response;
}
}
}

View File

@ -24,9 +24,9 @@ public final class LocationFetcher extends AsyncTask<Void, Void, LocationModel>
private static final String TAG = "LocationFetcher";
private final FetchListener<LocationModel> fetchListener;
private final String id;
private final long id;
public LocationFetcher(final String id, final FetchListener<LocationModel> fetchListener) {
public LocationFetcher(final long id, final FetchListener<LocationModel> fetchListener) {
// idSlug = id + "/" + slug UPDATE: slug can be ignored tbh
this.id = id;
this.fetchListener = fetchListener;
@ -52,8 +52,7 @@ public final class LocationFetcher extends AsyncTask<Void, Void, LocationModel>
// final JSONArray edges = timelineMedia.getJSONArray("edges");
// }
result = new LocationModel(
location.getString(Constants.EXTRAS_ID),
location.getString("slug"),
location.getLong(Constants.EXTRAS_ID),
location.getString("name"),
location.getString("blurb"),
location.getString("website"),

View File

@ -4,8 +4,8 @@ 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.repositories.responses.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.webservices.GraphQLService;
import awais.instagrabber.webservices.LocationService;
@ -27,8 +27,8 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService {
}
@Override
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
final ServiceCallback cb = new ServiceCallback<PostsFetchResponse>() {
public void fetch(final FetchListener<List<Media>> fetchListener) {
final ServiceCallback<PostsFetchResponse> cb = new ServiceCallback<PostsFetchResponse>() {
@Override
public void onSuccess(final PostsFetchResponse result) {
if (result == null) return;

View File

@ -3,40 +3,32 @@ package awais.instagrabber.asyncs;
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 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.enums.MediaItemType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.repositories.responses.Media;
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 PostFetcher extends AsyncTask<Void, Void, FeedModel> {
public final class PostFetcher extends AsyncTask<Void, Void, Media> {
private static final String TAG = "PostFetcher";
private final String shortCode;
private final FetchListener<FeedModel> fetchListener;
private final FetchListener<Media> fetchListener;
public PostFetcher(final String shortCode, final FetchListener<FeedModel> fetchListener) {
public PostFetcher(final String shortCode, final FetchListener<Media> fetchListener) {
this.shortCode = shortCode;
this.fetchListener = fetchListener;
}
@Override
protected FeedModel doInBackground(final Void... voids) {
protected Media doInBackground(final Void... voids) {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL("https://www.instagram.com/p/" + shortCode + "/?__a=1").openConnection();
@ -47,100 +39,101 @@ public final class PostFetcher extends AsyncTask<Void, Void, FeedModel> {
final JSONObject media = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql")
.getJSONObject("shortcode_media");
ProfileModel profileModel = null;
if (media.has("owner")) {
final JSONObject owner = media.getJSONObject("owner");
profileModel = new ProfileModel(
owner.optBoolean("is_private"),
owner.optBoolean("is_private"),
owner.optBoolean("is_verified"),
owner.optString("id"),
owner.optString("username"),
owner.optString("full_name"),
null,
null,
owner.optString("profile_pic_url"),
owner.optString("profile_pic_url"),
owner.optInt("edge_owner_to_timeline_media"),
owner.optInt("edge_followed_by"),
-1,
owner.optBoolean("followed_by_viewer"),
false,
owner.optBoolean("restricted_by_viewer"),
owner.optBoolean("blocked_by_viewer"),
owner.optBoolean("requested_by_viewer")
);
}
final long timestamp = media.getLong("taken_at_timestamp");
final boolean isVideo = media.has("is_video") && media.optBoolean("is_video");
final boolean isSlider = media.has("edge_sidecar_to_children");
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("edge_media_to_caption");
if (mediaToCaption == null) postCaption = null;
else {
final JSONArray captions = mediaToCaption.optJSONArray("edges");
postCaption = captions != null && captions.length() > 0 ?
captions.getJSONObject(0).getJSONObject("node").optString("text") : null;
}
JSONObject commentObject = media.optJSONObject("edge_media_to_parent_comment");
final long commentsCount = commentObject != null ? commentObject.optLong("count") : 0;
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 List<PostChild> 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());
}
feedModelBuilder.setSliderItems(postModels);
}
return feedModelBuilder.build();
// ProfileModel profileModel = null;
// if (media.has("owner")) {
// final JSONObject owner = media.getJSONObject("owner");
// profileModel = new ProfileModel(
// owner.optBoolean("is_private"),
// owner.optBoolean("is_private"),
// owner.optBoolean("is_verified"),
// owner.optString("id"),
// owner.optString("username"),
// owner.optString("full_name"),
// null,
// null,
// owner.optString("profile_pic_url"),
// owner.optString("profile_pic_url"),
// owner.optInt("edge_owner_to_timeline_media"),
// owner.optInt("edge_followed_by"),
// -1,
// owner.optBoolean("followed_by_viewer"),
// false,
// owner.optBoolean("restricted_by_viewer"),
// owner.optBoolean("blocked_by_viewer"),
// owner.optBoolean("requested_by_viewer")
// );
// }
// final long timestamp = media.getLong("taken_at_timestamp");
//
// final boolean isVideo = media.has("is_video") && media.optBoolean("is_video");
// final boolean isSlider = media.has("edge_sidecar_to_children");
//
// 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("edge_media_to_caption");
// if (mediaToCaption == null) postCaption = null;
// else {
// final JSONArray captions = mediaToCaption.optJSONArray("edges");
// postCaption = captions != null && captions.length() > 0 ?
// captions.getJSONObject(0).getJSONObject("node").optString("text") : null;
// }
//
// JSONObject commentObject = media.optJSONObject("edge_media_to_parent_comment");
// final long commentsCount = commentObject != null ? commentObject.optLong("count") : 0;
// 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 List<PostChild> 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());
// }
// feedModelBuilder.setSliderItems(postModels);
// }
// return feedModelBuilder.build();
return ResponseBodyUtils.parseGraphQLItem(media);
}
} catch (Exception e) {
if (logCollector != null) {
@ -161,7 +154,7 @@ public final class PostFetcher extends AsyncTask<Void, Void, FeedModel> {
}
@Override
protected void onPostExecute(final FeedModel feedModel) {
protected void onPostExecute(final Media feedModel) {
if (fetchListener != null) fetchListener.onResult(feedModel);
}
}

View File

@ -5,91 +5,83 @@ import android.util.Log;
import androidx.annotation.Nullable;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
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.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.TextUtils;
import awaisomereport.LogCollector;
import awais.instagrabber.repositories.responses.FriendshipStatus;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.webservices.GraphQLService;
import awais.instagrabber.webservices.ServiceCallback;
import awais.instagrabber.webservices.UserService;
import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class ProfileFetcher extends AsyncTask<Void, Void, Void> {
private static final String TAG = ProfileFetcher.class.getSimpleName();
private final UserService userService;
private final GraphQLService graphQLService;
public final class ProfileFetcher extends AsyncTask<Void, Void, ProfileModel> {
private final FetchListener<ProfileModel> fetchListener;
private final FetchListener<User> fetchListener;
private final boolean isLoggedIn;
private final String userName;
public ProfileFetcher(String userName, FetchListener<ProfileModel> fetchListener) {
public ProfileFetcher(final String userName,
final boolean isLoggedIn,
final FetchListener<User> fetchListener) {
this.userName = userName;
this.isLoggedIn = isLoggedIn;
this.fetchListener = fetchListener;
userService = isLoggedIn ? UserService.getInstance() : null;
graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
}
@Nullable
@Override
protected ProfileModel doInBackground(final Void... voids) {
ProfileModel result = null;
try {
final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/" + userName + "/?__a=1").openConnection();
conn.setUseCaches(true);
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject user = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql").getJSONObject(Constants.EXTRAS_USER);
final String cookie = settingsHelper.getString(Constants.COOKIE);
boolean isPrivate = user.getBoolean("is_private");
final String id = user.getString(Constants.EXTRAS_ID);
final String uid = CookieUtils.getUserIdFromCookie(cookie);
final JSONObject timelineMedia = user.getJSONObject("edge_owner_to_timeline_media");
if (timelineMedia.has("edges")) {
final JSONArray edges = timelineMedia.getJSONArray("edges");
}
String url = user.optString("external_url");
if (TextUtils.isEmpty(url)) url = null;
result = new ProfileModel(isPrivate,
user.optBoolean("followed_by_viewer") ? false : (id.equals(uid) ? false : isPrivate),
user.getBoolean("is_verified"),
id,
userName,
user.getString("full_name"),
user.getString("biography"),
url,
user.getString("profile_pic_url"),
user.getString("profile_pic_url_hd"),
timelineMedia.getLong("count"),
user.getJSONObject("edge_followed_by").getLong("count"),
user.getJSONObject("edge_follow").getLong("count"),
user.optBoolean("followed_by_viewer"),
user.optBoolean("follows_viewer"),
user.optBoolean("restricted_by_viewer"),
user.optBoolean("blocked_by_viewer"),
user.optBoolean("requested_by_viewer"));
}
conn.disconnect();
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ASYNC_PROFILE_FETCHER, "doInBackground");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
}
return result;
protected Void doInBackground(final Void... voids) {
if (isLoggedIn) {
userService.getUsernameInfo(userName, new ServiceCallback<User>() {
@Override
public void onSuccess(final User user) {
userService.getUserFriendship(user.getPk(), new ServiceCallback<FriendshipStatus>() {
@Override
public void onSuccess(final FriendshipStatus status) {
user.setFriendshipStatus(status);
fetchListener.onResult(user);
}
@Override
protected void onPostExecute(final ProfileModel result) {
if (fetchListener != null) fetchListener.onResult(result);
public void onFailure(final Throwable t) {
Log.e(TAG, "Error", t);
fetchListener.onFailure(t);
}
});
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error", t);
fetchListener.onFailure(t);
}
});
}
else {
graphQLService.fetchUser(userName, new ServiceCallback<User>() {
@Override
public void onSuccess(final User user) {
fetchListener.onResult(user);
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error", t);
fetchListener.onFailure(t);
}
});
}
return null;
}
@Override
protected void onPreExecute() {
if (fetchListener != null) fetchListener.doBefore();
}
}

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