mirror of
				https://github.com/KokaKiwi/BarInsta
				synced 2025-10-30 19:15:35 +00:00 
			
		
		
		
	close #1447
This commit is contained in:
		
							parent
							
								
									9c811a6291
								
							
						
					
					
						commit
						5e016e3210
					
				| @ -1,111 +0,0 @@ | ||||
| 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,194 +0,0 @@ | ||||
| package awais.instagrabber.dialogs; | ||||
| 
 | ||||
| import android.app.Dialog; | ||||
| import android.content.Context; | ||||
| import android.os.Bundle; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.AdapterView; | ||||
| import android.widget.ArrayAdapter; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.fragment.app.DialogFragment; | ||||
| import androidx.lifecycle.ViewModelProvider; | ||||
| import androidx.recyclerview.widget.GridLayoutManager; | ||||
| 
 | ||||
| import com.google.android.material.bottomsheet.BottomSheetDialog; | ||||
| import com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
| 
 | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import awais.instagrabber.R; | ||||
| import awais.instagrabber.adapters.MediaItemsAdapter; | ||||
| import awais.instagrabber.databinding.LayoutMediaPickerBinding; | ||||
| import awais.instagrabber.utils.MediaController; | ||||
| import awais.instagrabber.utils.PermissionUtils; | ||||
| import awais.instagrabber.utils.TextUtils; | ||||
| import awais.instagrabber.viewmodels.MediaPickerViewModel; | ||||
| 
 | ||||
