Commit 310ae9d5 authored by Donald's avatar Donald

loaded albums with observables

parent 36bfe2dc
......@@ -22,6 +22,7 @@ android {
disable 'ExtraTranslation'
abortOnError false
}
// This is handled for you by the 2.0+ Gradle Plugin
aaptOptions {
additionalParameters "--no-version-vectors"
......@@ -40,8 +41,8 @@ android {
}
debug {
applicationIdSuffix ".debug"
resValue "string", "app_name", "LeafPic (debug)"
applicationIdSuffix ".debug.observables"
resValue "string", "app_name", "LeafPic (observables)"
}
}
productFlavors {
......@@ -54,7 +55,7 @@ android {
}
}
project.ext.supportLib = "25.2.0"
project.ext.supportLib = "25.3.0"
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
......@@ -85,6 +86,12 @@ dependencies {
compile 'com.github.jetradarmobile:desertplaceholder:1.1.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
// Because RxAndroid releases are few and far between, it is recommended you also
// explicitly depend on RxJava's latest version for bug fixes and new features.
compile 'io.reactivex.rxjava2:rxjava:2.0.1'
compile 'com.jakewharton.rxrelay2:rxrelay:2.0.0'
//Butter Knife
compile 'com.jakewharton:butterknife:8.5.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
......
......@@ -10,6 +10,18 @@ import org.horaapps.leafpic.model.HandlingAlbums;
*/
public class MyApplication extends Application {
private static MyApplication mInstance;
@Override
public void onCreate() {
super.onCreate();
mInstance = this;
}
public static MyApplication getInstance() {
return mInstance;
}
public Album getAlbum() {
return getAlbums().getCount() > 0 ? getAlbums().getCurrentAlbum() : Album.getEmptyAlbum();
}
......
......@@ -32,6 +32,7 @@ import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.SwitchCompat;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
......@@ -50,6 +51,7 @@ import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.iconics.view.IconicsImageView;
import org.horaapps.leafpic.BuildConfig;
import org.horaapps.leafpic.MyApplication;
import org.horaapps.leafpic.R;
import org.horaapps.leafpic.SelectAlbumBuilder;
import org.horaapps.leafpic.activities.base.SharedMediaActivity;
......@@ -61,6 +63,8 @@ import org.horaapps.leafpic.model.Media;
import org.horaapps.leafpic.model.base.FilterMode;
import org.horaapps.leafpic.model.base.SortingMode;
import org.horaapps.leafpic.model.base.SortingOrder;
import org.horaapps.leafpic.new_way.CPHelper;
import org.horaapps.leafpic.new_way.DataManager;
import org.horaapps.leafpic.util.Affix;
import org.horaapps.leafpic.util.AlertDialogsHelper;
import org.horaapps.leafpic.util.Measure;
......@@ -72,6 +76,9 @@ import org.horaapps.leafpic.views.GridSpacingItemDecoration;
import java.util.ArrayList;
import java.util.Locale;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
public class MainActivity extends SharedMediaActivity {
......@@ -215,19 +222,37 @@ public class MainActivity extends SharedMediaActivity {
private void displayAlbums(boolean reload) {
toolbar.setNavigationIcon(getToolbarIcon(GoogleMaterial.Icon.gmd_menu));
toolbar.setTitle(getString(R.string.app_name));
albumsAdapter.clear();
DataManager.getInstance()
.getAlbumsRelay()
.map(album -> album.withMediaObservable(CPHelper.getLastMedia(MyApplication.getInstance(),album.getId())))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
album -> {
album.getMediaObservable().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
media -> album.addMedia(media),
throwable -> {},
() -> albumsAdapter.addAlbum(album));
}, throwable -> {
Log.wtf("asd", throwable.toString());
}, () -> {
albumsAdapter.notifyDataSetChanged();
Log.wtf("asd", "");
swipeRefreshLayout.setRefreshing(false);
});
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
albumsAdapter.swapDataSet(getAlbums().albums);
if (reload) new PrepareAlbumTask().execute();
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) { mDrawerLayout.openDrawer(GravityCompat.START); }
});
toolbar.setNavigationOnClickListener(v -> mDrawerLayout.openDrawer(GravityCompat.START));
albumsMode = true;
editMode = false;
supportInvalidateOptionsMenu();
mediaAdapter.swapDataSet(new ArrayList<Media>());
rvMedia.scrollToPosition(0);
}
......@@ -244,6 +269,13 @@ public class MainActivity extends SharedMediaActivity {
}
private boolean displayData(Intent data){
if (true==!!true) {
toggleRecyclersVisibility(true);
displayAlbums(true);
return true;
}
pickMode = data.getBooleanExtra(SplashScreen.PICK_MODE, false);
switch (data.getIntExtra(SplashScreen.CONTENT, SplashScreen.ALBUMS_BACKUP)) {
case SplashScreen.ALBUMS_PREFETCHED:
......@@ -280,7 +312,8 @@ public class MainActivity extends SharedMediaActivity {
rvMedia.setHasFixedSize(true);
rvMedia.setItemAnimator(new DefaultItemAnimator());
albumsAdapter = new AlbumsAdapter(getAlbums().albums, MainActivity.this);
// TODO: 3/21/17 redo
albumsAdapter = new AlbumsAdapter(new ArrayList<>(), MainActivity.this);
albumsAdapter.setOnClickListener(albumOnClickListener);
albumsAdapter.setOnLongClickListener(albumOnLongCLickListener);
rvAlbums.setAdapter(albumsAdapter);
......@@ -305,8 +338,10 @@ public class MainActivity extends SharedMediaActivity {
@Override
public void onRefresh() {
if (albumsMode) {
getAlbums().clearSelectedAlbums();
new PrepareAlbumTask().execute();
//getAlbums().clearSelectedAlbums();
//new PrepareAlbumTask().execute();
displayAlbums();
swipeRefreshLayout.setRefreshing(false);
} else {
getAlbum().clearSelectedMedia();
new PreparePhotosTask().execute();
......@@ -1264,6 +1299,7 @@ public class MainActivity extends SharedMediaActivity {
}
}
@Deprecated
private class PrepareAlbumTask extends AsyncTask<Void, Integer, Void> {
@Override
......@@ -1288,6 +1324,7 @@ public class MainActivity extends SharedMediaActivity {
}
}
@Deprecated
private class PreparePhotosTask extends AsyncTask<Void, Void, Void> {
@Override
......
......@@ -65,6 +65,10 @@ public class SplashScreen extends SharedMediaActivity {
setStatusBarColor();
if (PermissionUtils.isDeviceInfoGranted(this)) {
// todo no preload
startActivity(new Intent(SplashScreen.this, MainActivity.class));
finish();
if (getIntent().getAction().equals(ACTION_OPEN_ALBUM)) {
Bundle data = getIntent().getExtras();
......@@ -89,22 +93,19 @@ public class SplashScreen extends SharedMediaActivity {
private void startLookingForMedia() {
new Thread(new Runnable() {
@Override
public void run() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && getAlbums().getFoldersCount(HandlingAlbums.INCLUDED) > 0) {
new Thread(() -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && getAlbums().getFoldersCount(HandlingAlbums.INCLUDED) > 0) {
JobInfo job = new JobInfo.Builder(0, new ComponentName(getApplicationContext(), LookForMediaJob.class))
.setPeriodic(1000)
.setRequiresDeviceIdle(true)
.build();
JobInfo job = new JobInfo.Builder(0, new ComponentName(getApplicationContext(), LookForMediaJob.class))
.setPeriodic(1000)
.setRequiresDeviceIdle(true)
.build();
JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
if (scheduler.getAllPendingJobs().size() == 0)
Log.wtf(TAG, scheduler.schedule(job) == JobScheduler.RESULT_SUCCESS
? "LookForMediaJob scheduled successfully!" : "LookForMediaJob scheduled failed!");
JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
if (scheduler.getAllPendingJobs().size() == 0)
Log.wtf(TAG, scheduler.schedule(job) == JobScheduler.RESULT_SUCCESS
? "LookForMediaJob scheduled successfully!" : "LookForMediaJob scheduled failed!");
}
}
}).start();
}
......
......@@ -5,6 +5,7 @@ import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
......@@ -73,6 +74,8 @@ public class AlbumsAdapter extends RecyclerView.Adapter<AlbumsAdapter.ViewHolder
@Override
public void onBindViewHolder(final AlbumsAdapter.ViewHolder holder, int position) {
Album a = albums.get(position);
Log.wtf("asd/holder", a.toString());
Media f = a.getCoverAlbum();
Glide.with(holder.picture.getContext())
......@@ -138,6 +141,17 @@ public class AlbumsAdapter extends RecyclerView.Adapter<AlbumsAdapter.ViewHolder
notifyDataSetChanged();
}
public void clear() {
albums.clear();
notifyDataSetChanged();
}
public void addAlbum(Album album) {
albums.add(album);
//notifyDataSetChanged();
notifyItemInserted(albums.size()-1);
}
@Override
public int getItemCount() {
return albums.size();
......
package org.horaapps.leafpic.fragments;
/**
* Created by dnld on 3/13/17.
*/
public class AlbumsFragment {
}
package org.horaapps.leafpic.model;
import android.content.Context;
import android.database.Cursor;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.util.Log;
import org.horaapps.leafpic.MyApplication;
import org.horaapps.leafpic.adapters.MediaAdapter;
import org.horaapps.leafpic.model.base.FilterMode;
import org.horaapps.leafpic.model.base.MediaComparators;
import org.horaapps.leafpic.model.base.SortingMode;
import org.horaapps.leafpic.model.base.SortingOrder;
import org.horaapps.leafpic.new_way.CursorHandler;
import org.horaapps.leafpic.util.ContentHelper;
import org.horaapps.leafpic.util.PreferenceUtil;
import org.horaapps.leafpic.util.StringUtils;
import java.io.File;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import io.reactivex.Observable;
/**
* Created by dnld on 26/04/16.
*/
public class Album implements Serializable {
public class Album implements Serializable, CursorHandler {
private String name = null;
private String path = null;
......@@ -36,7 +43,45 @@ public class Album implements Serializable {
private ArrayList<Media> media;
private ArrayList<Media> selectedMedia;
private Observable<Media> mediaObservable;
public Album withMediaObservable(Observable<Media> mediaObservable) {
this.mediaObservable = mediaObservable;
return this;
}
public static String[] projection = new String[]{
MediaStore.Files.FileColumns.PARENT,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
"count(*)",
MediaStore.Images.Media.DATA,
};
public Album(Cursor cur) {
this(MyApplication.getInstance(),
StringUtils.getBucketPathByImagePath(cur.getString(3)),
cur.getLong(0), cur.getString(1), cur.getInt(2));
}
@Override
public Album handle(Cursor cur) throws SQLException {
return new Album(cur);
}
@Override
public String toString() {
return "Album{" +
"name='" + name + '\'' +
", path='" + path + '\'' +
", id=" + id +
", count=" + count +
'}';
}
public Observable<Media> getMediaObservable() {
return mediaObservable;
}
public Album(String path, String name) {
media = new ArrayList<>();
......@@ -512,4 +557,6 @@ public class Album implements Serializable {
private void scanFile(Context context, String[] path, MediaScannerConnection.OnScanCompletedListener onScanCompletedListener) {
MediaScannerConnection.scanFile(context, path, null, onScanCompletedListener);
}
}
......@@ -76,6 +76,7 @@ public class ContentProviderHelper {
return list;
}
public static long getAlbumId(Context context, String mediaPath) {
long id = -1;
Cursor cur = context.getContentResolver().query(MediaStore.Files.getContentUri("external"),
......
......@@ -9,6 +9,7 @@ import android.media.ExifInterface;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.MediaStore;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
......@@ -20,6 +21,7 @@ import com.drew.metadata.Tag;
import org.horaapps.leafpic.R;
import org.horaapps.leafpic.model.base.MediaDetailsMap;
import org.horaapps.leafpic.new_way.CursorHandler;
import org.horaapps.leafpic.util.MediaSignature;
import org.horaapps.leafpic.util.StringUtils;
import org.jetbrains.annotations.TestOnly;
......@@ -28,13 +30,14 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Created by dnld on 26/04/16.
*/
public class Media implements Parcelable, Serializable {
public class Media implements Parcelable, Serializable, CursorHandler {
private String path = null;
private long dateModified = -1;
......@@ -49,6 +52,14 @@ public class Media implements Parcelable, Serializable {
public Media() { }
@Override
public String toString() {
return "Media{" +
"path='" + path + '\'' +
", mime='" + mimeType + '\'' +
'}';
}
public Media(String path, long dateModified) {
this.path = path;
this.dateModified = dateModified;
......@@ -79,6 +90,21 @@ public class Media implements Parcelable, Serializable {
this.orientation = cur.getInt(4);
}
@Override
public Media handle(Cursor cu) throws SQLException {
return new Media(cu);
}
public static String[] getProjection() {
return new String[]{
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.DATE_TAKEN,
MediaStore.Images.Media.MIME_TYPE,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media.ORIENTATION
};
}
public void setUri(String uriString) {
this.uriString = uriString;
}
......
package org.horaapps.leafpic.new_way;
import android.content.Context;
import android.database.Cursor;
import android.provider.MediaStore;
import android.util.Log;
import org.horaapps.leafpic.MyApplication;
import org.horaapps.leafpic.model.Album;
import org.horaapps.leafpic.model.Media;
import org.horaapps.leafpic.model.base.FoldersFileFilter;
import org.horaapps.leafpic.model.base.ImageFileFilter;
import org.horaapps.leafpic.util.ContentHelper;
import org.horaapps.leafpic.util.PreferenceUtil;
import org.jetbrains.annotations.TestOnly;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import io.reactivex.Observable;
/**
* Created by dnld on 24/07/16.
*/
public class CPHelper {
public static Observable<Media> getLastMedia(Context context, long albumId) {
return getMedia(context, albumId, 1, true);
}
public static Observable<Media> getMedia(Context context, long albumId, boolean includeVideo) {
return getMedia(context, albumId, -1, includeVideo);
}
public static Observable<Media> getMedia(Context context, long albumId, int n, boolean includeVideo) {
String selection, selectionArgs[];
if(includeVideo) {
selection = String.format("(%s=? or %s=?) and %s=?",
MediaStore.Files.FileColumns.MEDIA_TYPE,
MediaStore.Files.FileColumns.MEDIA_TYPE,
MediaStore.Files.FileColumns.PARENT);
selectionArgs = new String[] {
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE),
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO),
String.valueOf(albumId)
};
} else {
selection = String.format("%s=? and %s=?",
MediaStore.Files.FileColumns.MEDIA_TYPE,
MediaStore.Files.FileColumns.PARENT);
selectionArgs = new String[] { String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE), String.valueOf(albumId) };
}
return QueryUtils.query(new Query.Builder()
.uri(MediaStore.Files.getContentUri("external"))
.projection(Media.getProjection())
.selection(selection)
.args(selectionArgs)
.sort(MediaStore.Images.Media.DATE_MODIFIED)
.ascending(false)
.limit(n).build(), context.getContentResolver(), Media::new);
}
public static long getAlbumId(Context context, String mediaPath) {
long id = -1;
Cursor cur = context.getContentResolver().query(MediaStore.Files.getContentUri("external"),
new String[]{ MediaStore.Files.FileColumns.PARENT },
MediaStore.Files.FileColumns.DATA+"=?", new String[]{ mediaPath }, null);
if(cur != null && cur.moveToFirst()){
id = cur.getLong(0);
cur.close();
}
return id;
}
@TestOnly private String getThumbnailPath(Context context, long id) {
Cursor cursor = MediaStore.Images.Thumbnails.queryMiniThumbnail(
context.getContentResolver(), id, MediaStore.Images.Thumbnails.MINI_KIND,
new String[]{ MediaStore.Images.Thumbnails.DATA });
if(cursor.moveToFirst())
return cursor.getString(cursor.getColumnIndex(MediaStore.Images.Thumbnails.DATA));
return null;
}
static ArrayList<Media> getMedia(String path, boolean includeVideo) {
ArrayList<Media> list = new ArrayList<Media>();
File[] images = new File(path).listFiles(new ImageFileFilter(includeVideo));
for (File image : images)
list.add(new Media(image));
return list;
}
static ArrayList<Album> getAlbums(Context context, HashSet<String> excludedAlbums, boolean hidden) {
return getHiddenAlbums(context, excludedAlbums);
/*return hidden ? getHiddenAlbums(context, excludedAlbums) : getAlbums(context, excludedAlbums);*/
}
private static ArrayList<Album> getHiddenAlbums(Context context, HashSet<String> excludedAlbums) {
ArrayList<Album> list = new ArrayList<Album>();
for (File storage : ContentHelper.getStorageRoots(context))
fetchRecursivelyHiddenFolder(context, storage, list, excludedAlbums, PreferenceUtil.getBool(context, "set_include_video", true));
return list;
}
private static void fetchRecursivelyHiddenFolder(Context context, File dir, ArrayList<Album> albumArrayList, HashSet<String> excludedAlbums, boolean includeVideo) {
if (!isExcluded(dir.getPath(), excludedAlbums)) {
File[] folders = dir.listFiles(new FoldersFileFilter());
if (folders != null) {
for (File temp : folders) {
File nomedia = new File(temp, ".nomedia");
if (!isExcluded(temp.getPath(), excludedAlbums) && (nomedia.exists() || temp.isHidden()))
checkAndAddFolder(context, temp, albumArrayList, includeVideo);
fetchRecursivelyHiddenFolder(context, temp, albumArrayList, excludedAlbums, includeVideo);
}
}
}
}
private static void checkAndAddFolder(Context context, File dir, ArrayList<Album> albumArrayList, boolean includeVideo) {
File[] files = dir.listFiles(new ImageFileFilter(includeVideo));
if (files != null && files.length > 0) {
//valid folder
Album asd = new Album(context, dir.getAbsolutePath(), -1, dir.getName(), files.length);
if (!asd.hasCustomCover()) {
long lastMod = Long.MIN_VALUE;
File choice = null;
for (File file : files) {
if (file.lastModified() > lastMod) {
choice = file;
lastMod = file.lastModified();
}
}
if (choice != null)
asd.addMedia(new Media(choice.getAbsolutePath(), choice.lastModified()));
}
albumArrayList.add(asd);
}
}
private static boolean isExcluded(String path, HashSet<String> excludedAlbums) {
for(String s : excludedAlbums) if (path.startsWith(s)) return true;
return false;
}
public static Album getAlbumFromMedia(Context context, String mediaPath) {
File parentFolder = new File(mediaPath).getParentFile();
if (parentFolder == null || !parentFolder.isDirectory())
return null;
return new Album(context, parentFolder.getPath(), getAlbumId(context, mediaPath), parentFolder.getName(), 0);
}
public static Observable<Album> getAlbums() {
return getAlbums(MyApplication.getInstance(), new HashSet<>());
}
private static Observable<Album> getAlbums(Context context, HashSet<String> excludedAlbums) {
String selection, selectionArgs[];
if (PreferenceUtil.getBool(context, "set_include_video", true)) {
selection = String.format("%s=? or %s=?) group by ( %s ",
MediaStore.Files.FileColumns.MEDIA_TYPE,
MediaStore.Files.FileColumns.MEDIA_TYPE,
MediaStore.Files.FileColumns.PARENT);
selectionArgs = new String[]{
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE),
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO),
};
} else {
selection = String.format("%s=?) group by ( %s ",
MediaStore.Files.FileColumns.MEDIA_TYPE,
MediaStore.Files.FileColumns.PARENT);
selectionArgs = new String[] { String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) };
}
Query query = new Query.Builder()
.uri(MediaStore.Files.getContentUri("external"))
.projection(Album.projection)
.selection(selection)
.args(selectionArgs).build();
Log.wtf("asd",query.toString());
return QueryUtils.query(query, context.getContentResolver(), Album::new);
/*Cursor cur = context.getContentResolver().query(
MediaStore.Files.getContentUri("external"), projection, selection, selectionArgs, null);
if (cur != null) {
if (cur.moveToFirst()) {
do {
String path = StringUtils.getBucketPathByImagePath(cur.getString(3));
boolean excluded = isExcluded(path, excludedAlbums);
if (!excluded) {
Album album = new Album(context, path, cur.getLong(0), cur.getString(1), cur.getInt(2));
if(!album.hasCustomCover())
album.addMedia(getLastMedia(context, cur.getLong(0)));
list.add(album);
}
}
while (cur.moveToNext());
}
cur.close();
}
return list;*/
}
}
package org.horaapps.leafpic.new_way;
import android.database.Cursor;
import java.sql.SQLException;
/**
* Created by dnld on 3/13/17.
*/
public interface CursorHandler<T> {
T handle(Cursor cu) throws SQLException;
}
package org.horaapps.leafpic.new_way;
import com.jakewharton.rxrelay2.PublishRelay;
import com.jakewharton.rxrelay2.ReplayRelay;
import org.horaapps.leafpic.model.Album;
import org.horaapps.leafpic.model.Media;
import org.reactivestreams.Subscription;