From 6f0d39a45759acad5648675a28659b59bd28c798 Mon Sep 17 00:00:00 2001 From: Karim Abou Zeid Date: Fri, 25 Mar 2016 21:48:43 +0100 Subject: [PATCH] Async folder loading. Optimized search result loading. --- .../ui/activities/SearchActivity.java | 55 ++++++--- .../mainactivity/folders/FoldersFragment.java | 114 +++++++++++++----- 2 files changed, 123 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SearchActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SearchActivity.java index fa8c213d..e505dd50 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SearchActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SearchActivity.java @@ -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> { 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> 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> { - 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 loadInBackground() { List 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)); diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/folders/FoldersFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/folders/FoldersFragment.java index 2bea9003..da2fa6c5 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/folders/FoldersFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/folders/FoldersFragment.java @@ -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> { 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 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(), 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 listFiles(@NonNull File directory, @Nullable FileFilter fileFilter) { + private static List listFiles(@NonNull File directory, @Nullable FileFilter fileFilter) { List 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 files) { + adapter.swapDataSet(files); + BreadCrumbLayout.Crumb crumb = getActiveCrumb(); + if (crumb != null && recyclerView != null) { + ((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(crumb.getScrollPosition(), 0); + } + } + + @Override + public Loader> onCreateLoader(int id, Bundle args) { + return new AsyncFileLoader(this); + } + + @Override + public void onLoadFinished(Loader> loader, List data) { + updateAdapter(data); + } + + @Override + public void onLoaderReset(Loader> loader) { + updateAdapter(new LinkedList()); + } + + private static class AsyncFileLoader extends WrappedAsyncTaskLoader> { + private WeakReference fragmentWeakReference; + + public AsyncFileLoader(FoldersFragment foldersFragment) { + super(foldersFragment.getActivity()); + fragmentWeakReference = new WeakReference<>(foldersFragment); + } + + @Override + public List 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(); + } + } } \ No newline at end of file