[Android] [PATCH] Async browser fragments loading

Geoffrey Métais geoffrey.metais at gmail.com
Mon Dec 1 10:55:08 CET 2014


---
 vlc-android/res/layout/audio_browser.xml           | 23 ++++---
 .../videolan/vlc/gui/DirectoryViewFragment.java    | 15 ++++-
 .../src/org/videolan/vlc/gui/HistoryFragment.java  | 15 ++++-
 .../src/org/videolan/vlc/gui/MainActivity.java     | 14 +++-
 .../vlc/gui/audio/AudioBrowserFragment.java        | 72 +++++++++++++++------
 .../vlc/gui/audio/AudioBrowserListAdapter.java     |  4 +-
 .../videolan/vlc/gui/video/VideoGridFragment.java  | 74 +++++++++++++++-------
 .../src/org/videolan/vlc/interfaces/IBrowser.java  |  9 +++
 8 files changed, 167 insertions(+), 59 deletions(-)
 create mode 100644 vlc-android/src/org/videolan/vlc/interfaces/IBrowser.java

diff --git a/vlc-android/res/layout/audio_browser.xml b/vlc-android/res/layout/audio_browser.xml
index 27ef224..504e7db 100644
--- a/vlc-android/res/layout/audio_browser.xml
+++ b/vlc-android/res/layout/audio_browser.xml
@@ -1,12 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
+<RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    android:orientation="vertical">
-    <RelativeLayout
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content">
+    android:layout_height="fill_parent">
         <org.videolan.vlc.widget.HeaderScrollView
             android:id="@+id/header"
             android:scrollbars="none"
@@ -77,12 +73,20 @@
             android:layout_alignRight="@+id/header"
             android:layout_alignTop="@+id/header"
             android:background="?attr/header_fadeout_overlay" />
-    </RelativeLayout>
 
+    <ProgressBar
+        android:id="@+id/loading_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/header"
+        android:indeterminate="true"
+        android:layout_centerInParent="true"
+        android:visibility="gone"/>
     <TextView
         android:id="@+id/no_media"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
+        android:layout_below="@+id/header"
         android:gravity="center"
         android:text="@string/nomedia"
         android:textSize="20sp"
@@ -91,7 +95,8 @@
     <android.support.v4.widget.SwipeRefreshLayout
         android:id="@+id/swipeLayout"
         android:layout_width="match_parent"
-        android:layout_height="match_parent">
+        android:layout_height="match_parent"
+        android:layout_below="@+id/header">
         <org.videolan.vlc.widget.FlingViewGroup
             android:id="@+id/content"
             android:layout_width="match_parent"
@@ -155,4 +160,4 @@
                 android:nextFocusRight="@+id/genres_list" />
         </org.videolan.vlc.widget.FlingViewGroup>
     </android.support.v4.widget.SwipeRefreshLayout>
-</LinearLayout>
+</RelativeLayout>
diff --git a/vlc-android/src/org/videolan/vlc/gui/DirectoryViewFragment.java b/vlc-android/src/org/videolan/vlc/gui/DirectoryViewFragment.java
index 0743533..040b60f 100644
--- a/vlc-android/src/org/videolan/vlc/gui/DirectoryViewFragment.java
+++ b/vlc-android/src/org/videolan/vlc/gui/DirectoryViewFragment.java
@@ -28,6 +28,7 @@ import org.videolan.libvlc.LibVlcUtil;
 import org.videolan.vlc.R;
 import org.videolan.vlc.audio.AudioServiceController;
 import org.videolan.vlc.gui.video.VideoPlayerActivity;
+import org.videolan.vlc.interfaces.IBrowser;
 import org.videolan.vlc.interfaces.IRefreshable;
 import org.videolan.vlc.interfaces.ISortable;
 import org.videolan.vlc.util.Util;
@@ -59,11 +60,12 @@ import android.widget.AdapterView.AdapterContextMenuInfo;
 import android.widget.AdapterView.OnItemLongClickListener;
 import android.widget.ListView;
 