| public class MediaPickerBottomDialogFragment extends BottomSheetDialogFragment { | ||||
|     private static final String TAG = MediaPickerBottomDialogFragment.class.getSimpleName(); | ||||
|     private static final int ATTACH_MEDIA_REQUEST_CODE = 100; | ||||
|     // private static final int HEIGHT_PIXELS = Utils.displayMetrics.heightPixels; | ||||
| 
 | ||||
|     private LayoutMediaPickerBinding binding; | ||||
|     private MediaPickerViewModel viewModel; | ||||
|     private MediaItemsAdapter mediaItemsAdapter; | ||||
|     private OnSelectListener onSelectListener; | ||||
|     // private int actionBarHeight; | ||||
|     // private int statusBarHeight; | ||||
| 
 | ||||
|     // private final BottomSheetBehavior.BottomSheetCallback bottomSheetCallback = new BottomSheetBehavior.BottomSheetCallback() { | ||||
|     //     @Override | ||||
|     //     public void onStateChanged(@NonNull final View bottomSheet, final int newState) { | ||||
|     // | ||||
|     //     } | ||||
|     // | ||||
|     //     @Override | ||||
|     //     public void onSlide(@NonNull final View bottomSheet, final float slideOffset) { | ||||
|     //         // Log.d(TAG, "onSlide: " + slideOffset); | ||||
|     //         final float sheetHeight = HEIGHT_PIXELS * slideOffset; | ||||
|     //         final Context context = getContext(); | ||||
|     //         if (context == null) return; | ||||
|     //         final float remaining = HEIGHT_PIXELS - sheetHeight - statusBarHeight; | ||||
|     //         if (remaining <= actionBarHeight) { | ||||
|     //             final ViewGroup.LayoutParams layoutParams = binding.toolbar.getLayoutParams(); | ||||
|     //             layoutParams.height = (int) (actionBarHeight - remaining); | ||||
|     //             binding.toolbar.requestLayout(); | ||||
|     //         } | ||||
|     //     } | ||||
|     // }; | ||||
| 
 | ||||
|     public static MediaPickerBottomDialogFragment newInstance() { | ||||
|         final Bundle args = new Bundle(); | ||||
|         final MediaPickerBottomDialogFragment fragment = new MediaPickerBottomDialogFragment(); | ||||
|         fragment.setArguments(args); | ||||
|         return fragment; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onCreate(@Nullable final Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setStyle(DialogFragment.STYLE_NORMAL, R.style.ThemeOverlay_Rounded_BottomSheetDialog); | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { | ||||
|         binding = LayoutMediaPickerBinding.inflate(inflater, container, false); | ||||
|         viewModel = new ViewModelProvider(this).get(MediaPickerViewModel.class); | ||||
|         // final Context context = getContext(); | ||||
|         // if (context == null) return binding.getRoot(); | ||||
|         // actionBarHeight = Utils.getActionBarHeight(context); | ||||
|         // statusBarHeight = Utils.getStatusBarHeight(context); | ||||
|         return binding.getRoot(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { | ||||
|         init(); | ||||
|         // final Dialog dialog = getDialog(); | ||||
|         // if (dialog == null) return; | ||||
|         // dialog.setOnShowListener(dialog1 -> { | ||||
|         //     final BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialog; | ||||
|         //     final View bottomSheetInternal = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet); | ||||
|         //     if (bottomSheetInternal == null) return; | ||||
|         //     final BottomSheetBehavior<View> behavior = BottomSheetBehavior.from(bottomSheetInternal); | ||||
|         //     behavior.setState(BottomSheetBehavior.STATE_EXPANDED); | ||||
|         // }); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onStart() { | ||||
|         super.onStart(); | ||||
|         final Dialog dialog = getDialog(); | ||||
|         if (dialog == null) return; | ||||
|         final BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialog; | ||||
|         final View bottomSheetInternal = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet); | ||||
|         if (bottomSheetInternal == null) return; | ||||
|         bottomSheetInternal.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT; | ||||
|         bottomSheetInternal.requestLayout(); | ||||
|         // final BottomSheetBehavior<View> behavior = BottomSheetBehavior.from(bottomSheetInternal); | ||||
|         // behavior.addBottomSheetCallback(bottomSheetCallback); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) { | ||||
|         if (requestCode == ATTACH_MEDIA_REQUEST_CODE) { | ||||
|             final Context context = getContext(); | ||||
|             if (context == null) return; | ||||
|             final boolean hasAttachMediaPerms = PermissionUtils.hasAttachMediaPerms(context); | ||||
|             if (hasAttachMediaPerms) { | ||||
|                 viewModel.loadMedia(context); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void init() { | ||||
|         setupList(); | ||||
|         setupAlbumPicker(); | ||||
|         final Context context = getContext(); | ||||
|         if (context == null) return; | ||||
|         if (!PermissionUtils.hasAttachMediaPerms(context)) { | ||||
|             PermissionUtils.requestAttachMediaPerms(this, ATTACH_MEDIA_REQUEST_CODE); | ||||
|             return; | ||||
|         } | ||||
|         viewModel.loadMedia(context); | ||||
|     } | ||||
| 
 | ||||
|     private void setupList() { | ||||
|         final Context context = getContext(); | ||||
|         if (context == null) return; | ||||
|         binding.mediaList.setLayoutManager(new GridLayoutManager(context, 3)); | ||||
|         binding.mediaList.setHasFixedSize(true); | ||||
|         mediaItemsAdapter = new MediaItemsAdapter(entry -> { | ||||
|             if (onSelectListener == null) return; | ||||
|             onSelectListener.onSelect(entry); | ||||
|         }); | ||||
|         binding.mediaList.setAdapter(mediaItemsAdapter); | ||||
|     } | ||||
| 
 | ||||
|     private void setupAlbumPicker() { | ||||
|         final Context context = getContext(); | ||||
|         if (context == null) return; | ||||
|         final ArrayAdapter<String> albumPickerAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item); | ||||
|         albumPickerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); | ||||
|         binding.albumPicker.setAdapter(albumPickerAdapter); | ||||
|         binding.albumPicker.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { | ||||
|             @Override | ||||
|             public void onItemSelected(final AdapterView<?> parent, final View view, final int position, final long id) { | ||||
|                 final List<MediaController.AlbumEntry> albumEntries = viewModel.getAllAlbums().getValue(); | ||||
|                 if (albumEntries == null) return; | ||||
|                 final MediaController.AlbumEntry albumEntry = albumEntries.get(position); | ||||
|                 mediaItemsAdapter.submitList(albumEntry.photos, () -> binding.mediaList.scrollToPosition(0)); | ||||
|             } | ||||
| 
 | ||||
|             @Override | ||||
|             public void onNothingSelected(final AdapterView<?> parent) { | ||||
|                 mediaItemsAdapter.submitList(Collections.emptyList()); | ||||
|             } | ||||
|         }); | ||||
|         viewModel.getAllAlbums().observe(getViewLifecycleOwner(), albums -> { | ||||
|             albumPickerAdapter.clear(); | ||||
|             albumPickerAdapter.addAll(albums.stream() | ||||
|                                             .map(albumEntry -> albumEntry.bucketName) | ||||
|                                             .filter(name -> !TextUtils.isEmpty(name)) | ||||
|                                             .collect(Collectors.toList())); | ||||
|             albumPickerAdapter.notifyDataSetChanged(); | ||||
|             if (albums.isEmpty()) return; | ||||
|             mediaItemsAdapter.submitList(albums.get(0).photos); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     public void setOnSelectListener(final OnSelectListener onSelectListener) { | ||||
|         this.onSelectListener = onSelectListener; | ||||
|     } | ||||
| 
 | ||||
|     public interface OnSelectListener { | ||||
|         void onSelect(MediaController.MediaEntry entry); | ||||
|     } | ||||
| } | ||||
| @ -8,11 +8,11 @@ import android.annotation.SuppressLint; | ||||
| import android.app.Activity; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.graphics.drawable.Animatable; | ||||
| import android.graphics.drawable.Drawable; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| import android.provider.DocumentsContract; | ||||
| import android.text.Editable; | ||||
| import android.util.Log; | ||||
| import android.util.Pair; | ||||
| @ -32,7 +32,6 @@ import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.appcompat.app.ActionBar; | ||||
| import androidx.appcompat.view.menu.ActionMenuItemView; | ||||
| import androidx.core.content.ContextCompat; | ||||
| import androidx.core.view.ViewCompat; | ||||
| import androidx.core.view.WindowInsetsAnimationCompat; | ||||
| import androidx.core.view.WindowInsetsAnimationControlListenerCompat; | ||||
| @ -97,7 +96,6 @@ import awais.instagrabber.customviews.helpers.TranslateDeferringInsetsAnimationC | ||||
| import awais.instagrabber.databinding.FragmentDirectMessagesThreadBinding; | ||||
| import awais.instagrabber.dialogs.DirectItemReactionDialogFragment; | ||||
| import awais.instagrabber.dialogs.GifPickerBottomDialogFragment; | ||||
| import awais.instagrabber.dialogs.MediaPickerBottomDialogFragment; | ||||
| import awais.instagrabber.fragments.PostViewV2Fragment; | ||||
| import awais.instagrabber.fragments.UserSearchFragment; | ||||
| import awais.instagrabber.fragments.UserSearchFragmentDirections; | ||||
| @ -130,6 +128,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact | ||||
|     private static final String TAG = DirectMessageThreadFragment.class.getSimpleName(); | ||||
|     private static final int AUDIO_RECORD_PERM_REQUEST_CODE = 1000; | ||||
|     private static final int CAMERA_REQUEST_CODE = 200; | ||||
|     private static final int FILE_PICKER_REQUEST_CODE = 500; | ||||
|     private static final String TRANSLATION_Y = "translationY"; | ||||
| 
 | ||||
|     private DirectItemsAdapter itemsAdapter; | ||||
| @ -474,6 +473,24 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact | ||||
|     @Override | ||||
|     public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { | ||||
|         super.onActivityResult(requestCode, resultCode, data); | ||||
|         if (requestCode == FILE_PICKER_REQUEST_CODE && resultCode == Activity.RESULT_OK) { | ||||
|             if (data == null || data.getData() == null) { | ||||
|                 Log.w(TAG, "data is null!"); | ||||
|                 return; | ||||
|             } | ||||
|             final Context context = getContext(); | ||||
|             if (context == null) { | ||||
|                 Log.w(TAG, "conetxt is null!"); | ||||
|                 return; | ||||
|             } | ||||
|             final Uri uri = data.getData(); | ||||
|             final String mimeType = Utils.getMimeType(uri, context.getContentResolver()); | ||||
|             if (mimeType.startsWith("image")) { | ||||
|                 navigateToImageEditFragment(uri); | ||||
|                 return; | ||||
|             } | ||||
|             handleSentMessage(viewModel.sendUri(uri)); | ||||
|         } | ||||
|         if (requestCode == CAMERA_REQUEST_CODE && resultCode == Activity.RESULT_OK) { | ||||
|             if (data == null || data.getData() == null) { | ||||
|                 Log.w(TAG, "data is null!"); | ||||
| @ -1109,17 +1126,15 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact | ||||
|         setupInsetsCallback(); | ||||
|         setupEmojiPicker(); | ||||
|         binding.gallery.setOnClickListener(v -> { | ||||
|             final MediaPickerBottomDialogFragment mediaPicker = MediaPickerBottomDialogFragment.newInstance(); | ||||
|             mediaPicker.setOnSelectListener(entry -> { | ||||
|                 mediaPicker.dismiss(); | ||||
|                 if (!isAdded()) return; | ||||
|                 if (!entry.isVideo) { | ||||
|                     navigateToImageEditFragment(entry.path); | ||||
|                     return; | ||||
|                 } | ||||
|                 handleSentMessage(viewModel.sendUri(entry)); | ||||
|             final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); | ||||
|             intent.setType("*/*"); | ||||
|             intent.addCategory(Intent.CATEGORY_OPENABLE); | ||||
|             intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); | ||||
|             intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{ | ||||
|                     "image/*", | ||||
|                     "video/mp4" | ||||
|             }); | ||||
|             mediaPicker.show(getChildFragmentManager(), "MediaPicker"); | ||||
|             startActivityForResult(intent, FILE_PICKER_REQUEST_CODE); | ||||
|         }); | ||||
|         binding.gif.setOnClickListener(v -> { | ||||
|             final GifPickerBottomDialogFragment gifPicker = GifPickerBottomDialogFragment.newInstance(); | ||||
|  | ||||
| @ -367,17 +367,6 @@ class ThreadManager( | ||||
|         return data | ||||
|     } | ||||
| 
 | ||||
|     fun sendUri(entry: MediaController.MediaEntry, scope: CoroutineScope): LiveData<Resource<Any?>> { | ||||
|         val data = MutableLiveData<Resource<Any?>>() | ||||
|         val uri = Uri.fromFile(File(entry.path)) | ||||
|         if (!entry.isVideo) { | ||||
|             sendPhoto(data, uri, entry.width, entry.height, scope) | ||||
|             return data | ||||
|         } | ||||
|         sendVideo(data, uri, entry.size, entry.duration, entry.width, entry.height, scope) | ||||
|         return data | ||||
|     } | ||||
| 
 | ||||
|     fun sendUri(uri: Uri, scope: CoroutineScope): LiveData<Resource<Any?>> { | ||||
|         val data = MutableLiveData<Resource<Any?>>() | ||||
|         val mimeType = Utils.getMimeType(uri, contentResolver) | ||||
|  | ||||
| @ -1,386 +0,0 @@ | ||||
| package awais.instagrabber.utils; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.database.Cursor; | ||||
| import android.os.Build; | ||||
| import android.os.Environment; | ||||
| import android.provider.MediaStore; | ||||
| import android.util.Log; | ||||
| import android.util.SparseArray; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| 
 | ||||
| import awais.instagrabber.R; | ||||
| 
 | ||||
| /* | ||||
|  * This is the source code of Telegram for Android v. 1.3.x. | ||||
|  * It is licensed under GNU GPL v. 2 or later. | ||||
|  * You should have received a copy of the license in this archive (see LICENSE). | ||||
|  * | ||||
|  * Copyright Nikolai Kudashov, 2013-2018. | ||||
|  */ | ||||
| public class MediaController { | ||||
|     private static final String TAG = MediaController.class.getSimpleName(); | ||||
|     private static final String[] PROJECTION_PHOTOS = { | ||||
|             MediaStore.Images.Media._ID, | ||||
|             MediaStore.Images.Media.BUCKET_ID, | ||||
|             MediaStore.Images.Media.BUCKET_DISPLAY_NAME, | ||||
|             MediaStore.Images.Media.DATA, | ||||
|             Build.VERSION.SDK_INT > 28 ? MediaStore.Images.Media.DATE_TAKEN : MediaStore.Images.Media.DATE_MODIFIED, | ||||
|             MediaStore.Images.Media.ORIENTATION, | ||||
|             MediaStore.Images.Media.WIDTH, | ||||
|             MediaStore.Images.Media.HEIGHT, | ||||
|             MediaStore.Images.Media.SIZE | ||||
|     }; | ||||
|     private static final String[] PROJECTION_VIDEO = { | ||||
|             MediaStore.Video.Media._ID, | ||||
|             MediaStore.Video.Media.BUCKET_ID, | ||||
|             MediaStore.Video.Media.BUCKET_DISPLAY_NAME, | ||||
|             MediaStore.Video.Media.DATA, | ||||
|             Build.VERSION.SDK_INT > 28 ? MediaStore.Video.Media.DATE_TAKEN : MediaStore.Images.Media.DATE_MODIFIED, | ||||
|             MediaStore.Video.Media.DURATION, | ||||
|             MediaStore.Video.Media.WIDTH, | ||||
|             MediaStore.Video.Media.HEIGHT, | ||||
|             MediaStore.Video.Media.SIZE | ||||
|     }; | ||||
| 
 | ||||
|     private final Context context; | ||||
|     private final OnLoadListener onLoadListener; | ||||
|     private final AppExecutors appExecutors; | ||||
| 
 | ||||
|     private static Runnable broadcastPhotosRunnable; | ||||
| 
 | ||||
|     private ArrayList<AlbumEntry> allMediaAlbums; | ||||
|     private ArrayList<AlbumEntry> allPhotoAlbums; | ||||
|     private AlbumEntry allPhotosAlbumEntry; | ||||
|     private AlbumEntry allMediaAlbumEntry; | ||||
|     private AlbumEntry allVideosAlbumEntry; | ||||
| 
 | ||||
|     public MediaController(final Context context, final OnLoadListener onLoadListener) { | ||||
|         this.context = context; | ||||
|         this.onLoadListener = onLoadListener; | ||||
|         appExecutors = AppExecutors.INSTANCE; | ||||
|     } | ||||
| 
 | ||||
|     public void load() { | ||||
|         loadGalleryAlbums(); | ||||
|     } | ||||
| 
 | ||||
|     private void loadGalleryAlbums() { | ||||
|         final Thread thread = new Thread(() -> { | ||||
|             final ArrayList<AlbumEntry> mediaAlbumsSorted = new ArrayList<>(); | ||||
|             final ArrayList<AlbumEntry> photoAlbumsSorted = new ArrayList<>(); | ||||
|             SparseArray<AlbumEntry> mediaAlbums = new SparseArray<>(); | ||||
|             SparseArray<AlbumEntry> photoAlbums = new SparseArray<>(); | ||||
|             AlbumEntry allPhotosAlbum = null; | ||||
|             AlbumEntry allVideosAlbum = null; | ||||
|             AlbumEntry allMediaAlbum = null; | ||||
|             String cameraFolder = null; | ||||
|             try { | ||||
|                 cameraFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath() + "/" + "Camera/"; | ||||
|             } catch (Exception e) { | ||||
|                 Log.e(TAG, "loadGalleryAlbums: ", e); | ||||
|             } | ||||
|             Integer mediaCameraAlbumId = null; | ||||
|             Integer photoCameraAlbumId = null; | ||||
| 
 | ||||
|             Cursor cursor = null; | ||||
|             try { | ||||
|                 if (PermissionUtils.hasAttachMediaPerms(context)) { | ||||
|                     cursor = MediaStore.Images.Media.query(context.getContentResolver(), | ||||
|                                                            MediaStore.Images.Media.EXTERNAL_CONTENT_URI, | ||||
|                                                            PROJECTION_PHOTOS, | ||||
|                                                            null, | ||||
|                                                            null, | ||||
|                                                            (Build.VERSION.SDK_INT > 28 | ||||
|                                                             ? MediaStore.Images.Media.DATE_TAKEN | ||||
|                                                             : MediaStore.Images.Media.DATE_MODIFIED) + " DESC"); | ||||
|                     if (cursor != null) { | ||||
|                         int imageIdColumn = cursor.getColumnIndex(MediaStore.Images.Media._ID); | ||||
|                         int bucketIdColumn = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_ID); | ||||
|                         int bucketNameColumn = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME); | ||||
|                         int dataColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATA); | ||||
|                         int dateColumn = cursor.getColumnIndex(Build.VERSION.SDK_INT > 28 ? MediaStore.Images.Media.DATE_TAKEN | ||||
|                                                                                           : MediaStore.Images.Media.DATE_MODIFIED); | ||||
|                         int orientationColumn = cursor.getColumnIndex(MediaStore.Images.Media.ORIENTATION); | ||||
|                         int widthColumn = cursor.getColumnIndex(MediaStore.Images.Media.WIDTH); | ||||
|                         int heightColumn = cursor.getColumnIndex(MediaStore.Images.Media.HEIGHT); | ||||
|                         int sizeColumn = cursor.getColumnIndex(MediaStore.Images.Media.SIZE); | ||||
| 
 | ||||
|                         while (cursor.moveToNext()) { | ||||
|                             String path = cursor.getString(dataColumn); | ||||
|                             if (TextUtils.isEmpty(path)) { | ||||
|                                 continue; | ||||
|                             } | ||||
| 
 | ||||
|                             int imageId = cursor.getInt(imageIdColumn); | ||||
|                             int bucketId = cursor.getInt(bucketIdColumn); | ||||
|                             String bucketName = cursor.getString(bucketNameColumn); | ||||
|                             long dateTaken = cursor.getLong(dateColumn); | ||||
|                             int orientation = cursor.getInt(orientationColumn); | ||||
|                             int width = cursor.getInt(widthColumn); | ||||
|                             int height = cursor.getInt(heightColumn); | ||||
|                             long size = cursor.getLong(sizeColumn); | ||||
| 
 | ||||
|                             MediaEntry mediaEntry = new MediaEntry(bucketId, imageId, dateTaken, path, orientation, -1, false, width, height, size); | ||||
| 
 | ||||
|                             if (allPhotosAlbum == null) { | ||||
|                                 allPhotosAlbum = new AlbumEntry(0, context.getString(R.string.all_photos), mediaEntry); | ||||
|                                 photoAlbumsSorted.add(0, allPhotosAlbum); | ||||
|                             } | ||||
|                             if (allMediaAlbum == null) { | ||||
|                                 allMediaAlbum = new AlbumEntry(0, context.getString(R.string.all_media), mediaEntry); | ||||
|                                 mediaAlbumsSorted.add(0, allMediaAlbum); | ||||
|                             } | ||||
|                             allPhotosAlbum.addPhoto(mediaEntry); | ||||
|                             allMediaAlbum.addPhoto(mediaEntry); | ||||
| 
 | ||||
|                             AlbumEntry albumEntry = mediaAlbums.get(bucketId); | ||||
|                             if (albumEntry == null) { | ||||
|                                 albumEntry = new AlbumEntry(bucketId, bucketName, mediaEntry); | ||||
|                                 mediaAlbums.put(bucketId, albumEntry); | ||||
|                                 if (mediaCameraAlbumId == null && cameraFolder != null && path.startsWith(cameraFolder)) { | ||||
|                                     mediaAlbumsSorted.add(0, albumEntry); | ||||
|                                     mediaCameraAlbumId = bucketId; | ||||
|                                 } else { | ||||
|                                     mediaAlbumsSorted.add(albumEntry); | ||||
|                                 } | ||||
|                             } | ||||
|                             albumEntry.addPhoto(mediaEntry); | ||||
| 
 | ||||
|                             albumEntry = photoAlbums.get(bucketId); | ||||
|                             if (albumEntry == null) { | ||||
|                                 albumEntry = new AlbumEntry(bucketId, bucketName, mediaEntry); | ||||
|                                 photoAlbums.put(bucketId, albumEntry); | ||||
|                                 if (photoCameraAlbumId == null && cameraFolder != null && path.startsWith(cameraFolder)) { | ||||
|                                     photoAlbumsSorted.add(0, albumEntry); | ||||
|                                     photoCameraAlbumId = bucketId; | ||||
|                                 } else { | ||||
|                                     photoAlbumsSorted.add(albumEntry); | ||||
|                                 } | ||||
|                             } | ||||
|                             albumEntry.addPhoto(mediaEntry); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } catch (Throwable e) { | ||||
|                 Log.e(TAG, "loadGalleryAlbums: ", e); | ||||
|             } finally { | ||||
|                 if (cursor != null) { | ||||
|                     try { | ||||
|                         cursor.close(); | ||||
|                     } catch (Exception e) { | ||||
|                         Log.e(TAG, "loadGalleryAlbums: ", e); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             try { | ||||
|                 if (PermissionUtils.hasAttachMediaPerms(context)) { | ||||
|                     cursor = MediaStore.Images.Media.query(context.getContentResolver(), | ||||
|                                                            MediaStore.Video.Media.EXTERNAL_CONTENT_URI, | ||||
|                                                            PROJECTION_VIDEO, | ||||
|                                                            MediaStore.Video.Media.MIME_TYPE + "=?", | ||||
|                                                            new String[]{"video/mp4"}, | ||||
|                                                            (Build.VERSION.SDK_INT > 28 | ||||
|                                                             ? MediaStore.Video.Media.DATE_TAKEN | ||||
|                                                             : MediaStore.Video.Media.DATE_MODIFIED) + " DESC"); | ||||
|                     if (cursor != null) { | ||||
|                         int imageIdColumn = cursor.getColumnIndex(MediaStore.Video.Media._ID); | ||||
|                         int bucketIdColumn = cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_ID); | ||||
|                         int bucketNameColumn = cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_DISPLAY_NAME); | ||||
|                         int dataColumn = cursor.getColumnIndex(MediaStore.Video.Media.DATA); | ||||
|                         int dateColumn = cursor.getColumnIndex(Build.VERSION.SDK_INT > 28 ? MediaStore.Video.Media.DATE_TAKEN | ||||
|                                                                                           : MediaStore.Video.Media.DATE_MODIFIED); | ||||
|                         int durationColumn = cursor.getColumnIndex(MediaStore.Video.Media.DURATION); | ||||
|                         int widthColumn = cursor.getColumnIndex(MediaStore.Video.Media.WIDTH); | ||||
|                         int heightColumn = cursor.getColumnIndex(MediaStore.Video.Media.HEIGHT); | ||||
|                         int sizeColumn = cursor.getColumnIndex(MediaStore.Video.Media.SIZE); | ||||
| 
 | ||||
|                         while (cursor.moveToNext()) { | ||||
|                             String path = cursor.getString(dataColumn); | ||||
|                             if (TextUtils.isEmpty(path)) { | ||||
|                                 continue; | ||||
|                             } | ||||
| 
 | ||||
|                             int imageId = cursor.getInt(imageIdColumn); | ||||
|                             int bucketId = cursor.getInt(bucketIdColumn); | ||||
|                             String bucketName = cursor.getString(bucketNameColumn); | ||||
|                             long dateTaken = cursor.getLong(dateColumn); | ||||
|                             long duration = cursor.getLong(durationColumn); | ||||
|                             int width = cursor.getInt(widthColumn); | ||||
|                             int height = cursor.getInt(heightColumn); | ||||
|                             long size = cursor.getLong(sizeColumn); | ||||
| 
 | ||||
|                             MediaEntry mediaEntry = new MediaEntry(bucketId, imageId, dateTaken, path, -1, duration, true, width, height, size); | ||||
| 
 | ||||
|                             if (allVideosAlbum == null) { | ||||
|                                 allVideosAlbum = new AlbumEntry(0, context.getString(R.string.all_videos), mediaEntry); | ||||
|                                 allVideosAlbum.videoOnly = true; | ||||
|                                 int index = 0; | ||||
|                                 if (allMediaAlbum != null) { | ||||
|                                     index++; | ||||
|                                 } | ||||
|                                 if (allPhotosAlbum != null) { | ||||
|                                     index++; | ||||
|                                 } | ||||
|                                 mediaAlbumsSorted.add(index, allVideosAlbum); | ||||
|                             } | ||||
|                             if (allMediaAlbum == null) { | ||||
|                                 allMediaAlbum = new AlbumEntry(0, context.getString(R.string.all_media), mediaEntry); | ||||
|                                 mediaAlbumsSorted.add(0, allMediaAlbum); | ||||
|                             } | ||||
|                             allVideosAlbum.addPhoto(mediaEntry); | ||||
|                             allMediaAlbum.addPhoto(mediaEntry); | ||||
| 
 | ||||
|                             AlbumEntry albumEntry = mediaAlbums.get(bucketId); | ||||
|                             if (albumEntry == null) { | ||||
|                                 albumEntry = new AlbumEntry(bucketId, bucketName, mediaEntry); | ||||
|                                 mediaAlbums.put(bucketId, albumEntry); | ||||
|                                 if (mediaCameraAlbumId == null && cameraFolder != null && path.startsWith(cameraFolder)) { | ||||
|                                     mediaAlbumsSorted.add(0, albumEntry); | ||||
|                                     mediaCameraAlbumId = bucketId; | ||||
|                                 } else { | ||||
|                                     mediaAlbumsSorted.add(albumEntry); | ||||
|                                 } | ||||
|                             } | ||||
| 
 | ||||
|                             albumEntry.addPhoto(mediaEntry); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } catch (Throwable e) { | ||||
|                 Log.e(TAG, "loadGalleryAlbums: ", e); | ||||
|             } finally { | ||||
|                 if (cursor != null) { | ||||
|                     try { | ||||
|                         cursor.close(); | ||||
|                     } catch (Exception e) { | ||||
|                         Log.e(TAG, "loadGalleryAlbums: ", e); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             for (int a = 0; a < mediaAlbumsSorted.size(); a++) { | ||||
|                 Collections.sort(mediaAlbumsSorted.get(a).photos, (o1, o2) -> { | ||||
|                     if (o1.dateTaken < o2.dateTaken) { | ||||
|                         return 1; | ||||
|                     } else if (o1.dateTaken > o2.dateTaken) { | ||||
|                         return -1; | ||||
|                     } | ||||
|                     return 0; | ||||
|                 }); | ||||
|             } | ||||
|             broadcastNewPhotos(mediaAlbumsSorted, photoAlbumsSorted, mediaCameraAlbumId, allMediaAlbum, allPhotosAlbum, allVideosAlbum, 0); | ||||
|         }); | ||||
|         thread.setPriority(Thread.MIN_PRIORITY); | ||||
|         thread.start(); | ||||
|     } | ||||
| 
 | ||||
|     private void broadcastNewPhotos(final ArrayList<AlbumEntry> mediaAlbumsSorted, | ||||
|                                     final ArrayList<AlbumEntry> photoAlbumsSorted, | ||||
|                                     final Integer cameraAlbumIdFinal, | ||||
|                                     final AlbumEntry allMediaAlbumFinal, | ||||
|                                     final AlbumEntry allPhotosAlbumFinal, | ||||
|                                     final AlbumEntry allVideosAlbumFinal, | ||||
|                                     int delay) { | ||||
|         if (broadcastPhotosRunnable != null) { | ||||
|             appExecutors.getMainThread().cancel(broadcastPhotosRunnable); | ||||
|         } | ||||
|         appExecutors.getMainThread().execute(broadcastPhotosRunnable = () -> { | ||||
|             allMediaAlbums = mediaAlbumsSorted; | ||||
|             allPhotoAlbums = photoAlbumsSorted; | ||||
|             broadcastPhotosRunnable = null; | ||||
|             allPhotosAlbumEntry = allPhotosAlbumFinal; | ||||
|             allMediaAlbumEntry = allMediaAlbumFinal; | ||||
|             allVideosAlbumEntry = allVideosAlbumFinal; | ||||
|             if (onLoadListener != null) { | ||||
|                 onLoadListener.onLoad(); | ||||
|             } | ||||
|         }, delay); | ||||
|     } | ||||
| 
 | ||||
|     public AlbumEntry getAllMediaAlbumEntry() { | ||||
|         return allMediaAlbumEntry; | ||||
|     } | ||||
| 
 | ||||
|     public AlbumEntry getAllPhotosAlbumEntry() { | ||||
|         return allPhotosAlbumEntry; | ||||
|     } | ||||
| 
 | ||||
|     public AlbumEntry getAllVideosAlbumEntry() { | ||||
|         return allVideosAlbumEntry; | ||||
|     } | ||||
| 
 | ||||
|     public ArrayList<AlbumEntry> getAllMediaAlbums() { | ||||
|         return allMediaAlbums; | ||||
|     } | ||||
| 
 | ||||
|     public ArrayList<AlbumEntry> getAllPhotoAlbums() { | ||||
|         return allPhotoAlbums; | ||||
|     } | ||||
| 
 | ||||
|     public static class AlbumEntry { | ||||
|         public int bucketId; | ||||
|         public boolean videoOnly; | ||||
|         public String bucketName; | ||||
|         public MediaEntry coverPhoto; | ||||
|         public ArrayList<MediaEntry> photos = new ArrayList<>(); | ||||
|         public SparseArray<MediaEntry> photosByIds = new SparseArray<>(); | ||||
| 
 | ||||
|         public AlbumEntry(int bucketId, String bucketName, MediaEntry coverPhoto) { | ||||
|             this.bucketId = bucketId; | ||||
|             this.bucketName = bucketName; | ||||
|             this.coverPhoto = coverPhoto; | ||||
|         } | ||||
| 
 | ||||
|         public void addPhoto(MediaEntry mediaEntry) { | ||||
|             photos.add(mediaEntry); | ||||
|             photosByIds.put(mediaEntry.imageId, mediaEntry); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static class MediaEntry { | ||||
|         public int bucketId; | ||||
|         public int imageId; | ||||
|         public long dateTaken; | ||||
|         public long duration; | ||||
|         public int width; | ||||
|         public int height; | ||||
|         public long size; | ||||
|         public String path; | ||||
|         public int orientation; | ||||
|         public boolean isVideo; | ||||
|         public boolean isMuted; | ||||
|         public boolean canDeleteAfter; | ||||
| 
 | ||||
|         public MediaEntry(int bucketId, | ||||
|                           int imageId, | ||||
|                           long dateTaken, | ||||
|                           String path, | ||||
|                           int orientation, | ||||
|                           long duration, | ||||
|                           boolean isVideo, | ||||
|                           int width, | ||||
|                           int height, | ||||
|                           long size) { | ||||
|             this.bucketId = bucketId; | ||||
|             this.imageId = imageId; | ||||
|             this.dateTaken = dateTaken; | ||||
|             this.path = path; | ||||
|             this.width = width; | ||||
|             this.height = height; | ||||
|             this.size = size; | ||||
|             if (isVideo) { | ||||
|                 this.duration = duration; | ||||
|             } else { | ||||
|                 this.orientation = orientation; | ||||
|             } | ||||
|             this.isVideo = isVideo; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public interface OnLoadListener { | ||||
|         void onLoad(); | ||||
|     } | ||||
| } | ||||
| @ -76,10 +76,6 @@ class DirectThreadViewModel( | ||||
|         return threadManager.sendText(text, viewModelScope) | ||||
|     } | ||||
| 
 | ||||
|     fun sendUri(entry: MediaController.MediaEntry): LiveData<Resource<Any?>> { | ||||
|         return threadManager.sendUri(entry, viewModelScope) | ||||
|     } | ||||
| 
 | ||||
|     fun sendUri(uri: Uri): LiveData<Resource<Any?>> { | ||||
|         return threadManager.sendUri(uri, viewModelScope) | ||||
|     } | ||||
|  | ||||
| @ -1,41 +0,0 @@ | ||||
| package awais.instagrabber.viewmodels; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| 
 | ||||
| import androidx.lifecycle.LiveData; | ||||
| import androidx.lifecycle.MutableLiveData; | ||||
| import androidx.lifecycle.ViewModel; | ||||
| 
 | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import awais.instagrabber.utils.MediaController; | ||||
| import awais.instagrabber.utils.MediaController.AlbumEntry; | ||||
| 
 | ||||
| public class MediaPickerViewModel extends ViewModel implements MediaController.OnLoadListener { | ||||
|     private final MutableLiveData<List<AlbumEntry>> allAlbums = new MutableLiveData<>(Collections.emptyList()); | ||||
| 
 | ||||
|     private MediaController mediaController; | ||||
| 
 | ||||
|     public MediaPickerViewModel() { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public void loadMedia(final Context context) { | ||||
|         mediaController = new MediaController(context, this); | ||||
|         mediaController.load(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onLoad() { | ||||
|         if (mediaController == null) { | ||||
|             return; | ||||
|         } | ||||
|         final List<AlbumEntry> allPhotoAlbums = mediaController.getAllMediaAlbums(); | ||||
|         this.allAlbums.postValue(allPhotoAlbums); | ||||
|     } | ||||
| 
 | ||||
|     public LiveData<List<AlbumEntry>> getAllAlbums() { | ||||
|         return allAlbums; | ||||
|     } | ||||
| } | ||||
| @ -1,30 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent"> | ||||
| 
 | ||||
|     <androidx.appcompat.widget.Toolbar | ||||
|         android:id="@+id/toolbar" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="?actionBarSize" | ||||
|         app:layout_constraintBottom_toTopOf="@id/media_list" | ||||
|         app:layout_constraintTop_toTopOf="parent"> | ||||
| 
 | ||||
|         <Spinner | ||||
|             android:id="@+id/album_picker" | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="match_parent" /> | ||||
| 
 | ||||
|     </androidx.appcompat.widget.Toolbar> | ||||
| 
 | ||||
|     <androidx.recyclerview.widget.RecyclerView | ||||
|         android:id="@+id/media_list" | ||||
|         android:layout_width="0dp" | ||||
|         android:layout_height="0dp" | ||||
|         app:layout_constraintBottom_toBottomOf="parent" | ||||
|         app:layout_constraintEnd_toEndOf="parent" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintTop_toBottomOf="@id/toolbar" /> | ||||
| 
 | ||||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user