Async folder loading. Optimized search result loading.

This commit is contained in:
Karim Abou Zeid 2016-03-25 21:48:43 +01:00
commit 6f0d39a457
2 changed files with 123 additions and 46 deletions

View file

@ -3,7 +3,6 @@ package com.kabouzeid.gramophone.ui.activities;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.view.MenuItemCompat;
@ -11,6 +10,7 @@ import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
@ -33,9 +33,11 @@ import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
import hugo.weaving.DebugLog;
public class SearchActivity extends AbsMusicServiceActivity implements SearchView.OnQueryTextListener, LoaderManager.LoaderCallbacks<List<Object>> {
public static final String TAG = SearchActivity.class.getSimpleName();
public static final String QUERY = "query";
private static final int LOADER_ID = 1;
@Bind(R.id.recycler_view)
@ -48,7 +50,9 @@ public class SearchActivity extends AbsMusicServiceActivity implements SearchVie
SearchView searchView;
private SearchAdapter adapter;
private String query;
@DebugLog
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -81,9 +85,24 @@ public class SearchActivity extends AbsMusicServiceActivity implements SearchVie
setUpToolBar();
if (savedInstanceState != null) {
query = savedInstanceState.getString(QUERY);
}
getSupportLoaderManager().initLoader(LOADER_ID, null, this);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(QUERY, query);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
private void setUpToolBar() {
toolbar.setBackgroundColor(ThemeStore.primaryColor(this));
setSupportActionBar(toolbar);
@ -91,6 +110,7 @@ public class SearchActivity extends AbsMusicServiceActivity implements SearchVie
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@DebugLog
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_search, menu);
@ -98,7 +118,7 @@ public class SearchActivity extends AbsMusicServiceActivity implements SearchVie
final MenuItem searchItem = menu.findItem(R.id.search);
searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
searchView.setQueryHint(getString(R.string.search_hint));
searchView.setOnQueryTextListener(this);
searchView.setMaxWidth(Integer.MAX_VALUE);
MenuItemCompat.expandActionView(searchItem);
MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
@ -114,6 +134,14 @@ public class SearchActivity extends AbsMusicServiceActivity implements SearchVie
}
});
searchView.setQuery(query, false);
searchView.post(new Runnable() {
@Override
public void run() {
searchView.setOnQueryTextListener(SearchActivity.this);
}
});
return super.onCreateOptionsMenu(menu);
}
@ -126,18 +154,14 @@ public class SearchActivity extends AbsMusicServiceActivity implements SearchVie
}
private void search(@NonNull String query) {
Loader loader = getSupportLoaderManager().getLoader(LOADER_ID);
AsyncSearchResultLoader asyncSearchResultLoader = (AsyncSearchResultLoader) loader;
asyncSearchResultLoader.setQuery(query);
asyncSearchResultLoader.forceLoad();
this.query = query;
getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
}
@Override
public void onMediaStoreChanged() {
super.onMediaStoreChanged();
Loader loader = getSupportLoaderManager().getLoader(LOADER_ID);
AsyncSearchResultLoader asyncSearchResultLoader = (AsyncSearchResultLoader) loader;
asyncSearchResultLoader.forceLoad();
getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
}
@Override
@ -146,6 +170,7 @@ public class SearchActivity extends AbsMusicServiceActivity implements SearchVie
return false;
}
@DebugLog
@Override
public boolean onQueryTextChange(String newText) {
search(newText);
@ -161,7 +186,7 @@ public class SearchActivity extends AbsMusicServiceActivity implements SearchVie
@Override
public Loader<List<Object>> onCreateLoader(int id, Bundle args) {
return new AsyncSearchResultLoader(this);
return new AsyncSearchResultLoader(this, query);
}
@Override
@ -175,21 +200,17 @@ public class SearchActivity extends AbsMusicServiceActivity implements SearchVie
}
private static class AsyncSearchResultLoader extends WrappedAsyncTaskLoader<List<Object>> {
private String query;
private final String query;
public AsyncSearchResultLoader(Context context) {
public AsyncSearchResultLoader(Context context, String query) {
super(context);
setUpdateThrottle(200);
}
public void setQuery(@Nullable String query) {
this.query = query;
}
@Override
public List<Object> loadInBackground() {
List<Object> results = new ArrayList<>();
if (query != null && !query.trim().equals("")) {
if (!TextUtils.isEmpty(query)) {
List songs = SongLoader.getSongs(getContext(), query);
if (!songs.isEmpty()) {
results.add(getContext().getResources().getString(R.string.songs));

View file

@ -14,6 +14,8 @@ import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
@ -39,6 +41,7 @@ import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.interfaces.CabHolder;
import com.kabouzeid.gramophone.loader.SongLoader;
import com.kabouzeid.gramophone.loader.SortedCursor;
import com.kabouzeid.gramophone.misc.WrappedAsyncTaskLoader;
import com.kabouzeid.gramophone.model.Song;
import com.kabouzeid.gramophone.ui.activities.MainActivity;
import com.kabouzeid.gramophone.ui.fragments.mainactivity.AbsMainActivityFragment;
@ -51,6 +54,7 @@ import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -61,9 +65,11 @@ import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
public class FoldersFragment extends AbsMainActivityFragment implements MainActivity.MainActivityFragmentCallbacks, CabHolder, BreadCrumbLayout.SelectionCallback, SongFileAdapter.Callbacks, AppBarLayout.OnOffsetChangedListener {
public class FoldersFragment extends AbsMainActivityFragment implements MainActivity.MainActivityFragmentCallbacks, CabHolder, BreadCrumbLayout.SelectionCallback, SongFileAdapter.Callbacks, AppBarLayout.OnOffsetChangedListener, LoaderManager.LoaderCallbacks<List<File>> {
public static final String TAG = FoldersFragment.class.getSimpleName();
private static final int LOADER_ID = 1;
protected static final String PATH = "path";
protected static final String CRUMBS = "crumbs";
@ -101,43 +107,41 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
}
public void setCrumb(BreadCrumbLayout.Crumb crumb, boolean addToHistory) {
if (crumb == null) return;
saveScrollPosition();
updateAdapter(crumb.getFile());
breadCrumbs.setActiveOrAdd(crumb, false);
if (addToHistory)
if (addToHistory) {
breadCrumbs.addHistory(crumb);
crumb = breadCrumbs.findCrumb(crumb.getFile()); // get the real reference so we can restore previous scroll states
((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(crumb.getScrollPosition(), 0);
}
getLoaderManager().restartLoader(LOADER_ID, null, this);
}
private void saveScrollPosition() {
if (breadCrumbs.size() > 0) {
BreadCrumbLayout.Crumb crumb = breadCrumbs.getCrumb(breadCrumbs.getActiveIndex());
BreadCrumbLayout.Crumb crumb = getActiveCrumb();
if (crumb != null) {
crumb.setScrollPosition(((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition());
}
}
@Nullable
private BreadCrumbLayout.Crumb getActiveCrumb() {
return breadCrumbs != null && breadCrumbs.size() > 0 ? breadCrumbs.getCrumb(breadCrumbs.getActiveIndex()) : null;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(CRUMBS, breadCrumbs.getStateWrapper());
}
private void updateAdapter(File directory) {
List<File> files = sort(listFiles(directory, getFileFilter()));
if (adapter == null) {
adapter = new SongFileAdapter(getMainActivity(), files, R.layout.item_list, this, this);
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
super.onChanged();
checkIsEmpty();
}
});
recyclerView.setAdapter(adapter);
checkIsEmpty();
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState == null) {
setCrumb(new BreadCrumbLayout.Crumb(tryGetCanonicalFile((File) getArguments().getSerializable(PATH))), true);
} else {
adapter.swapDataSet(files);
breadCrumbs.restoreFromStateWrapper((BreadCrumbLayout.SavedStateWrapper) savedInstanceState.getParcelable(CRUMBS));
getLoaderManager().initLoader(LOADER_ID, null, this);
}
}
@ -158,13 +162,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
setUpToolbar();
setUpBreadCrumbs();
setUpRecyclerView();
if (savedInstanceState == null) {
setCrumb(new BreadCrumbLayout.Crumb(tryGetCanonicalFile((File) getArguments().getSerializable(PATH))), true);
} else {
breadCrumbs.restoreFromStateWrapper((BreadCrumbLayout.SavedStateWrapper) savedInstanceState.getParcelable(CRUMBS));
setCrumb(breadCrumbs.getCrumb(breadCrumbs.getActiveIndex()), true);
}
setUpAdapter();
}
private void setUpAppbarColor() {
@ -194,6 +192,19 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
appbar.addOnOffsetChangedListener(this);
}
private void setUpAdapter() {
adapter = new SongFileAdapter(getMainActivity(), new LinkedList<File>(), R.layout.item_list, this, this);
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
super.onChanged();
checkIsEmpty();
}
});
recyclerView.setAdapter(adapter);
checkIsEmpty();
}
@Override
public void onPause() {
super.onPause();
@ -463,7 +474,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
}
@NonNull
private List<File> listFiles(@NonNull File directory, @Nullable FileFilter fileFilter) {
private static List<File> listFiles(@NonNull File directory, @Nullable FileFilter fileFilter) {
List<File> fileList = new LinkedList<>();
File[] found = directory.listFiles(fileFilter);
if (found != null) {
@ -605,4 +616,49 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
return file;
}
}
private void updateAdapter(@NonNull List<File> files) {
adapter.swapDataSet(files);
BreadCrumbLayout.Crumb crumb = getActiveCrumb();
if (crumb != null && recyclerView != null) {
((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(crumb.getScrollPosition(), 0);
}
}
@Override
public Loader<List<File>> onCreateLoader(int id, Bundle args) {
return new AsyncFileLoader(this);
}
@Override
public void onLoadFinished(Loader<List<File>> loader, List<File> data) {
updateAdapter(data);
}
@Override
public void onLoaderReset(Loader<List<File>> loader) {
updateAdapter(new LinkedList<File>());
}
private static class AsyncFileLoader extends WrappedAsyncTaskLoader<List<File>> {
private WeakReference<FoldersFragment> fragmentWeakReference;
public AsyncFileLoader(FoldersFragment foldersFragment) {
super(foldersFragment.getActivity());
fragmentWeakReference = new WeakReference<>(foldersFragment);
}
@Override
public List<File> loadInBackground() {
FoldersFragment foldersFragment = fragmentWeakReference.get();
File directory = null;
if (foldersFragment != null) {
BreadCrumbLayout.Crumb crumb = foldersFragment.getActiveCrumb();
if (crumb != null) {
directory = crumb.getFile();
}
}
return directory != null ? foldersFragment.sort(listFiles(directory, foldersFragment.getFileFilter())) : new LinkedList<File>();
}
}
}