Austin Huang
4 years ago
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
8 changed files with 28 additions and 790 deletions
-
111app/src/main/java/awais/instagrabber/adapters/MediaItemsAdapter.java
-
194app/src/main/java/awais/instagrabber/dialogs/MediaPickerBottomDialogFragment.java
-
41app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
-
11app/src/main/java/awais/instagrabber/managers/ThreadManager.kt
-
386app/src/main/java/awais/instagrabber/utils/MediaController.java
-
4app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt
-
41app/src/main/java/awais/instagrabber/viewmodels/MediaPickerViewModel.java
-
30app/src/main/res/layout/layout_media_picker.xml
@ -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); |
|||
} |
|||
} |
@ -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(); |
|||
} |
|||
} |
@ -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> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue