Browse Source
Merge pull request #503 from ammargitham/dm-notifications-enhancements
Merge pull request #503 from ammargitham/dm-notifications-enhancements
DM updaterenovate/org.robolectric-robolectric-4.x test-v19.1.0-a1
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
546 changed files with 40550 additions and 10208 deletions
-
5.idea/jarRepositories.xml
-
24app/build.gradle
-
5app/proguard-rules.pro
-
26app/src/main/AndroidManifest.xml
-
28app/src/main/java/awais/instagrabber/InstaGrabberApplication.java
-
276app/src/main/java/awais/instagrabber/activities/CameraActivity.java
-
6app/src/main/java/awais/instagrabber/activities/DirectDownload.java
-
69app/src/main/java/awais/instagrabber/activities/MainActivity.java
-
407app/src/main/java/awais/instagrabber/adapters/DirectItemsAdapter.java
-
50app/src/main/java/awais/instagrabber/adapters/DirectMessageInboxAdapter.java
-
152app/src/main/java/awais/instagrabber/adapters/DirectMessageItemsAdapter.java
-
47app/src/main/java/awais/instagrabber/adapters/DirectMessageMembersAdapter.java
-
117app/src/main/java/awais/instagrabber/adapters/DirectPendingUsersAdapter.java
-
81app/src/main/java/awais/instagrabber/adapters/DirectReactionsAdapter.java
-
183app/src/main/java/awais/instagrabber/adapters/DirectUsersAdapter.java
-
8app/src/main/java/awais/instagrabber/adapters/DiscoverTopicsAdapter.java
-
60app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java
-
17app/src/main/java/awais/instagrabber/adapters/FeedItemCallbackAdapter.java
-
94app/src/main/java/awais/instagrabber/adapters/FiltersAdapter.java
-
9app/src/main/java/awais/instagrabber/adapters/LikesAdapter.java
-
111app/src/main/java/awais/instagrabber/adapters/MediaItemsAdapter.java
-
2app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java
-
206app/src/main/java/awais/instagrabber/adapters/PostViewerChildAdapter.java
-
45app/src/main/java/awais/instagrabber/adapters/PostsAdapter.java
-
57app/src/main/java/awais/instagrabber/adapters/PostsMediaAdapter.java
-
60app/src/main/java/awais/instagrabber/adapters/SavedCollectionsAdapter.java
-
22app/src/main/java/awais/instagrabber/adapters/SliderItemsAdapter.java
-
151app/src/main/java/awais/instagrabber/adapters/UserSearchResultsAdapter.java
-
132app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageInboxItemViewHolder.java
-
152app/src/main/java/awais/instagrabber/adapters/viewholder/FeedGridItemViewHolder.java
-
6app/src/main/java/awais/instagrabber/adapters/viewholder/FeedStoryViewHolder.java
-
72app/src/main/java/awais/instagrabber/adapters/viewholder/FilterViewHolder.java
-
11app/src/main/java/awais/instagrabber/adapters/viewholder/FollowsViewHolder.java
-
10app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java
-
29app/src/main/java/awais/instagrabber/adapters/viewholder/PostMediaViewHolder.java
-
43app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewHolder.java
-
4app/src/main/java/awais/instagrabber/adapters/viewholder/SliderItemViewHolder.java
-
27app/src/main/java/awais/instagrabber/adapters/viewholder/SliderPhotoViewHolder.java
-
24app/src/main/java/awais/instagrabber/adapters/viewholder/SliderVideoViewHolder.java
-
6app/src/main/java/awais/instagrabber/adapters/viewholder/StoryListViewHolder.java
-
146app/src/main/java/awais/instagrabber/adapters/viewholder/TopicClusterViewHolder.java
-
6app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ChildCommentViewHolder.java
-
6app/src/main/java/awais/instagrabber/adapters/viewholder/comments/ParentCommentViewHolder.java
-
359app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectInboxItemViewHolder.java
-
100app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemActionLogViewHolder.java
-
68app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemAnimatedMediaViewHolder.java
-
44app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemDefaultViewHolder.java
-
36app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemLikeViewHolder.java
-
83app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemLinkViewHolder.java
-
180app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemMediaShareViewHolder.java
-
75app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemMediaViewHolder.java
-
47app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemPlaceholderViewHolder.java
-
126app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemProfileViewHolder.java
-
192app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemRavenMediaViewHolder.java
-
175app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemReelShareViewHolder.java
-
119app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemStoryShareViewHolder.java
-
38app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemTextViewHolder.java
-
90app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemVideoCallEventViewHolder.java
-
592app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemViewHolder.java
-
190app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemVoiceMediaViewHolder.java
-
32app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageActionLogViewHolder.java
-
57app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageAnimatedMediaViewHolder.java
-
30app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageDefaultViewHolder.java
-
100app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageItemViewHolder.java
-
46app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageLinkViewHolder.java
-
64app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageMediaShareViewHolder.java
-
52app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageMediaViewHolder.java
-
30app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessagePlaceholderViewHolder.java
-
35app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageProfileViewHolder.java
-
104app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageRavenMediaViewHolder.java
-
67app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageReelShareViewHolder.java
-
67app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageStoryShareViewHolder.java
-
43app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageTextViewHolder.java
-
29app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageVideoCallEventViewHolder.java
-
77app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageVoiceMediaViewHolder.java
-
89app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectPendingUserViewHolder.java
-
71app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectReactionViewHolder.java
-
102app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectUserViewHolder.java
-
89app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/RecipientThreadViewHolder.java
-
71app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedItemViewHolder.java
-
21app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedPhotoViewHolder.java
-
13app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedSliderViewHolder.java
-
55app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedVideoViewHolder.java
-
74app/src/main/java/awais/instagrabber/animations/CubicBezierInterpolator.java
-
61app/src/main/java/awais/instagrabber/animations/FabAnimation.java
-
84app/src/main/java/awais/instagrabber/animations/RevealOutlineAnimation.java
-
56app/src/main/java/awais/instagrabber/animations/RoundedRectRevealOutlineProvider.java
-
45app/src/main/java/awais/instagrabber/animations/ScaleAnimation.java
-
269app/src/main/java/awais/instagrabber/asyncs/CommentsFetcher.java
-
84app/src/main/java/awais/instagrabber/asyncs/CreateThreadAction.java
-
27app/src/main/java/awais/instagrabber/asyncs/DiscoverPostFetchService.java
-
10app/src/main/java/awais/instagrabber/asyncs/DownloadedCheckerAsyncTask.java
-
32app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java
-
8app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java
-
6app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java
-
176app/src/main/java/awais/instagrabber/asyncs/ImageUploader.java
-
7app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java
-
6app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java
-
209app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java
-
130app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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(); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
|||
} |
@ -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()); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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)); |
|||
} |
|||
} |
@ -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()); |
|||
} |
|||
} |
@ -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()); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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(); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
|
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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 |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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 |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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()); |
|||
} |
|||
} |
@ -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)); |
|||
} |
|||
} |
@ -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)); |
|||
} |
|||
} |
@ -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())); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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)); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
|||
} |
@ -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()); |
|||
} |
|||
} |
|||
} |
@ -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()); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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()); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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)); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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(); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
|||
} |
Some files were not shown because too many files changed in this diff
Reference in new issue
xxxxxxxxxx