-public class DirectoryViewFragment extends ListFragment implements IRefreshable, ISortable, SwipeRefreshLayout.OnRefreshListener {
+public class DirectoryViewFragment extends ListFragment implements IBrowser, IRefreshable, ISortable, SwipeRefreshLayout.OnRefreshListener {
     public final static String TAG = "VLC/DirectoryViewFragment";
 
     private DirectoryAdapter mDirectoryAdapter;
     private SwipeRefreshLayout mSwipeRefreshLayout;
+    private boolean mReady = true;
 
     /* All subclasses of Fragment must include a public empty constructor. */
     public DirectoryViewFragment() { }
@@ -292,4 +294,15 @@ public class DirectoryViewFragment extends ListFragment implements IRefreshable,
     public void onRefresh() {
         refresh();
     }
+
+    @Override
+    public void setReadyToDisplay(boolean ready) {
+        mReady = ready;
+    }
+
+    @Override
+    public void display() {
+        refresh();
+        mReady = true;
+    }
 }
diff --git a/vlc-android/src/org/videolan/vlc/gui/HistoryFragment.java b/vlc-android/src/org/videolan/vlc/gui/HistoryFragment.java
index f7dfc28..f9335be 100644
--- a/vlc-android/src/org/videolan/vlc/gui/HistoryFragment.java
+++ b/vlc-android/src/org/videolan/vlc/gui/HistoryFragment.java
@@ -24,6 +24,7 @@ import org.videolan.libvlc.LibVLC;
 import org.videolan.libvlc.LibVlcUtil;
 import org.videolan.vlc.R;
 import org.videolan.vlc.audio.AudioServiceController;
+import org.videolan.vlc.interfaces.IBrowser;
 import org.videolan.vlc.interfaces.IRefreshable;
 
 import android.os.Bundle;
@@ -41,11 +42,12 @@ import android.widget.AbsListView;
 import android.widget.AdapterView.AdapterContextMenuInfo;
 import android.widget.ListView;
 
-public class HistoryFragment extends ListFragment implements IRefreshable, SwipeRefreshLayout.OnRefreshListener {
+public class HistoryFragment extends ListFragment implements IBrowser, IRefreshable, SwipeRefreshLayout.OnRefreshListener {
     public final static String TAG = "VLC/HistoryFragment";
 
     private HistoryAdapter mHistoryAdapter;
     private SwipeRefreshLayout mSwipeRefreshLayout;
+    private boolean mReady = true;
 
     /* All subclasses of Fragment must include a public empty constructor. */
     public HistoryFragment() { }
@@ -151,4 +153,15 @@ public class HistoryFragment extends ListFragment implements IRefreshable, Swipe
     public void onRefresh() {
         refresh();
     }
+
+    @Override
+    public void setReadyToDisplay(boolean ready) {
+        mReady = ready;
+    }
+
+    @Override
+    public void display() {
+        refresh();
+        mReady = true;
+    }
 }
diff --git a/vlc-android/src/org/videolan/vlc/gui/MainActivity.java b/vlc-android/src/org/videolan/vlc/gui/MainActivity.java
index 74540d9..c2e05c6 100644
--- a/vlc-android/src/org/videolan/vlc/gui/MainActivity.java
+++ b/vlc-android/src/org/videolan/vlc/gui/MainActivity.java
@@ -40,6 +40,7 @@ import org.videolan.vlc.gui.audio.EqualizerFragment;
 import org.videolan.vlc.gui.video.MediaInfoFragment;
 import org.videolan.vlc.gui.video.VideoGridFragment;
 import org.videolan.vlc.gui.video.VideoListAdapter;
+import org.videolan.vlc.interfaces.IBrowser;
 import org.videolan.vlc.interfaces.IRefreshable;
 import org.videolan.vlc.interfaces.ISortable;
 import org.videolan.vlc.util.AndroidDevices;
@@ -229,7 +230,13 @@ public class MainActivity extends ActionBarActivity {
 
         /* Set up the sidebar click listener
          * no need to invalidate menu for now */
-        mDrawerToggle = new ActionBarDrawerToggle(this, mRootContainer, R.string.drawer_open, R.string.drawer_close);
+        mDrawerToggle = new ActionBarDrawerToggle(this, mRootContainer, R.string.drawer_open, R.string.drawer_close){
+            @Override
+            public void onDrawerClosed(View drawerView) {
+                super.onDrawerClosed(drawerView);
+                ((IBrowser) getSupportFragmentManager().findFragmentById(R.id.fragment_placeholder)).setReadyToDisplay(true);
+            }
+        };
 
         // Set the drawer toggle as the DrawerListener
         mRootContainer.setDrawerListener(mDrawerToggle);
@@ -267,8 +274,10 @@ public class MainActivity extends ActionBarActivity {
                     slideDownAudioPlayer();
 
                 /* Switch the fragment */
+                    Fragment fragment = getFragment(entry.id);
+                    ((IBrowser)fragment).setReadyToDisplay(false);
                     FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-                    ft.replace(R.id.fragment_placeholder, getFragment(entry.id), entry.id);
+                    ft.replace(R.id.fragment_placeholder, fragment, entry.id);
                     ft.commit();
                     supportInvalidateOptionsMenu();
                     mCurrentFragment = entry.id;
@@ -291,7 +300,6 @@ public class MainActivity extends ActionBarActivity {
                 }else if (entry.attributeID == R.attr.ic_menu_preferences){
                     startActivityForResult(new Intent(mContext, PreferencesActivity.class), ACTIVITY_RESULT_PREFERENCES);
                 }
-                mRootContainer.closeDrawer(mListView);
             }
         });
 
diff --git a/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.java b/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.java
index fd223f4..26f53ec 100644
--- a/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.java
+++ b/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.java
@@ -21,7 +21,6 @@
 package org.videolan.vlc.gui.audio;
 
 import android.annotation.TargetApi;
-import android.app.Activity;
 import android.app.AlertDialog;
 import android.os.Build;
 import android.os.Bundle;
@@ -50,12 +49,14 @@ import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
 import android.widget.ListView;
 import android.widget.PopupMenu;
 import android.widget.PopupMenu.OnMenuItemClickListener;
+import android.widget.ProgressBar;
 
 import org.videolan.libvlc.LibVlcUtil;
 import org.videolan.libvlc.Media;
 import org.videolan.vlc.MediaLibrary;
 import org.videolan.vlc.R;
 import org.videolan.vlc.audio.AudioServiceController;
+import org.videolan.vlc.interfaces.IBrowser;
 import org.videolan.vlc.gui.CommonDialogs;
 import org.videolan.vlc.gui.MainActivity;
 import org.videolan.vlc.util.AndroidDevices;
@@ -69,7 +70,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
-public class AudioBrowserFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener{
+public class AudioBrowserFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener, IBrowser {
     public final static String TAG = "VLC/AudioBrowserFragment";
 
     private FlingViewGroup mFlingViewGroup;
@@ -84,6 +85,7 @@ public class AudioBrowserFragment extends Fragment implements SwipeRefreshLayout
     private AudioBrowserListAdapter mArtistsAdapter;
     private AudioBrowserListAdapter mAlbumsAdapter;
     private AudioBrowserListAdapter mGenresAdapter;
+    private View mLoading;
 
     private View mEmptyView;
 
@@ -93,6 +95,9 @@ public class AudioBrowserFragment extends Fragment implements SwipeRefreshLayout
     public final static int MODE_SONG = 2;
     public final static int MODE_GENRE = 3;
 
+    public final static int MSG_LOADING = 0;
+    private volatile boolean mReadyToDisplay = true;
+
     /* All subclasses of Fragment must include a public empty constructor. */
     public AudioBrowserFragment() { }
 
@@ -136,6 +141,7 @@ public class AudioBrowserFragment extends Fragment implements SwipeRefreshLayout
         mHeader.setOnKeyListener(keyListener);
 
         mEmptyView = v.findViewById(R.id.no_media);
+        mLoading = (ProgressBar) v.findViewById(R.id.loading_view);
 
         ListView songsList = (ListView)v.findViewById(R.id.songs_list);
         ListView artistList = (ListView)v.findViewById(R.id.artists_list);
@@ -470,6 +476,43 @@ public class AudioBrowserFragment extends Fragment implements SwipeRefreshLayout
         updateLists();
     }
 
+    @Override
+    public void setReadyToDisplay(boolean ready) {
+        if (mLoading != null && mLoading.getVisibility() == View.VISIBLE)
+            display();
+        else
+            mReadyToDisplay = ready;
+    }
+
+    @Override
+    public void display() {
+        mReadyToDisplay = true;
+        if (getActivity() != null)
+            getActivity().runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mSwipeRefreshLayout.setRefreshing(false);
+                    mHandler.removeMessages(MSG_LOADING);
+                    mLoading.setVisibility(View.GONE);
+                    mArtistsAdapter.notifyDataSetChanged();
+                    mSongsAdapter.notifyDataSetChanged();
+                    mAlbumsAdapter.notifyDataSetChanged();
+                    mGenresAdapter.notifyDataSetChanged();
+
+                    // Refresh the fast scroll data, since SectionIndexer doesn't respect notifyDataSetChanged
+                    int[] lists = {R.id.artists_list, R.id.albums_list, R.id.songs_list, R.id.genres_list};
+                    if (getView() != null) {
+                        ListView l;
+                        for (int r : lists) {
+                            l = (ListView) getView().findViewById(r);
+                            l.setFastScrollEnabled(true);
+                        }
+                    }
+                    focusHelper(false, R.id.artists_list);
+                }
+            });
+    }
+
     private static class AudioBrowserHandler extends WeakHandler<AudioBrowserFragment> {
         public AudioBrowserHandler(AudioBrowserFragment owner) {
             super(owner);
@@ -484,22 +527,26 @@ public class AudioBrowserFragment extends Fragment implements SwipeRefreshLayout
                 case MediaLibrary.MEDIA_ITEMS_UPDATED:
                     fragment.updateLists();
                     break;
+                case MSG_LOADING:
+                    if (!fragment.mReadyToDisplay)
+                        fragment.mLoading.setVisibility(View.VISIBLE);
             }
         }
     };
 
     private void updateLists() {
-        final Activity context = getActivity();
         mSongsAdapter.clear();
         mArtistsAdapter.clear();
         mAlbumsAdapter.clear();
         mGenresAdapter.clear();
         final List<Media> audioList = MediaLibrary.getInstance().getAudioItems();
         if (audioList.isEmpty()){
+            mSwipeRefreshLayout.setRefreshing(false);
             mEmptyView.setVisibility(View.VISIBLE);
             focusHelper(true, R.id.artists_list);
         } else {
             mEmptyView.setVisibility(View.GONE);
+            mHandler.sendEmptyMessageDelayed(MSG_LOADING, 300);
 
             new Thread(new Runnable() {
                 @Override
@@ -517,23 +564,8 @@ public class AudioBrowserFragment extends Fragment implements SwipeRefreshLayout
                     Collections.sort(audioList, MediaComparators.byGenre);
                     mGenresAdapter.addAll(audioList, AudioBrowserListAdapter.TYPE_GENRES);
 
-                    context.runOnUiThread(new Runnable() {
-                        @Override
-                        public void run() {
-                            // Refresh the fast scroll data, since SectionIndexer doesn't respect notifyDataSetChanged
-                            int[] lists = {R.id.artists_list, R.id.albums_list, R.id.songs_list, R.id.genres_list};
-                            if (getView() != null) {
-                                ListView l;
-                                for (int r : lists) {
-                                    l = (ListView) getView().findViewById(r);
-                                    l.setFastScrollEnabled(false);
-                                    l.setFastScrollEnabled(true);
-                                }
-                            }
-                            focusHelper(false, R.id.artists_list);
-                            mSwipeRefreshLayout.setRefreshing(false);
-                        }
-                    });
+                    if (mReadyToDisplay)
+                        display();
                 }
             }).start();
         }
diff --git a/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserListAdapter.java b/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserListAdapter.java
index 082f343..9453b13 100644
--- a/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserListAdapter.java
+++ b/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserListAdapter.java
@@ -215,7 +215,7 @@ public class AudioBrowserListAdapter extends BaseAdapter implements SectionIndex
                 firstSeparator = false;
             }
         }
-        notifyDataSetChanged();
+//        notifyDataSetChanged();
     }
 
     public void addSeparator(String title, Media media) {
@@ -228,7 +228,7 @@ public class AudioBrowserListAdapter extends BaseAdapter implements SectionIndex
             mSeparatorItemMap.put(title, item);
             mItems.add(item);
         }
-        notifyDataSetChanged();
+//        notifyDataSetChanged();
     }
 
     /**
diff --git a/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java b/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java
index 49a98f6..7294aaa 100644
--- a/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java
+++ b/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java
@@ -34,6 +34,7 @@ import org.videolan.vlc.MediaLibrary;
 import org.videolan.vlc.R;
 import org.videolan.vlc.Thumbnailer;
 import org.videolan.vlc.audio.AudioServiceController;
+import org.videolan.vlc.interfaces.IBrowser;
 import org.videolan.vlc.gui.CommonDialogs;
 import org.videolan.vlc.gui.MainActivity;
 import org.videolan.vlc.interfaces.ISortable;
@@ -74,7 +75,7 @@ import android.widget.PopupMenu;
 import android.widget.PopupMenu.OnMenuItemClickListener;
 import android.widget.TextView;
 
-public class VideoGridFragment extends SherlockGridFragment implements ISortable, VideoBrowserInterface, SwipeRefreshLayout.OnRefreshListener {
+public class VideoGridFragment extends SherlockGridFragment implements IBrowser, ISortable, VideoBrowserInterface, SwipeRefreshLayout.OnRefreshListener {
 
     public final static String TAG = "VLC/VideoListFragment";
 
@@ -101,6 +102,7 @@ public class VideoGridFragment extends SherlockGridFragment implements ISortable
     private SwipeRefreshLayout mSwipeRefreshLayout;
 
     private AudioServiceController mAudioController;
+    private boolean mReady = true;
 
     // Gridview position saved in onPause()
     private int mGVFirstVisiblePos;
@@ -397,38 +399,41 @@ public class VideoGridFragment extends SherlockGridFragment implements ISortable
     public void updateList() {
         if (!mSwipeRefreshLayout.isRefreshing())
             mSwipeRefreshLayout.setRefreshing(true);
-        List<Media> itemList = mMediaLibrary.getVideoItems();
+        final List<Media> itemList = mMediaLibrary.getVideoItems();
 
         if (mThumbnailer != null)
             mThumbnailer.clearJobs();
         else
             Log.w(TAG, "Can't generate thumbnails, the thumbnailer is missing");
 
+        mVideoAdapter.setNotifyOnChange(false);
         mVideoAdapter.clear();
 
         if (itemList.size() > 0) {
-            if (mGroup != null || itemList.size() <= 10) {
-                for (Media item : itemList) {
-                    if (mGroup == null || item.getTitle().startsWith(mGroup)) {
-                        mVideoAdapter.add(item);
-                        if (mThumbnailer != null)
-                            mThumbnailer.addJob(item);
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    if (mGroup != null || itemList.size() <= 10) {
+                        for (Media item : itemList) {
+                            if (mGroup == null || item.getTitle().startsWith(mGroup)) {
+                                mVideoAdapter.add(item);
+                                if (mThumbnailer != null)
+                                    mThumbnailer.addJob(item);
+                            }
+                        }
                     }
+                    else {
+                        List<MediaGroup> groups = MediaGroup.group(itemList);
+                        for (MediaGroup item : groups) {
+                            mVideoAdapter.add(item.getMedia());
+                            if (mThumbnailer != null)
+                                mThumbnailer.addJob(item);
+                        }
+                    }
+                    if (mReady)
+                        display();
                 }
-            }
-            else {
-                List<MediaGroup> groups = MediaGroup.group(itemList);
-                for (MediaGroup item : groups) {
-                    mVideoAdapter.add(item.getMedia());
-                    if (mThumbnailer != null)
-                        mThumbnailer.addJob(item);
-                }
-            }
-            mVideoAdapter.sort();
-            mGVFirstVisiblePos = mGridView.getFirstVisiblePosition();
-            mGridView.setSelection(mGVFirstVisiblePos);
-            mGridView.requestFocus();
-            focusHelper(false);
+            }).start();
         } else
             focusHelper(true);
         stopRefresh();
@@ -477,6 +482,29 @@ public class VideoGridFragment extends SherlockGridFragment implements ISortable
 
     @Override
     public void onRefresh() {
-        updateList();
+        if (getActivity()!=null)
+            MediaLibrary.getInstance().loadMediaItems(getActivity(), true);
+    }
+
+    @Override
+    public void setReadyToDisplay(boolean ready) {
+        mReady = ready;
+    }
+
+    @Override
+    public void display() {
+        if (getActivity() != null)
+            getActivity().runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mVideoAdapter.sort();
+                    mVideoAdapter.notifyDataSetChanged();
+                    mGVFirstVisiblePos = mGridView.getFirstVisiblePosition();
+                    mGridView.setSelection(mGVFirstVisiblePos);
+                    mGridView.requestFocus();
+                    focusHelper(false);
+                    mReady = true;
+                }
+            });
     }
 }
diff --git a/vlc-android/src/org/videolan/vlc/interfaces/IBrowser.java b/vlc-android/src/org/videolan/vlc/interfaces/IBrowser.java
new file mode 100644
index 0000000..7d36c94
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/interfaces/IBrowser.java
@@ -0,0 +1,9 @@
+package org.videolan.vlc.interfaces;
+
+/**
+ * Created by dekans on 11/28/14.
+ */
+public interface IBrowser {
+    void setReadyToDisplay(boolean ready);
+    void display();
+}
-- 
1.9.1



More information about the Android mailing list