[Android] [PATCH 01/10] Use MediaBrowser to browse local files

Geoffrey Métais geoffrey.metais at gmail.com
Mon Apr 20 10:49:47 CEST 2015


Replaces DirectoryViewFragment
---
 vlc-android/res/layout/directory_view_item.xml     |   2 +
 .../src/org/videolan/vlc/gui/BrowserActivity.java  |  14 +-
 .../src/org/videolan/vlc/gui/BrowserAdapter.java   | 165 ---------
 .../src/org/videolan/vlc/gui/BrowserFragment.java  |  45 ---
 .../videolan/vlc/gui/DirectoryViewFragment.java    |   7 +-
 .../src/org/videolan/vlc/gui/HistoryFragment.java  |   2 +-
 .../src/org/videolan/vlc/gui/MainActivity.java     |  33 +-
 .../org/videolan/vlc/gui/MediaBrowserAdapter.java  | 165 +++++++++
 .../org/videolan/vlc/gui/MediaBrowserFragment.java |  45 +++
 .../src/org/videolan/vlc/gui/SidebarAdapter.java   |  10 +-
 .../vlc/gui/audio/AudioAlbumsSongsFragment.java    |   4 +-
 .../vlc/gui/audio/AudioBrowserFragment.java        |   5 +-
 .../vlc/gui/browser/BaseBrowserAdapter.java        | 218 ++++++++++++
 .../vlc/gui/browser/BaseBrowserFragment.java       | 378 +++++++++++++++++++++
 .../vlc/gui/browser/FileBrowserFragment.java       | 137 ++++++++
 .../vlc/gui/browser/NetworkBrowserAdapter.java     |  33 ++
 .../vlc/gui/browser/NetworkBrowserFragment.java    | 173 ++++++++++
 .../videolan/vlc/gui/network/NetworkAdapter.java   | 200 -----------
 .../videolan/vlc/gui/network/NetworkFragment.java  | 344 -------------------
 .../videolan/vlc/gui/video/VideoGridFragment.java  |   6 +-
 .../src/org/videolan/vlc/util/AndroidDevices.java  |   9 +
 vlc-android/src/org/videolan/vlc/util/Util.java    |   4 +
 .../tv/src/org/videolan/vlc/gui/tv/TvUtil.java     |   5 +-
 .../vlc/gui/tv/browser/BrowserGridFragment.java    |   6 +-
 24 files changed, 1204 insertions(+), 806 deletions(-)
 delete mode 100644 vlc-android/src/org/videolan/vlc/gui/BrowserAdapter.java
 delete mode 100644 vlc-android/src/org/videolan/vlc/gui/BrowserFragment.java
 create mode 100644 vlc-android/src/org/videolan/vlc/gui/MediaBrowserAdapter.java
 create mode 100644 vlc-android/src/org/videolan/vlc/gui/MediaBrowserFragment.java
 create mode 100644 vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserAdapter.java
 create mode 100644 vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.java
 create mode 100644 vlc-android/src/org/videolan/vlc/gui/browser/FileBrowserFragment.java
 create mode 100644 vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserAdapter.java
 create mode 100644 vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserFragment.java
 delete mode 100644 vlc-android/src/org/videolan/vlc/gui/network/NetworkAdapter.java
 delete mode 100644 vlc-android/src/org/videolan/vlc/gui/network/NetworkFragment.java

diff --git a/vlc-android/res/layout/directory_view_item.xml b/vlc-android/res/layout/directory_view_item.xml
index fa8db6d..3005275 100644
--- a/vlc-android/res/layout/directory_view_item.xml
+++ b/vlc-android/res/layout/directory_view_item.xml
@@ -52,6 +52,8 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
+        android:paddingRight="10dp"
+        android:paddingLeft="10dp"
         android:background="@drawable/ic_more"
         android:clickable="true" />
 
diff --git a/vlc-android/src/org/videolan/vlc/gui/BrowserActivity.java b/vlc-android/src/org/videolan/vlc/gui/BrowserActivity.java
index 0cee56a..44fe229 100644
--- a/vlc-android/src/org/videolan/vlc/gui/BrowserActivity.java
+++ b/vlc-android/src/org/videolan/vlc/gui/BrowserActivity.java
@@ -27,7 +27,6 @@ import java.util.Arrays;
 import java.util.Locale;
 import java.util.Stack;
 
-import org.videolan.libvlc.LibVLC;
 import org.videolan.libvlc.LibVlcUtil;
 import org.videolan.vlc.MediaDatabase;
 import org.videolan.vlc.MediaLibrary;
@@ -49,7 +48,6 @@ import android.preference.PreferenceManager;
 import android.text.InputType;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
-import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
 import android.view.MenuItem;
 import android.view.View;
@@ -66,7 +64,7 @@ public class BrowserActivity extends ListActivity {
      * TODO:
      */
 
-    private BrowserAdapter mAdapter;
+    private MediaBrowserAdapter mAdapter;
     private File mCurrentDir;
     private final Stack<ScrollState> mScrollStates = new Stack<ScrollState>();
     private String mRoots[];
@@ -86,7 +84,7 @@ public class BrowserActivity extends ListActivity {
         applyTheme();
         super.onCreate(savedInstanceState);
         setContentView(R.layout.browser);
-        mAdapter = new BrowserAdapter(this);
+        mAdapter = new MediaBrowserAdapter(this);
         setListAdapter(mAdapter);
 
         IntentFilter filter = new IntentFilter();
@@ -131,7 +129,7 @@ public class BrowserActivity extends ListActivity {
         int position = ((AdapterContextMenuInfo)menuInfo).position;
         final File item = mAdapter.getItem(position);
         if (mCurrentDir != null
-                || item.getPath().equals(BrowserAdapter.ADD_ITEM_PATH)
+                || item.getPath().equals(MediaBrowserAdapter.ADD_ITEM_PATH)
                 || AndroidDevices.getStorageDirectories().contains(
                         item.getPath())) {
             return;
@@ -158,7 +156,7 @@ public class BrowserActivity extends ListActivity {
             if (f.exists())
                 mAdapter.add(f);
         }
-        mAdapter.add(new File(BrowserAdapter.ADD_ITEM_PATH));
+        mAdapter.add(new File(MediaBrowserAdapter.ADD_ITEM_PATH));
         mAdapter.sort();
 
         // set scroll position to top
@@ -167,7 +165,7 @@ public class BrowserActivity extends ListActivity {
 
     private void openDir(File file) {
         if(file == null || !file.exists() || file.getPath() == null
-                || file.getPath().equals(BrowserAdapter.ADD_ITEM_PATH))
+                || file.getPath().equals(MediaBrowserAdapter.ADD_ITEM_PATH))
             return;
 
         mAdapter.clear();
@@ -190,7 +188,7 @@ public class BrowserActivity extends ListActivity {
     @Override
     protected void onListItemClick(ListView l, View v, int position, long id) {
         File file = mAdapter.getItem(position);
-        if(file.getPath().equals(BrowserAdapter.ADD_ITEM_PATH)) {
+        if(file.getPath().equals(MediaBrowserAdapter.ADD_ITEM_PATH)) {
             AlertDialog.Builder b = new AlertDialog.Builder(this);
             final EditText input = new EditText(this);
             if (!LibVlcUtil.isHoneycombOrLater()) {
diff --git a/vlc-android/src/org/videolan/vlc/gui/BrowserAdapter.java b/vlc-android/src/org/videolan/vlc/gui/BrowserAdapter.java
deleted file mode 100644
index c78781e..0000000
--- a/vlc-android/src/org/videolan/vlc/gui/BrowserAdapter.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*****************************************************************************
- * BrowserAdapter.java
- *****************************************************************************
- * Copyright © 2011-2012 VLC authors and VideoLAN
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-package org.videolan.vlc.gui;
-
-import java.io.File;
-import java.util.Comparator;
-import java.util.List;
-
-import org.videolan.vlc.MediaDatabase;
-import org.videolan.vlc.R;
-import org.videolan.vlc.VLCApplication;
-
-import android.content.Context;
-import android.os.Build;
-import android.os.Environment;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.TextView;
-
-public class BrowserAdapter extends ArrayAdapter<File>
-                            implements Comparator<File> {
-    public final static String TAG = "VLC/BrowserAdapter";
-
-    public final static String ADD_ITEM_PATH = "/add/a/path";
-
-    public BrowserAdapter(Context context) {
-        super(context, 0);
-    }
-
-    @Override
-    public synchronized void add(File object) {
-        super.add(object);
-    }
-
-    /**
-     * Display the view of a file browser item.
-     */
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-
-        ViewHolder holder;
-        View view = convertView;
-        if (view == null) {
-            LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-            view = inflater.inflate(R.layout.browser_item, parent, false);
-            holder = new ViewHolder();
-            holder.layout = view.findViewById(R.id.layout_item);
-            holder.check = (CheckBox) view.findViewById(R.id.browser_item_selected);
-            holder.text = (TextView) view.findViewById(R.id.browser_item_dir);
-            view.setTag(holder);
-        } else
-            holder = (ViewHolder) view.getTag();
-
-        final File item = getItem(position);
-        final MediaDatabase dbManager = MediaDatabase.getInstance();
-
-        if(item != null) {
-            if(item.getPath().equals(ADD_ITEM_PATH)) {
-                holder.text.setText(R.string.add_custom_path);
-                holder.check.setVisibility(View.GONE);
-            } else if(item.getName() != null) {
-                holder.text.setText(getVisibleName(item));
-                holder.check.setVisibility(View.VISIBLE);
-                holder.check.setOnCheckedChangeListener(null);
-                holder.check.setTag(item);
-                holder.check.setEnabled(true);
-                holder.check.setChecked(false);
-
-                List<File> dirs = dbManager.getMediaDirs();
-                for (File dir : dirs) {
-                    if (dir.getPath().equals(item.getPath())) {
-                        holder.check.setEnabled(true);
-                        holder.check.setChecked(true);
-                        break;
-                    } else if (dir.getPath().startsWith(item.getPath()+"/")) {
-                        Log.i(TAG, dir.getPath() + " startWith " + item.getPath());
-                        holder.check.setEnabled(false);
-                        holder.check.setChecked(true);
-                        break;
-                    }
-                }
-
-                holder.check.setOnCheckedChangeListener(onCheckedChangeListener);
-            }
-        }
-
-        return view;
-    }
-
-    private final OnCheckedChangeListener onCheckedChangeListener = new OnCheckedChangeListener() {
-        @Override
-        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-            final MediaDatabase dbManager = MediaDatabase.getInstance();
-            File item = (File) buttonView.getTag();
-            if (item == null)
-                return;
-
-            if (buttonView.isEnabled() && isChecked) {
-                dbManager.addDir(item.getPath());
-                File tmpFile = item.getParentFile();
-                while (tmpFile != null && !tmpFile.getPath().equals("/")) {
-                    dbManager.removeDir(tmpFile.getPath());
-                    tmpFile = tmpFile.getParentFile();
-                }
-            } else {
-                dbManager.removeDir(item.getPath());
-            }
-        }
-    };
-
-    public void sort() {
-        super.sort(this);
-    }
-
-    @Override
-    public int compare(File file1, File file2) {
-        // float the add item to the bottom
-        if(file1.getPath().equals(ADD_ITEM_PATH))
-            return 1;
-        else if(file2.getPath().equals(ADD_ITEM_PATH))
-            return -1;
-
-        return String.CASE_INSENSITIVE_ORDER.compare(file1.getName(), file2.getName());
-    }
-
-    private String getVisibleName(File file) {
-        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            // Show "sdcard" for the user's folder when running in multi-user
-            if (file.getAbsolutePath().equals(Environment.getExternalStorageDirectory().getPath())) {
-                return VLCApplication.getAppContext().getString(R.string.internal_memory);
-            }
-        }
-        return file.getName();
-    }
-
-    static class ViewHolder {
-        View layout;
-        CheckBox check;
-        TextView text;
-    }
-}
diff --git a/vlc-android/src/org/videolan/vlc/gui/BrowserFragment.java b/vlc-android/src/org/videolan/vlc/gui/BrowserFragment.java
deleted file mode 100644
index 1d61d93..0000000
--- a/vlc-android/src/org/videolan/vlc/gui/BrowserFragment.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * *************************************************************************
- *  BrowserFragment.java
- * **************************************************************************
- *  Copyright © 2015 VLC authors and VideoLAN
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *  ***************************************************************************
- */
-
-package org.videolan.vlc.gui;
-
-import android.support.v4.app.Fragment;
-import android.support.v7.app.ActionBarActivity;
-
-import org.videolan.vlc.widget.SwipeRefreshLayout;
-
-public abstract class BrowserFragment extends Fragment {
-
-    protected SwipeRefreshLayout mSwipeRefreshLayout;
-    protected volatile boolean mReadyToDisplay = true;
-
-    protected void setReadyToDisplay(boolean ready){}
-    protected void display(){}
-
-    protected abstract String getTitle();
-    public abstract void clear();
-    public void onStart(){
-        super.onStart();
-        ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle(getTitle());
-        getActivity().supportInvalidateOptionsMenu();
-    }
-}
diff --git a/vlc-android/src/org/videolan/vlc/gui/DirectoryViewFragment.java b/vlc-android/src/org/videolan/vlc/gui/DirectoryViewFragment.java
index 5ec2332..56b583c 100644
--- a/vlc-android/src/org/videolan/vlc/gui/DirectoryViewFragment.java
+++ b/vlc-android/src/org/videolan/vlc/gui/DirectoryViewFragment.java
@@ -60,10 +60,9 @@ import android.view.ViewGroup;
 import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.AdapterView.OnItemLongClickListener;
 import android.widget.ListView;
 
-public class DirectoryViewFragment extends BrowserFragment implements IRefreshable, SwipeRefreshLayout.OnRefreshListener, AdapterView.OnItemClickListener {
+public class DirectoryViewFragment extends MediaBrowserFragment implements IRefreshable, SwipeRefreshLayout.OnRefreshListener, AdapterView.OnItemClickListener {
     public final static String TAG = "VLC/DirectoryViewFragment";
 
     private DirectoryAdapter mDirectoryAdapter;
@@ -209,12 +208,12 @@ public class DirectoryViewFragment extends BrowserFragment implements IRefreshab
         Fragment fragment = fm.findFragmentByTag(SidebarAdapter.SidebarEntry.ID_AUDIO);
         if (fragment != null) {
             ft.remove(fragment);
-            ((BrowserFragment)fragment).clear();
+            ((MediaBrowserFragment)fragment).clear();
         }
         fragment = fm.findFragmentByTag(SidebarAdapter.SidebarEntry.ID_VIDEO);
         if (fragment != null) {
             ft.remove(fragment);
-            ((BrowserFragment)fragment).clear();
+            ((MediaBrowserFragment)fragment).clear();
         }
         if (!ft.isEmpty())
             ft.commit();
diff --git a/vlc-android/src/org/videolan/vlc/gui/HistoryFragment.java b/vlc-android/src/org/videolan/vlc/gui/HistoryFragment.java
index 0305473..c23a467 100644
--- a/vlc-android/src/org/videolan/vlc/gui/HistoryFragment.java
+++ b/vlc-android/src/org/videolan/vlc/gui/HistoryFragment.java
@@ -41,7 +41,7 @@ import android.widget.AdapterView;
 import android.widget.AdapterView.AdapterContextMenuInfo;
 import android.widget.ListView;
 
-public class HistoryFragment extends BrowserFragment implements IRefreshable, SwipeRefreshLayout.OnRefreshListener, AdapterView.OnItemClickListener {
+public class HistoryFragment extends MediaBrowserFragment implements IRefreshable, SwipeRefreshLayout.OnRefreshListener, AdapterView.OnItemClickListener {
     public final static String TAG = "VLC/HistoryFragment";
 
     private HistoryAdapter mHistoryAdapter;
diff --git a/vlc-android/src/org/videolan/vlc/gui/MainActivity.java b/vlc-android/src/org/videolan/vlc/gui/MainActivity.java
index f20af7a..021936f 100644
--- a/vlc-android/src/org/videolan/vlc/gui/MainActivity.java
+++ b/vlc-android/src/org/videolan/vlc/gui/MainActivity.java
@@ -63,19 +63,18 @@ import android.widget.ListView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
-import org.videolan.libvlc.LibVlcException;
 import org.videolan.libvlc.LibVlcUtil;
 import org.videolan.vlc.BuildConfig;
 import org.videolan.vlc.MediaDatabase;
 import org.videolan.vlc.MediaLibrary;
 import org.videolan.vlc.R;
-import org.videolan.vlc.VLCApplication;
 import org.videolan.vlc.audio.AudioService;
 import org.videolan.vlc.audio.AudioServiceController;
 import org.videolan.vlc.gui.SidebarAdapter.SidebarEntry;
 import org.videolan.vlc.gui.audio.AudioBrowserFragment;
 import org.videolan.vlc.gui.audio.AudioPlayer;
-import org.videolan.vlc.gui.network.NetworkFragment;
+import org.videolan.vlc.gui.browser.BaseBrowserFragment;
+import org.videolan.vlc.gui.browser.NetworkBrowserFragment;
 import org.videolan.vlc.gui.video.VideoGridFragment;
 import org.videolan.vlc.gui.video.VideoListAdapter;
 import org.videolan.vlc.gui.video.VideoPlayerActivity;
@@ -198,8 +197,8 @@ public class MainActivity extends ActionBarActivity implements OnItemClickListen
             @Override
             public void onDrawerClosed(View drawerView) {
                 super.onDrawerClosed(drawerView);
-                if (getSupportFragmentManager().findFragmentById(R.id.fragment_placeholder) instanceof BrowserFragment)
-                    ((BrowserFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_placeholder)).setReadyToDisplay(true);
+                if (getSupportFragmentManager().findFragmentById(R.id.fragment_placeholder) instanceof MediaBrowserFragment)
+                    ((MediaBrowserFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_placeholder)).setReadyToDisplay(true);
             }
         };
 
@@ -380,17 +379,11 @@ public class MainActivity extends ActionBarActivity implements OnItemClickListen
 
         if (mCurrentFragment!= null) {
             // If it's the directory view, a "backpressed" action shows a parent.
-            if (mCurrentFragment.equals(SidebarEntry.ID_DIRECTORIES)) {
-                DirectoryViewFragment directoryView = (DirectoryViewFragment) getFragment(mCurrentFragment);
-                if (!directoryView.isRootDirectory()) {
-                    directoryView.showParentDirectory();
-                    return;
-                }
-            } else if (mCurrentFragment.equals(SidebarEntry.ID_NETWORK)){
-                NetworkFragment networkFragment = (NetworkFragment) getSupportFragmentManager()
+            if (mCurrentFragment.equals(SidebarEntry.ID_NETWORK) || mCurrentFragment.equals(SidebarEntry.ID_DIRECTORIES)){
+                BaseBrowserFragment browserFragment = (BaseBrowserFragment) getSupportFragmentManager()
                         .findFragmentById(R.id.fragment_placeholder);
-                if (networkFragment !=null && !networkFragment.isRootDirectory()) {
-                    networkFragment.goBack();
+                if (browserFragment !=null && !browserFragment.isRootDirectory()) {
+                    browserFragment.goBack();
                     return;
                 }
             }
@@ -504,11 +497,11 @@ public class MainActivity extends ActionBarActivity implements OnItemClickListen
                     item.setTitle(R.string.sortby_date);
         }
 
-        boolean networkSave = current instanceof NetworkFragment && !((NetworkFragment)current).isRootDirectory();
+        boolean networkSave = current instanceof BaseBrowserFragment && !((BaseBrowserFragment)current).isRootDirectory();
         if (networkSave) {
             MenuItem item = menu.findItem(R.id.ml_menu_save);
             item.setVisible(true);
-            String mrl = ((NetworkFragment)current).mMrl;
+            String mrl = ((BaseBrowserFragment)current).mMrl;
             item.setIcon(MediaDatabase.getInstance().networkFavExists(mrl) ?
                     R.drawable.ic_menu_bookmark_w :
                     R.drawable.ic_menu_bookmark_outline_w);
@@ -591,7 +584,7 @@ public class MainActivity extends ActionBarActivity implements OnItemClickListen
             case R.id.ml_menu_save:
                 if (current == null)
                     break;
-                ((NetworkFragment)current).toggleFavorite();
+                ((NetworkBrowserFragment)current).toggleFavorite();
                 item.setIcon(R.drawable.ic_menu_bookmark_w);
                 break;
         }
@@ -965,8 +958,8 @@ public class MainActivity extends ActionBarActivity implements OnItemClickListen
 
                 /* Switch the fragment */
             Fragment fragment = getFragment(entry.id);
-            if (fragment instanceof BrowserFragment)
-                ((BrowserFragment) fragment).setReadyToDisplay(false);
+            if (fragment instanceof MediaBrowserFragment)
+                ((MediaBrowserFragment)fragment).setReadyToDisplay(false);
             FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
             ft.replace(R.id.fragment_placeholder, fragment, entry.id);
             ft.addToBackStack(mCurrentFragment);
diff --git a/vlc-android/src/org/videolan/vlc/gui/MediaBrowserAdapter.java b/vlc-android/src/org/videolan/vlc/gui/MediaBrowserAdapter.java
new file mode 100644
index 0000000..551d357
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/MediaBrowserAdapter.java
@@ -0,0 +1,165 @@
+/*****************************************************************************
+ * BrowserAdapter.java
+ *****************************************************************************
+ * Copyright © 2011-2012 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+package org.videolan.vlc.gui;
+
+import java.io.File;
+import java.util.Comparator;
+import java.util.List;
+
+import org.videolan.vlc.MediaDatabase;
+import org.videolan.vlc.R;
+import org.videolan.vlc.VLCApplication;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.Environment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.TextView;
+
+public class MediaBrowserAdapter extends ArrayAdapter<File>
+                            implements Comparator<File> {
+    public final static String TAG = "VLC/BrowserAdapter";
+
+    public final static String ADD_ITEM_PATH = "/add/a/path";
+
+    public MediaBrowserAdapter(Context context) {
+        super(context, 0);
+    }
+
+    @Override
+    public synchronized void add(File object) {
+        super.add(object);
+    }
+
+    /**
+     * Display the view of a file browser item.
+     */
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+
+        ViewHolder holder;
+        View view = convertView;
+        if (view == null) {
+            LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            view = inflater.inflate(R.layout.browser_item, parent, false);
+            holder = new ViewHolder();
+            holder.layout = view.findViewById(R.id.layout_item);
+            holder.check = (CheckBox) view.findViewById(R.id.browser_item_selected);
+            holder.text = (TextView) view.findViewById(R.id.browser_item_dir);
+            view.setTag(holder);
+        } else
+            holder = (ViewHolder) view.getTag();
+
+        final File item = getItem(position);
+        final MediaDatabase dbManager = MediaDatabase.getInstance();
+
+        if(item != null) {
+            if(item.getPath().equals(ADD_ITEM_PATH)) {
+                holder.text.setText(R.string.add_custom_path);
+                holder.check.setVisibility(View.GONE);
+            } else if(item.getName() != null) {
+                holder.text.setText(getVisibleName(item));
+                holder.check.setVisibility(View.VISIBLE);
+                holder.check.setOnCheckedChangeListener(null);
+                holder.check.setTag(item);
+                holder.check.setEnabled(true);
+                holder.check.setChecked(false);
+
+                List<File> dirs = dbManager.getMediaDirs();
+                for (File dir : dirs) {
+                    if (dir.getPath().equals(item.getPath())) {
+                        holder.check.setEnabled(true);
+                        holder.check.setChecked(true);
+                        break;
+                    } else if (dir.getPath().startsWith(item.getPath()+"/")) {
+                        Log.i(TAG, dir.getPath() + " startWith " + item.getPath());
+                        holder.check.setEnabled(false);
+                        holder.check.setChecked(true);
+                        break;
+                    }
+                }
+
+                holder.check.setOnCheckedChangeListener(onCheckedChangeListener);
+            }
+        }
+
+        return view;
+    }
+
+    private final OnCheckedChangeListener onCheckedChangeListener = new OnCheckedChangeListener() {
+        @Override
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            final MediaDatabase dbManager = MediaDatabase.getInstance();
+            File item = (File) buttonView.getTag();
+            if (item == null)
+                return;
+
+            if (buttonView.isEnabled() && isChecked) {
+                dbManager.addDir(item.getPath());
+                File tmpFile = item.getParentFile();
+                while (tmpFile != null && !tmpFile.getPath().equals("/")) {
+                    dbManager.removeDir(tmpFile.getPath());
+                    tmpFile = tmpFile.getParentFile();
+                }
+            } else {
+                dbManager.removeDir(item.getPath());
+            }
+        }
+    };
+
+    public void sort() {
+        super.sort(this);
+    }
+
+    @Override
+    public int compare(File file1, File file2) {
+        // float the add item to the bottom
+        if(file1.getPath().equals(ADD_ITEM_PATH))
+            return 1;
+        else if(file2.getPath().equals(ADD_ITEM_PATH))
+            return -1;
+
+        return String.CASE_INSENSITIVE_ORDER.compare(file1.getName(), file2.getName());
+    }
+
+    private String getVisibleName(File file) {
+        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            // Show "sdcard" for the user's folder when running in multi-user
+            if (file.getAbsolutePath().equals(Environment.getExternalStorageDirectory().getPath())) {
+                return VLCApplication.getAppContext().getString(R.string.internal_memory);
+            }
+        }
+        return file.getName();
+    }
+
+    static class ViewHolder {
+        View layout;
+        CheckBox check;
+        TextView text;
+    }
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/MediaBrowserFragment.java b/vlc-android/src/org/videolan/vlc/gui/MediaBrowserFragment.java
new file mode 100644
index 0000000..66190ff
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/MediaBrowserFragment.java
@@ -0,0 +1,45 @@
+/*
+ * *************************************************************************
+ *  BrowserFragment.java
+ * **************************************************************************
+ *  Copyright © 2015 VLC authors and VideoLAN
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *  ***************************************************************************
+ */
+
+package org.videolan.vlc.gui;
+
+import android.support.v4.app.Fragment;
+import android.support.v7.app.ActionBarActivity;
+
+import org.videolan.vlc.widget.SwipeRefreshLayout;
+
+public abstract class MediaBrowserFragment extends Fragment {
+
+    protected SwipeRefreshLayout mSwipeRefreshLayout;
+    protected volatile boolean mReadyToDisplay = true;
+
+    protected void setReadyToDisplay(boolean ready){}
+    protected void display(){}
+
+    protected abstract String getTitle();
+    public abstract void clear();
+    public void onStart(){
+        super.onStart();
+        ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle(getTitle());
+        getActivity().supportInvalidateOptionsMenu();
+    }
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/SidebarAdapter.java b/vlc-android/src/org/videolan/vlc/gui/SidebarAdapter.java
index c9286e0..9b4f2af 100644
--- a/vlc-android/src/org/videolan/vlc/gui/SidebarAdapter.java
+++ b/vlc-android/src/org/videolan/vlc/gui/SidebarAdapter.java
@@ -28,7 +28,9 @@ import org.videolan.vlc.BuildConfig;
 import org.videolan.vlc.R;
 import org.videolan.vlc.VLCApplication;
 import org.videolan.vlc.gui.audio.AudioBrowserFragment;
-import org.videolan.vlc.gui.network.NetworkFragment;
+import org.videolan.vlc.gui.browser.BaseBrowserFragment;
+import org.videolan.vlc.gui.browser.FileBrowserFragment;
+import org.videolan.vlc.gui.browser.NetworkBrowserFragment;
 import org.videolan.vlc.gui.video.VideoGridFragment;
 import org.videolan.vlc.util.Util;
 
@@ -47,7 +49,7 @@ import android.widget.TextView;
 public class SidebarAdapter extends BaseAdapter {
     public final static String TAG = "VLC/SidebarAdapter";
 
-    static class SidebarEntry {
+    public static class SidebarEntry {
         public static final  int TYPE_FRAGMENT = 0;
         public static final  int TYPE_ACTION = 1;
         public static final  int TYPE_SECONDARY_FRAGMENT = 2;
@@ -166,13 +168,13 @@ public class SidebarAdapter extends BaseAdapter {
         } else if(id.equals(SidebarEntry.ID_VIDEO)) {
             f = new VideoGridFragment();
         } else if(id.endsWith(SidebarEntry.ID_DIRECTORIES)) {
-            f = new DirectoryViewFragment();
+            f = new FileBrowserFragment();
         } else if(id.equals(SidebarEntry.ID_HISTORY)) {
             f = new HistoryFragment();
         } else if(id.equals(SidebarEntry.ID_MRL)) {
             f = new MRLPanelFragment();
         } else if(id.equals(SidebarEntry.ID_NETWORK)) {
-            f = new NetworkFragment();
+            f = new NetworkBrowserFragment();
         }
         else {
             mCurrentFragmentId = prevFragmentId; // Restore the current fragment id.
diff --git a/vlc-android/src/org/videolan/vlc/gui/audio/AudioAlbumsSongsFragment.java b/vlc-android/src/org/videolan/vlc/gui/audio/AudioAlbumsSongsFragment.java
index b4b3f5e..9149e0e 100644
--- a/vlc-android/src/org/videolan/vlc/gui/audio/AudioAlbumsSongsFragment.java
+++ b/vlc-android/src/org/videolan/vlc/gui/audio/AudioAlbumsSongsFragment.java
@@ -52,7 +52,7 @@ import org.videolan.vlc.MediaLibrary;
 import org.videolan.vlc.MediaWrapper;
 import org.videolan.vlc.R;
 import org.videolan.vlc.audio.AudioServiceController;
-import org.videolan.vlc.gui.BrowserFragment;
+import org.videolan.vlc.gui.MediaBrowserFragment;
 import org.videolan.vlc.gui.CommonDialogs;
 import org.videolan.vlc.util.AndroidDevices;
 import org.videolan.vlc.util.Util;
@@ -64,7 +64,7 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
-public class AudioAlbumsSongsFragment extends BrowserFragment implements SwipeRefreshLayout.OnRefreshListener {
+public class AudioAlbumsSongsFragment extends MediaBrowserFragment implements SwipeRefreshLayout.OnRefreshListener {
 
     public final static String TAG = "VLC/AudioAlbumsSongsFragment";
 
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 3375a10..bf0389c 100644
--- a/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.java
+++ b/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.java
@@ -52,7 +52,6 @@ import android.widget.TextView;
 
 import com.android.widget.SlidingTabLayout;
 
-import org.videolan.libvlc.LibVLC;
 import org.videolan.libvlc.LibVlcUtil;
 import org.videolan.libvlc.Media;
 import org.videolan.libvlc.util.MediaBrowser;
@@ -61,7 +60,7 @@ import org.videolan.vlc.MediaLibrary;
 import org.videolan.vlc.MediaWrapper;
 import org.videolan.vlc.R;
 import org.videolan.vlc.audio.AudioServiceController;
-import org.videolan.vlc.gui.BrowserFragment;
+import org.videolan.vlc.gui.MediaBrowserFragment;
 import org.videolan.vlc.gui.CommonDialogs;
 import org.videolan.vlc.gui.MainActivity;
 import org.videolan.vlc.gui.SecondaryActivity;
@@ -82,7 +81,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
-public class AudioBrowserFragment extends BrowserFragment implements SwipeRefreshLayout.OnRefreshListener, SlidingTabLayout.OnTabChangedListener, MediaBrowser.EventListener, IBrowser {
+public class AudioBrowserFragment extends MediaBrowserFragment implements SwipeRefreshLayout.OnRefreshListener, SlidingTabLayout.OnTabChangedListener, MediaBrowser.EventListener, IBrowser {
     public final static String TAG = "VLC/AudioBrowserFragment";
 
     private AudioServiceController mAudioController;
diff --git a/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserAdapter.java b/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserAdapter.java
new file mode 100644
index 0000000..03df2d5
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserAdapter.java
@@ -0,0 +1,218 @@
+/**
+ * **************************************************************************
+ * BaseBrowserAdapter.java
+ * ****************************************************************************
+ * Copyright © 2015 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * ***************************************************************************
+ */
+package org.videolan.vlc.gui.browser;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.videolan.libvlc.Media;
+import org.videolan.vlc.MediaWrapper;
+import org.videolan.vlc.R;
+import org.videolan.vlc.gui.audio.MediaComparators;
+import org.videolan.vlc.util.Util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class BaseBrowserAdapter extends  RecyclerView.Adapter<RecyclerView.ViewHolder> {
+    private static final String TAG = "VLC/BaseBrowserAdapter";
+
+    private static final int TYPE_MEDIA = 0;
+    private static final int TYPE_SEPARATOR = 1;
+
+    protected int FOLDER_RES_ID = R.drawable.ic_menu_folder;
+
+    ArrayList<Object> mMediaList = new ArrayList<Object>();
+    BaseBrowserFragment fragment;
+
+    public BaseBrowserAdapter(BaseBrowserFragment fragment){
+        this.fragment = fragment;
+    }
+
+    @Override
+    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        RecyclerView.ViewHolder vh;
+        View v;
+        if (viewType == TYPE_MEDIA) {
+            v = LayoutInflater.from(parent.getContext())
+                    .inflate(R.layout.directory_view_item, parent, false);
+            vh = new MediaViewHolder(v);
+        } else {
+            v = LayoutInflater.from(parent.getContext())
+                    .inflate(R.layout.browser_item_separator, parent, false);
+            vh = new SeparatorViewHolder(v);
+        }
+        return vh;
+    }
+
+    @Override
+    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
+        if (holder instanceof MediaViewHolder) {
+            final MediaViewHolder vh = (MediaViewHolder) holder;
+            final MediaWrapper media = (MediaWrapper) getItem(position);
+            boolean hasContextMenu = (media.getType() == MediaWrapper.TYPE_AUDIO ||
+                  media.getType() == MediaWrapper.TYPE_VIDEO ||
+                  (media.getType() == MediaWrapper.TYPE_DIR && Util.canWrite(media.getLocation())));
+            vh.title.setText(media.getTitle());
+            vh.text.setVisibility(View.GONE);
+            vh.icon.setImageResource(getIconResId(media));
+            vh.more.setVisibility(hasContextMenu ? View.VISIBLE : View.GONE);
+            vh.itemView.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    MediaWrapper mw = (MediaWrapper) getItem(holder.getPosition());
+                    if (mw.getType() == MediaWrapper.TYPE_DIR)
+                        fragment.browse(mw);
+                    else
+                        Util.openMedia(v.getContext(), mw);
+                }
+            });
+            if (hasContextMenu) {
+                vh.more.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        fragment.onPopupMenu(vh.more, holder.getPosition());
+                    }
+                });
+                vh.itemView.setOnLongClickListener(new View.OnLongClickListener() {
+                    @Override
+                    public boolean onLongClick(View v) {
+                        fragment.onPopupMenu(vh.title, holder.getPosition());
+                        return true;
+                    }
+                });
+            }
+        } else {
+            SeparatorViewHolder vh = (SeparatorViewHolder) holder;
+            vh.title.setText(getItem(position).toString());
+        }
+    }
+
+    @Override
+    public int getItemCount() {
+        return mMediaList.size();
+    }
+
+    public class MediaViewHolder extends RecyclerView.ViewHolder {
+        public TextView title;
+        public TextView text;
+        public ImageView icon;
+        public ImageView more;
+
+        public MediaViewHolder(View v) {
+            super(v);
+            title = (TextView) v.findViewById(R.id.title);
+            text = (TextView) v.findViewById(R.id.text);
+            icon = (ImageView) v.findViewById(R.id.dvi_icon);
+            more = (ImageView) v.findViewById(R.id.item_more);
+        }
+    }
+
+    public static class SeparatorViewHolder extends RecyclerView.ViewHolder {
+        public TextView title;
+
+        public SeparatorViewHolder(View v) {
+            super(v);
+            title = (TextView) v.findViewById(R.id.separator_title);
+        }
+    }
+
+    public void clear(){
+        mMediaList.clear();
+        notifyDataSetChanged();
+    }
+
+    public boolean isEmpty(){
+        return mMediaList.isEmpty();
+    }
+
+    public void addItem(Media media, boolean root, boolean first){
+        MediaWrapper mediaWrapper = new MediaWrapper(media);
+        addItem(mediaWrapper, root, first);
+
+    }
+
+    public void addItem(Object item, boolean root, boolean top){
+        int position = top ? 0 : mMediaList.size();
+        if (item instanceof MediaWrapper && ((MediaWrapper)item).getTitle().startsWith("."))
+            return;
+        else if (item instanceof Media)
+            item = new MediaWrapper((Media) item);
+
+        mMediaList.add(position, item);
+        notifyItemInserted(position);
+    }
+
+    public void removeItem(int position){
+        mMediaList.remove(position);
+        notifyItemRemoved(position);
+    }
+
+    public Object getItem(int position){
+        return mMediaList.get(position);
+    }
+
+    public int getItemViewType(int position){
+        if (getItem(position) instanceof  MediaWrapper)
+            return TYPE_MEDIA;
+        else
+            return TYPE_SEPARATOR;
+    }
+
+    public void sortList(){
+        ArrayList<MediaWrapper> files = new ArrayList<MediaWrapper>(), dirs = new ArrayList<MediaWrapper>();
+        for (Object item : mMediaList){
+            if (item instanceof MediaWrapper) {
+                MediaWrapper media = (MediaWrapper) item;
+                if (media.getType() == MediaWrapper.TYPE_DIR)
+                    dirs.add(media);
+                else
+                    files.add(media);
+            }
+        }
+        Collections.sort(dirs, MediaComparators.byName);
+        Collections.sort(files, MediaComparators.byName);
+        mMediaList.clear();
+        mMediaList.addAll(dirs);
+        mMediaList.addAll(files);
+        notifyDataSetChanged();
+    }
+
+    private int getIconResId(MediaWrapper media) {
+        switch (media.getType()){
+            case MediaWrapper.TYPE_AUDIO:
+                return R.drawable.ic_browser_audio_normal;
+            case MediaWrapper.TYPE_DIR:
+                return FOLDER_RES_ID;
+            case MediaWrapper.TYPE_VIDEO:
+                return R.drawable.ic_browser_video_normal;
+            case MediaWrapper.TYPE_SUBTITLE:
+                return R.drawable.ic_browser_subtitle_normal;
+            default:
+                return R.drawable.ic_browser_unknown_normal;
+        }
+    }
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.java b/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.java
new file mode 100644
index 0000000..9a9840f
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.java
@@ -0,0 +1,378 @@
+/**
+ * **************************************************************************
+ * BaseBrowserFragment.java
+ * ****************************************************************************
+ * Copyright © 2015 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * ***************************************************************************
+ */
+package org.videolan.vlc.gui.browser;
+
+import android.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Message;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.PopupMenu;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.videolan.libvlc.LibVLC;
+import org.videolan.libvlc.LibVlcUtil;
+import org.videolan.libvlc.Media;
+import org.videolan.libvlc.util.MediaBrowser;
+import org.videolan.vlc.MediaLibrary;
+import org.videolan.vlc.MediaWrapper;
+import org.videolan.vlc.R;
+import org.videolan.vlc.audio.AudioServiceController;
+import org.videolan.vlc.gui.CommonDialogs;
+import org.videolan.vlc.gui.DividerItemDecoration;
+import org.videolan.vlc.gui.MainActivity;
+import org.videolan.vlc.gui.MediaBrowserFragment;
+import org.videolan.vlc.gui.SidebarAdapter;
+import org.videolan.vlc.gui.video.VideoPlayerActivity;
+import org.videolan.vlc.interfaces.IRefreshable;
+import org.videolan.vlc.util.Util;
+import org.videolan.vlc.util.VLCInstance;
+import org.videolan.vlc.util.VLCRunnable;
+import org.videolan.vlc.util.WeakHandler;
+import org.videolan.vlc.widget.SwipeRefreshLayout;
+
+import java.io.File;
+import java.io.IOException;
+
+
+public abstract class BaseBrowserFragment extends MediaBrowserFragment implements IRefreshable, MediaBrowser.EventListener, SwipeRefreshLayout.OnRefreshListener {
+    protected static final String TAG = "VLC/BaseBrowserFragment";
+
+    public static String ROOT = "smb";
+    public static final String KEY_MRL = "key_mrl";
+    public static final String KEY_MEDIA = "key_media";
+    public static final String KEY_POSITION = "key_list";
+
+    protected BrowserFragmentHandler mHandler;
+    protected MediaBrowser mMediaBrowser;
+    protected RecyclerView mRecyclerView;
+    protected BaseBrowserAdapter mAdapter;
+    protected LinearLayoutManager mLayoutManager;
+    protected TextView mEmptyView;
+    public String mMrl;
+    protected MediaWrapper mCurrentMedia;
+    protected int mSavedPosition = -1, mFavorites = 0;
+    protected boolean mRoot;
+    protected LibVLC mLibVLC;
+
+    protected abstract Fragment createFragment();
+    protected abstract void browseRoot();
+    protected abstract String getCategoryTitle();
+
+    public BaseBrowserFragment(){
+        mHandler = new BrowserFragmentHandler(this);
+        mAdapter = new BaseBrowserAdapter(this);
+    }
+
+    public void onCreate(Bundle bundle){
+        super.onCreate(bundle);
+        mLibVLC = VLCInstance.get();
+
+        if (bundle == null)
+            bundle = getArguments();
+        if (bundle != null){
+            mCurrentMedia = bundle.getParcelable(KEY_MEDIA);
+            if (mCurrentMedia != null)
+                mMrl = mCurrentMedia.getLocation();
+            else
+                mMrl = bundle.getString(KEY_MRL);
+            mSavedPosition = bundle.getInt(KEY_POSITION);
+        }
+    }
+
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
+        View v = inflater.inflate(R.layout.network_browser, container, false);
+        mRecyclerView = (RecyclerView) v.findViewById(R.id.network_list);
+        mEmptyView = (TextView) v.findViewById(android.R.id.empty);
+        mLayoutManager = new LinearLayoutManager(getActivity());
+        mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
+        mRecyclerView.setLayoutManager(mLayoutManager);
+        mRecyclerView.setAdapter(mAdapter);
+        mRecyclerView.setOnScrollListener(mScrollListener);
+
+        mSwipeRefreshLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipeLayout);
+        mSwipeRefreshLayout.setColorSchemeResources(R.color.orange700);
+        mSwipeRefreshLayout.setOnRefreshListener(this);
+        return v;
+    }
+
+    public void onStop(){
+        super.onStop();
+        if (mMediaBrowser != null)
+            mMediaBrowser.release();
+    }
+
+    public void onSaveInstanceState(Bundle outState){
+        outState.putString(KEY_MRL, mMrl);
+        if (mRecyclerView != null) {
+            outState.putInt(KEY_POSITION, mLayoutManager.findFirstCompletelyVisibleItemPosition());
+        }
+        super.onSaveInstanceState(outState);
+    }
+
+    public boolean isRootDirectory(){
+        return mRoot;
+    }
+
+    public String getTitle(){
+        if (mRoot)
+            return getCategoryTitle();
+        else
+            return mCurrentMedia != null ? mCurrentMedia.getTitle() : mMrl;
+    }
+
+    public void goBack(){
+        getActivity().getSupportFragmentManager().popBackStack();
+    }
+
+    public void browse (MediaWrapper media){
+        FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
+        Fragment next = createFragment();
+        Bundle args = new Bundle();
+        args.putParcelable(KEY_MEDIA, media);
+        next.setArguments(args);
+        ft.replace(R.id.fragment_placeholder, next, media.getLocation());
+        ft.addToBackStack(mMrl);
+        ft.commit();
+    }
+
+
+    @Override
+    public void onMediaAdded(int index, Media media) {
+        mAdapter.addItem(media, mRoot, true);
+        updateEmptyView();
+        if (mRoot)
+            mHandler.sendEmptyMessage(BrowserFragmentHandler.MSG_HIDE_LOADING);
+    }
+
+    @Override
+    public void onMediaRemoved(int index, Media media) {
+        mAdapter.removeItem(index);
+    }
+
+    @Override
+    public void onBrowseEnd() {
+        mAdapter.sortList();
+        mHandler.sendEmptyMessage(BrowserFragmentHandler.MSG_HIDE_LOADING);
+        if (mSavedPosition > 0) {
+            mLayoutManager.scrollToPositionWithOffset(mSavedPosition, 0);
+            mSavedPosition = 0;
+        }
+        focusHelper(mAdapter.isEmpty());
+    }
+
+    @Override
+    public void onRefresh() {
+        mSavedPosition = mLayoutManager.findFirstCompletelyVisibleItemPosition();
+        refresh();
+    }
+
+    RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() {
+        @Override
+        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+            super.onScrollStateChanged(recyclerView, newState);
+        }
+
+        @Override
+        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+            int topRowVerticalPosition =
+                    (recyclerView == null || recyclerView.getChildCount() == 0) ? 0 : recyclerView.getChildAt(0).getTop();
+            mSwipeRefreshLayout.setEnabled(topRowVerticalPosition >= 0);
+        }
+    };
+
+    /**
+     * Update views visibility and emptiness info
+     *
+     * @return True if content needs can be refreshed
+     */
+    protected boolean updateEmptyView(){
+        if (mAdapter.isEmpty()){
+            mEmptyView.setText("Directory empty");
+            mEmptyView.setVisibility(View.VISIBLE);
+            mRecyclerView.setVisibility(View.GONE);
+            mSwipeRefreshLayout.setEnabled(false);
+        } else if (mEmptyView.getVisibility() == View.VISIBLE) {
+            mEmptyView.setVisibility(View.GONE);
+            mRecyclerView.setVisibility(View.VISIBLE);
+            mSwipeRefreshLayout.setEnabled(true);
+        }
+        return true;
+    }
+
+    protected void updateDisplay(){
+        if (mMediaBrowser == null)
+            mMediaBrowser = new MediaBrowser(mLibVLC, this);
+        if (mAdapter.isEmpty())
+            refresh();
+    }
+
+    @Override
+    public void refresh() {
+        mAdapter.clear();
+        if (mRoot)
+            browseRoot();
+        else
+            mMediaBrowser.browse(mMrl);
+        mHandler.sendEmptyMessageDelayed(BrowserFragmentHandler.MSG_SHOW_LOADING, 300);
+    }
+
+
+    protected static class BrowserFragmentHandler extends WeakHandler<BaseBrowserFragment> {
+
+        public static final int MSG_SHOW_LOADING = 0;
+        public static final int MSG_HIDE_LOADING = 1;
+
+        public BrowserFragmentHandler(BaseBrowserFragment owner) {
+            super(owner);
+        }
+        @Override
+        public void handleMessage(Message msg) {
+            BaseBrowserFragment fragment = getOwner();
+            switch (msg.what){
+                case MSG_SHOW_LOADING:
+                    fragment.mSwipeRefreshLayout.setRefreshing(true);
+                    break;
+                case MSG_HIDE_LOADING:
+                    removeMessages(MSG_SHOW_LOADING);
+                    fragment.mSwipeRefreshLayout.setRefreshing(false);
+                    break;
+            }
+        }
+    }
+
+    protected void focusHelper(boolean idIsEmpty) {
+        if (getActivity() == null)
+            return;
+        MainActivity main = (MainActivity)getActivity();
+        View parent = View.inflate(main, R.layout.directory_view, null);
+        main.setMenuFocusDown(idIsEmpty, R.id.network_list);
+        main.setSearchAsFocusDown(idIsEmpty, parent, R.id.network_list);
+    }
+
+    public void clear(){
+        mAdapter.clear();
+    }
+
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    public void onPopupMenu(View anchor, final int position) {
+        MediaWrapper mw = (MediaWrapper) mAdapter.getItem(position);
+        if (!LibVlcUtil.isHoneycombOrLater()) {
+            // Call the "classic" context menu
+            anchor.performLongClick();
+            return;
+        }
+        boolean canWrite = Util.canWrite(mw.getLocation());
+        PopupMenu popupMenu = new PopupMenu(getActivity(), anchor);
+        if (mw.getType() == MediaWrapper.TYPE_AUDIO || mw.getType() == MediaWrapper.TYPE_VIDEO) {
+            popupMenu.getMenuInflater().inflate(R.menu.directory_view_file, popupMenu.getMenu());
+            popupMenu.getMenu().findItem(R.id.directory_view_delete).setVisible(canWrite);
+        } else if (mw.getType() == MediaWrapper.TYPE_DIR) {
+            if (canWrite) {
+                Menu menu = popupMenu.getMenu();
+                popupMenu.getMenuInflater().inflate(R.menu.directory_view_dir, menu);
+                boolean nomedia = new File(mw.getLocation() + "/.nomedia").exists();
+                menu.findItem(R.id.directory_view_hide_media).setVisible(!nomedia);
+                menu.findItem(R.id.directory_view_show_media).setVisible(nomedia);
+            }
+        }
+
+        popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+            @Override
+            public boolean onMenuItemClick(MenuItem item) {
+                return handleContextItemSelected(item, position);
+            }
+        });
+        popupMenu.show();
+    }
+
+    private boolean handleContextItemSelected(MenuItem item, int position) {
+        int id = item.getItemId();
+        if (! (mAdapter.getItem(position) instanceof MediaWrapper))
+            return super.onContextItemSelected(item);
+        MediaWrapper mw = (MediaWrapper) mAdapter.getItem(position);
+        switch (id){
+            case R.id.directory_view_play:
+                Util.openMedia(getActivity(), (MediaWrapper) mAdapter.getItem(position));
+                return true;
+            case R.id.directory_view_append:
+                AudioServiceController.getInstance().append(mw.getLocation());
+                return true;
+            case R.id.directory_view_delete:
+                AlertDialog alertDialog = CommonDialogs.deleteMedia(getActivity(), mw.getLocation(),
+                        new VLCRunnable() {
+                            @Override
+                            public void run(Object o) {
+                                refresh();
+                            }
+                        });
+                alertDialog.show();
+                return true;
+            case R.id.directory_view_play_audio:
+                AudioServiceController.getInstance().load(mw.getLocation(), true);
+                return true;
+            case  R.id.directory_view_play_video:
+                VideoPlayerActivity.start(getActivity(), mw.getLocation());
+                return true;
+            case R.id.directory_view_hide_media:
+                try {
+                    new File(mw.getLocation()+"/.nomedia").createNewFile();
+                    updateLib();
+                } catch (IOException e) {}
+                return true;
+            case R.id.directory_view_show_media:
+                new File(mw.getLocation()+"/.nomedia").delete();
+                updateLib();
+                return true;
+        }
+        return false;
+    }
+
+    private void updateLib() {
+        FragmentManager fm = getFragmentManager();
+        FragmentTransaction ft = fm.beginTransaction();
+        Fragment fragment = fm.findFragmentByTag(SidebarAdapter.SidebarEntry.ID_AUDIO);
+        if (fragment != null) {
+            ft.remove(fragment);
+            ((MediaBrowserFragment)fragment).clear();
+        }
+        fragment = fm.findFragmentByTag(SidebarAdapter.SidebarEntry.ID_VIDEO);
+        if (fragment != null) {
+            ft.remove(fragment);
+            ((MediaBrowserFragment)fragment).clear();
+        }
+        if (!ft.isEmpty())
+            ft.commit();
+        MediaLibrary.getInstance().loadMediaItems();
+    }
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/browser/FileBrowserFragment.java b/vlc-android/src/org/videolan/vlc/gui/browser/FileBrowserFragment.java
new file mode 100644
index 0000000..c833922
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/browser/FileBrowserFragment.java
@@ -0,0 +1,137 @@
+/*
+ * *************************************************************************
+ *  FileBrowserFragment.java
+ * **************************************************************************
+ *  Copyright © 2015 VLC authors and VideoLAN
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *  ***************************************************************************
+ */
+
+package org.videolan.vlc.gui.browser;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Environment;
+import android.support.v4.app.Fragment;
+import android.support.v7.internal.widget.AdapterViewCompat;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.MenuInflater;
+import android.view.View;
+
+import org.videolan.vlc.MediaWrapper;
+import org.videolan.vlc.R;
+import org.videolan.vlc.util.AndroidDevices;
+import org.videolan.vlc.util.Util;
+
+import java.io.File;
+
+public class FileBrowserFragment extends BaseBrowserFragment {
+
+    public FileBrowserFragment() {
+        super();
+        ROOT = Environment.getExternalStorageDirectory().getPath();
+    }
+
+    @Override
+    public void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        mRoot = mMrl == null;
+        Log.d(TAG, "file root ? "+mRoot);
+    }
+
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+        int position = ((AdapterViewCompat.AdapterContextMenuInfo)menuInfo).position;
+        MenuInflater menuInflater = getActivity().getMenuInflater();
+        if (mAdapter.getItem(position) instanceof MediaWrapper) {
+            MediaWrapper mw = (MediaWrapper) mAdapter.getItem(position);
+            if (mw.getType() == MediaWrapper.TYPE_DIR) {
+                if (Util.canWrite(mw.getLocation())) {
+                    menuInflater.inflate(R.menu.directory_view_dir, menu);
+                    boolean nomedia = new File(mw.getLocation() + "/.nomedia").exists();
+                    menu.findItem(R.id.directory_view_hide_media).setVisible(!nomedia);
+                    menu.findItem(R.id.directory_view_show_media).setVisible(nomedia);
+                }
+            } else if (mw.getType() == MediaWrapper.TYPE_AUDIO || mw.getType() == MediaWrapper.TYPE_VIDEO)
+                menuInflater.inflate(R.menu.directory_view_file, menu);
+        }
+    }
+
+    @Override
+    protected Fragment createFragment() {
+        return new FileBrowserFragment();
+    }
+
+    @Override
+    protected String getCategoryTitle() {
+        return getString(R.string.directories);
+    }
+
+    @Override
+    protected void browseRoot() {
+        Log.d(TAG, "file browse root");
+        String storages[] = AndroidDevices.getMediaDirectories();
+        MediaWrapper mw;
+        for (String storage : storages) {
+            mw = new MediaWrapper(storage);
+            mw.setTitle(AndroidDevices.getStorageTitle(storage));
+            mw.setType(MediaWrapper.TYPE_DIR);
+            mAdapter.addItem(mw, false, false);
+        }
+        mHandler.sendEmptyMessage(BrowserFragmentHandler.MSG_HIDE_LOADING);
+        updateEmptyView();
+        mAdapter.notifyDataSetChanged();
+    }
+
+    public void onStart(){
+        super.onStart();
+
+        //Handle network connection state
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
+        filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
+        filter.addAction(Intent.ACTION_MEDIA_REMOVED);
+        filter.addAction(Intent.ACTION_MEDIA_EJECT);
+        getActivity().registerReceiver(storageReceiver, filter);
+        if (updateEmptyView())
+            updateDisplay();
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        getActivity().unregisterReceiver(storageReceiver);
+    }
+
+    private final BroadcastReceiver storageReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+
+            if (action.equalsIgnoreCase(Intent.ACTION_MEDIA_MOUNTED) ||
+                    action.equalsIgnoreCase(Intent.ACTION_MEDIA_UNMOUNTED) ||
+                    action.equalsIgnoreCase(Intent.ACTION_MEDIA_REMOVED) ||
+                    action.equalsIgnoreCase(Intent.ACTION_MEDIA_EJECT)) {
+                if (updateEmptyView())
+                    updateDisplay();
+            }
+        }
+    };
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserAdapter.java b/vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserAdapter.java
new file mode 100644
index 0000000..9af76c8
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserAdapter.java
@@ -0,0 +1,33 @@
+/*
+ * *************************************************************************
+ *  NetworkBrowserAdapter.java
+ * **************************************************************************
+ *  Copyright © 2015 VLC authors and VideoLAN
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *  ***************************************************************************
+ */
+
+package org.videolan.vlc.gui.browser;
+
+import org.videolan.vlc.R;
+
+public class NetworkBrowserAdapter extends BaseBrowserAdapter{
+
+    public NetworkBrowserAdapter(BaseBrowserFragment fragment){
+        super(fragment);
+        FOLDER_RES_ID = R.drawable.ic_menu_network;
+    }
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserFragment.java b/vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserFragment.java
new file mode 100644
index 0000000..e5e3dea
--- /dev/null
+++ b/vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserFragment.java
@@ -0,0 +1,173 @@
+/*
+ * *************************************************************************
+ *  NetworkBrowserFragment.java
+ * **************************************************************************
+ *  Copyright © 2015 VLC authors and VideoLAN
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *  ***************************************************************************
+ */
+
+package org.videolan.vlc.gui.browser;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.View;
+
+import org.videolan.libvlc.util.MediaBrowser;
+import org.videolan.vlc.MediaDatabase;
+import org.videolan.vlc.MediaWrapper;
+import org.videolan.vlc.R;
+import org.videolan.vlc.util.AndroidDevices;
+
+import java.util.ArrayList;
+
+public class NetworkBrowserFragment extends BaseBrowserFragment {
+
+    public NetworkBrowserFragment() {
+        ROOT = "smb";
+        mHandler = new BrowserFragmentHandler(this);
+        mAdapter = new NetworkBrowserAdapter(this);
+    }
+
+    @Override
+    public void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        if (mMrl == null)
+            mMrl = ROOT;
+        mRoot = ROOT.equals(mMrl);
+    }
+
+    public void onStart(){
+        super.onStart();
+
+        //Handle network connection state
+        IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
+        getActivity().registerReceiver(networkReceiver, filter);
+    }
+
+    @Override
+    protected Fragment createFragment() {
+        return new NetworkBrowserFragment();
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        getActivity().unregisterReceiver(networkReceiver);
+    }
+
+    protected void updateDisplay(){
+        if (mMediaBrowser == null)
+            mMediaBrowser = new MediaBrowser(mLibVLC, this);
+        if (mAdapter.isEmpty())
+            refresh();
+        else if (mRoot)
+            updateFavorites();
+    }
+
+    @Override
+    protected void browseRoot() {
+        ArrayList<MediaWrapper> favs = MediaDatabase.getInstance().getAllNetworkFav();
+        if (!favs.isEmpty()) {
+            mFavorites = favs.size();
+            for (MediaWrapper fav : favs) {
+                mAdapter.addItem(fav, false, true);
+            }
+            mAdapter.addItem("Network favorites", false, true);
+        }
+        mMediaBrowser.discoverNetworkShares();
+    }
+
+    @Override
+    protected String getCategoryTitle() {
+        return getString(R.string.network_browsing);
+    }
+
+    private void updateFavorites(){
+        ArrayList<MediaWrapper> favs = MediaDatabase.getInstance().getAllNetworkFav();
+        int newSize = favs.size(), totalSize = mAdapter.getItemCount();
+
+        if (newSize == 0 && mFavorites == 0)
+            return;
+        for (int i = 1 ; i <= mFavorites ; ++i){ //remove former favorites
+            mAdapter.removeItem(totalSize-i);
+        }
+        if (newSize == 0)
+            mAdapter.removeItem(totalSize-mFavorites-1); //also remove separator if no more fav
+        else {
+            if (mFavorites == 0)
+                mAdapter.addItem("Network favorites", false, false); //add header if needed
+            for (MediaWrapper fav : favs)
+                mAdapter.addItem(fav, false, false); //add new favorites
+        }
+        mFavorites = newSize; //update count
+    }
+
+    public void toggleFavorite() {
+        MediaDatabase db = MediaDatabase.getInstance();
+        if (db.networkFavExists(mMrl))
+            db.deleteNetworkFav(mMrl);
+        else
+            db.addNetworkFavItem(mMrl, mCurrentMedia.getTitle());
+        getActivity().supportInvalidateOptionsMenu();
+    }
+
+    /**
+     * Update views visibility and emptiness info
+     *
+     * @return True if content needs can be refreshed
+     */
+    protected boolean updateEmptyView() {
+        if (AndroidDevices.hasLANConnection()) {
+            if (mAdapter.isEmpty()) {
+                mEmptyView.setText(mRoot ? R.string.network_shares_discovery : R.string.network_empty);
+                mEmptyView.setVisibility(View.VISIBLE);
+                mRecyclerView.setVisibility(View.GONE);
+                mSwipeRefreshLayout.setEnabled(false);
+            } else {
+                if (mEmptyView.getVisibility() == View.VISIBLE) {
+                    mEmptyView.setVisibility(View.GONE);
+                    mRecyclerView.setVisibility(View.VISIBLE);
+                    mSwipeRefreshLayout.setEnabled(true);
+                }
+            }
+            return true;
+        } else {
+            if (mEmptyView.getVisibility() == View.GONE) {
+                mEmptyView.setText(R.string.network_connection_needed);
+                mEmptyView.setVisibility(View.VISIBLE);
+                mRecyclerView.setVisibility(View.GONE);
+                mSwipeRefreshLayout.setEnabled(false);
+            }
+            return false;
+        }
+    }
+
+    private final BroadcastReceiver networkReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action))
+                if (updateEmptyView())
+                    updateDisplay();
+        }
+    };
+}
diff --git a/vlc-android/src/org/videolan/vlc/gui/network/NetworkAdapter.java b/vlc-android/src/org/videolan/vlc/gui/network/NetworkAdapter.java
deleted file mode 100644
index 32cc811..0000000
--- a/vlc-android/src/org/videolan/vlc/gui/network/NetworkAdapter.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/**
- * **************************************************************************
- * NetworkAdapter.java
- * ****************************************************************************
- * Copyright © 2015 VLC authors and VideoLAN
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- * ***************************************************************************
- */
-package org.videolan.vlc.gui.network;
-
-import android.net.Uri;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import org.videolan.libvlc.Media;
-import org.videolan.vlc.MediaWrapper;
-import org.videolan.vlc.R;
-import org.videolan.vlc.gui.audio.MediaComparators;
-import org.videolan.vlc.util.Util;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
-public class NetworkAdapter extends  RecyclerView.Adapter<RecyclerView.ViewHolder> {
-    private static final String TAG = "VLC/NetworkAdapter";
-
-    private static final int TYPE_MEDIA = 0;
-    private static final int TYPE_SEPARATOR = 1;
-
-    ArrayList<Object> mMediaList = new ArrayList<Object>();
-    NetworkFragment fragment;
-
-    public NetworkAdapter(NetworkFragment fragment){
-        this.fragment = fragment;
-    }
-
-    @Override
-    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        RecyclerView.ViewHolder vh;
-        View v;
-        if (viewType == TYPE_MEDIA) {
-            v = LayoutInflater.from(parent.getContext())
-                    .inflate(R.layout.directory_view_item, parent, false);
-            vh = new MediaViewHolder(v);
-        } else {
-            v = LayoutInflater.from(parent.getContext())
-                    .inflate(R.layout.browser_item_separator, parent, false);
-            vh = new SeparatorViewHolder(v);
-        }
-        return vh;
-    }
-
-    @Override
-    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
-        if (holder instanceof MediaViewHolder) {
-            MediaViewHolder vh = (MediaViewHolder) holder;
-            MediaWrapper media = (MediaWrapper) getItem(position);
-            vh.title.setText(media.getTitle());
-            vh.text.setVisibility(View.GONE);
-            vh.icon.setImageResource(getIconResId(media));
-            vh.more.setVisibility(View.GONE);
-            vh.itemView.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    MediaWrapper mw = (MediaWrapper) getItem(holder.getPosition());
-                    if (mw.getType() == MediaWrapper.TYPE_DIR)
-                        fragment.browse(mw);
-                    else
-                        Util.openMedia(v.getContext(), mw);
-                }
-            });
-        } else {
-            SeparatorViewHolder vh = (SeparatorViewHolder) holder;
-            vh.title.setText(getItem(position).toString());
-        }
-    }
-
-    @Override
-    public int getItemCount() {
-        return mMediaList.size();
-    }
-
-    public static class MediaViewHolder extends RecyclerView.ViewHolder {
-        public TextView title;
-        public TextView text;
-        public ImageView icon;
-        public ImageView more;
-
-        public MediaViewHolder(View v) {
-            super(v);
-            title = (TextView) v.findViewById(R.id.title);
-            text = (TextView) v.findViewById(R.id.text);
-            icon = (ImageView) v.findViewById(R.id.dvi_icon);
-            more = (ImageView) v.findViewById(R.id.item_more);
-        }
-    }
-
-    public static class SeparatorViewHolder extends RecyclerView.ViewHolder {
-        public TextView title;
-
-        public SeparatorViewHolder(View v) {
-            super(v);
-            title = (TextView) v.findViewById(R.id.separator_title);
-        }
-    }
-
-    public void clear(){
-        mMediaList.clear();
-        notifyDataSetChanged();
-    }
-
-    public boolean isEmpty(){
-        return mMediaList.isEmpty();
-    }
-
-    public void addItem(Media media, boolean root, boolean first){
-        MediaWrapper mediaWrapper = new MediaWrapper(media);
-        addItem(mediaWrapper, root, first);
-
-    }
-
-    public void addItem(Object item, boolean root, boolean first){
-        int position = first ? 0 : mMediaList.size();
-        if (item instanceof MediaWrapper && ((MediaWrapper)item).getTitle().startsWith("."))
-            return;
-        else if (item instanceof Media)
-            item = new MediaWrapper((Media) item);
-
-        mMediaList.add(position, item);
-        if (root)
-            notifyItemInserted(position);
-    }
-
-    public void removeItem(int position){
-        mMediaList.remove(position);
-        notifyItemRemoved(position);
-    }
-
-    public Object getItem(int position){
-        return mMediaList.get(position);
-    }
-
-    public int getItemViewType(int position){
-        if (getItem(position) instanceof  MediaWrapper)
-            return TYPE_MEDIA;
-        else
-            return TYPE_SEPARATOR;
-    }
-
-    public void sortList(){
-        ArrayList<MediaWrapper> files = new ArrayList<MediaWrapper>(), dirs = new ArrayList<MediaWrapper>();
-        for (Object item : mMediaList){
-            if (item instanceof MediaWrapper) {
-                MediaWrapper media = (MediaWrapper) item;
-                if (media.getType() == MediaWrapper.TYPE_DIR)
-                    dirs.add(media);
-                else
-                    files.add(media);
-            }
-        }
-        Collections.sort(dirs, MediaComparators.byName);
-        Collections.sort(files, MediaComparators.byName);
-        mMediaList.clear();
-        mMediaList.addAll(dirs);
-        mMediaList.addAll(files);
-        notifyDataSetChanged();
-    }
-
-    private int getIconResId(MediaWrapper media) {
-        switch (media.getType()){
-            case MediaWrapper.TYPE_AUDIO:
-                return R.drawable.ic_browser_audio_normal;
-            case MediaWrapper.TYPE_DIR:
-                return R.drawable.ic_menu_network;
-            case MediaWrapper.TYPE_VIDEO:
-                return R.drawable.ic_browser_video_normal;
-            case MediaWrapper.TYPE_SUBTITLE:
-                return R.drawable.ic_browser_subtitle_normal;
-            default:
-                return R.drawable.ic_browser_unknown_normal;
-        }
-    }
-}
diff --git a/vlc-android/src/org/videolan/vlc/gui/network/NetworkFragment.java b/vlc-android/src/org/videolan/vlc/gui/network/NetworkFragment.java
deleted file mode 100644
index 9562bd4..0000000
--- a/vlc-android/src/org/videolan/vlc/gui/network/NetworkFragment.java
+++ /dev/null
@@ -1,344 +0,0 @@
-/**
- * **************************************************************************
- * NetworkFragment.java
- * ****************************************************************************
- * Copyright © 2015 VLC authors and VideoLAN
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- * ***************************************************************************
- */
-package org.videolan.vlc.gui.network;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
-import android.os.Bundle;
-import android.os.Message;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import org.videolan.libvlc.LibVLC;
-import org.videolan.libvlc.Media;
-import org.videolan.libvlc.util.MediaBrowser;
-import org.videolan.vlc.MediaDatabase;
-import org.videolan.vlc.MediaWrapper;
-import org.videolan.vlc.R;
-import org.videolan.vlc.gui.BrowserFragment;
-import org.videolan.vlc.gui.DividerItemDecoration;
-import org.videolan.vlc.gui.MainActivity;
-import org.videolan.vlc.interfaces.IRefreshable;
-import org.videolan.vlc.util.AndroidDevices;
-import org.videolan.vlc.util.Strings;
-import org.videolan.vlc.util.VLCInstance;
-import org.videolan.vlc.util.WeakHandler;
-import org.videolan.vlc.widget.SwipeRefreshLayout;
-
-import java.util.ArrayList;
-
-
-public class NetworkFragment extends BrowserFragment implements IRefreshable, MediaBrowser.EventListener, SwipeRefreshLayout.OnRefreshListener {
-    private static final String TAG = "VLC/NetworkFragment";
-
-    public static final String SMB_ROOT = "smb";
-    public static final String KEY_MRL = "key_mrl";
-    public static final String KEY_MEDIA = "key_media";
-    public static final String KEY_POSITION = "key_list";
-
-    private NetworkFragmentHandler mHandler;
-    private MediaBrowser mMediaBrowser;
-    private RecyclerView mRecyclerView;
-    private SwipeRefreshLayout mSwipeRefreshLayout;
-    private NetworkAdapter mAdapter;
-    private LinearLayoutManager mLayoutManager;
-    TextView mEmptyView;
-    public String mMrl;
-    private MediaWrapper mCurrentMedia;
-    private int mSavedPosition = -1, mFavorites = 0;
-    private boolean mRoot;
-    private LibVLC mLibVLC;
-
-    public void onCreate(Bundle bundle){
-        super.onCreate(bundle);
-        mLibVLC = VLCInstance.get();
-
-        if (bundle == null)
-            bundle = getArguments();
-        if (bundle != null){
-            mCurrentMedia = bundle.getParcelable(KEY_MEDIA);
-            if (mCurrentMedia != null)
-                mMrl = mCurrentMedia.getLocation();
-            else
-                mMrl = bundle.getString(KEY_MRL);
-            mSavedPosition = bundle.getInt(KEY_POSITION);
-        }
-        if (mMrl == null)
-            mMrl = SMB_ROOT;
-        mRoot = SMB_ROOT.equals(mMrl);
-        mHandler = new NetworkFragmentHandler(this);
-        mAdapter = new NetworkAdapter(this);
-    }
-
-    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
-        View v = inflater.inflate(R.layout.network_browser, container, false);
-        mRecyclerView = (RecyclerView) v.findViewById(R.id.network_list);
-        mEmptyView = (TextView) v.findViewById(android.R.id.empty);
-        mLayoutManager = new LinearLayoutManager(getActivity());
-        mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
-        mRecyclerView.setLayoutManager(mLayoutManager);
-        mRecyclerView.setAdapter(mAdapter);
-        mRecyclerView.setOnScrollListener(mScrollListener);
-
-        mSwipeRefreshLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipeLayout);
-        mSwipeRefreshLayout.setColorSchemeResources(R.color.orange700);
-        mSwipeRefreshLayout.setOnRefreshListener(this);
-        return v;
-    }
-
-    public void onStop(){
-        super.onStop();
-        if (mMediaBrowser != null)
-            mMediaBrowser.release();
-    }
-
-    public void onStart(){
-        super.onStart();
-
-        //Handle network connection state
-        IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
-        getActivity().registerReceiver(networkReceiver, filter);
-    }
-    public void onSaveInstanceState(Bundle outState){
-        outState.putString(KEY_MRL, mMrl);
-        if (mRecyclerView != null) {
-            outState.putInt(KEY_POSITION, mLayoutManager.findFirstCompletelyVisibleItemPosition());
-        }
-        super.onSaveInstanceState(outState);
-    }
-
-    public String getTitle(){
-        if (mRoot)
-            return getString(R.string.network_browsing);
-        else
-            return mCurrentMedia != null ? mCurrentMedia.getTitle() : mMrl;
-    }
-
-    public boolean isRootDirectory(){
-        return mRoot;
-    }
-
-    public void goBack(){
-        getActivity().getSupportFragmentManager().popBackStack();
-    }
-
-    public void browse (MediaWrapper media){
-        FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
-        Fragment next = new NetworkFragment();
-        Bundle args = new Bundle();
-        args.putParcelable(KEY_MEDIA, media);
-        next.setArguments(args);
-        ft.replace(R.id.fragment_placeholder, next, media.getLocation());
-        ft.addToBackStack(mMrl);
-        ft.commit();
-    }
-
-    @Override
-    public void onMediaAdded(int index, Media media) {
-        mAdapter.addItem(media, mRoot, true);
-        updateEmptyView();
-        if (mRoot)
-            mHandler.sendEmptyMessage(NetworkFragmentHandler.MSG_HIDE_LOADING);
-    }
-
-    @Override
-    public void onMediaRemoved(int index, Media media) {
-        mAdapter.removeItem(index);
-    }
-
-    @Override
-    public void onBrowseEnd() {
-        mAdapter.sortList();
-        mHandler.sendEmptyMessage(NetworkFragmentHandler.MSG_HIDE_LOADING);
-        if (mSavedPosition > 0) {
-            mLayoutManager.scrollToPositionWithOffset(mSavedPosition, 0);
-            mSavedPosition = 0;
-        }
-        focusHelper(mAdapter.isEmpty());
-    }
-
-    @Override
-    public void onRefresh() {
-        mSavedPosition = mLayoutManager.findFirstCompletelyVisibleItemPosition();
-        refresh();
-    }
-
-    RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() {
-        @Override
-        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
-            super.onScrollStateChanged(recyclerView, newState);
-        }
-
-        @Override
-        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
-            int topRowVerticalPosition =
-                    (recyclerView == null || recyclerView.getChildCount() == 0) ? 0 : recyclerView.getChildAt(0).getTop();
-            mSwipeRefreshLayout.setEnabled(topRowVerticalPosition >= 0);
-        }
-    };
-
-    /**
-     * Update views visibility and emptiness info
-     *
-     * @return True if content needs can be refreshed
-     */
-    private boolean updateEmptyView(){
-        if (AndroidDevices.hasLANConnection()){
-            if (mAdapter.isEmpty()){
-                mEmptyView.setText(mRoot ? R.string.network_shares_discovery : R.string.network_empty);
-                mEmptyView.setVisibility(View.VISIBLE);
-                mRecyclerView.setVisibility(View.GONE);
-                mSwipeRefreshLayout.setEnabled(false);
-            } else {
-                if (mEmptyView.getVisibility() == View.VISIBLE) {
-                    mEmptyView.setVisibility(View.GONE);
-                    mRecyclerView.setVisibility(View.VISIBLE);
-                    mSwipeRefreshLayout.setEnabled(true);
-                }
-            }
-            return true;
-        } else {
-            if (mEmptyView.getVisibility() == View.GONE){
-                mEmptyView.setText(R.string.network_connection_needed);
-                mEmptyView.setVisibility(View.VISIBLE);
-                mRecyclerView.setVisibility(View.GONE);
-                mSwipeRefreshLayout.setEnabled(false);
-            }
-            return false;
-        }
-
-    }
-
-    private void updateDisplay(){
-        if (mMediaBrowser == null)
-            mMediaBrowser = new MediaBrowser(mLibVLC, this);
-        if (mAdapter.isEmpty())
-            refresh();
-        else if (mRoot)
-            updateFavorites();
-    }
-
-    @Override
-    public void refresh() {
-        mAdapter.clear();
-        if (mRoot){
-            ArrayList<MediaWrapper> favs = MediaDatabase.getInstance().getAllNetworkFav();
-            if (!favs.isEmpty()) {
-                mFavorites = favs.size();
-                for (MediaWrapper fav : favs) {
-                    mAdapter.addItem(fav, false, true);
-                    mAdapter.notifyDataSetChanged();
-                }
-                mAdapter.addItem("Network favorites", false, true);
-            }
-        }
-        if (mRoot)
-            mMediaBrowser.discoverNetworkShares();
-        else
-            mMediaBrowser.browse(mMrl);
-        mHandler.sendEmptyMessageDelayed(NetworkFragmentHandler.MSG_SHOW_LOADING, 300);
-    }
-
-    private void updateFavorites(){
-        ArrayList<MediaWrapper> favs = MediaDatabase.getInstance().getAllNetworkFav();
-        int newSize = favs.size(), totalSize = mAdapter.getItemCount();
-
-        if (newSize == 0 && mFavorites == 0)
-            return;
-        for (int i = 1 ; i <= mFavorites ; ++i){ //remove former favorites
-            mAdapter.removeItem(totalSize-i);
-        }
-        if (newSize == 0)
-            mAdapter.removeItem(totalSize-mFavorites-1); //also remove separator if no more fav
-        else {
-            if (mFavorites == 0)
-                mAdapter.addItem("Network favorites", false, false); //add header if needed
-            for (MediaWrapper fav : favs)
-                mAdapter.addItem(fav, false, false); //add new favorites
-        }
-        mFavorites = newSize; //update count
-    }
-
-    public void toggleFavorite() {
-        MediaDatabase db = MediaDatabase.getInstance();
-        if (db.networkFavExists(mMrl))
-            db.deleteNetworkFav(mMrl);
-        else
-            db.addNetworkFavItem(mMrl, mCurrentMedia.getTitle());
-        getActivity().supportInvalidateOptionsMenu();
-    }
-
-    private static class NetworkFragmentHandler extends WeakHandler<NetworkFragment> {
-
-        public static final int MSG_SHOW_LOADING = 0;
-        public static final int MSG_HIDE_LOADING = 1;
-
-        public NetworkFragmentHandler(NetworkFragment owner) {
-            super(owner);
-        }
-        @Override
-        public void handleMessage(Message msg) {
-            NetworkFragment fragment = getOwner();
-            switch (msg.what){
-                case MSG_SHOW_LOADING:
-                    fragment.mSwipeRefreshLayout.setRefreshing(true);
-                    break;
-                case MSG_HIDE_LOADING:
-                    removeMessages(MSG_SHOW_LOADING);
-                    fragment.mSwipeRefreshLayout.setRefreshing(false);
-                    break;
-            }
-        }
-    }
-
-    private final BroadcastReceiver networkReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action))
-                if (updateEmptyView())
-                    updateDisplay();
-        }
-    };
-
-    private void focusHelper(boolean idIsEmpty) {
-        if (getActivity() == null)
-            return;
-        MainActivity main = (MainActivity)getActivity();
-        View parent = View.inflate(main, R.layout.directory_view, null);
-        main.setMenuFocusDown(idIsEmpty, R.id.network_list);
-        main.setSearchAsFocusDown(idIsEmpty, parent, R.id.network_list);
-    }
-
-    public void clear(){
-        mAdapter.clear();
-    }
-}
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 07f7783..0cdc730 100644
--- a/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java
+++ b/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java
@@ -54,8 +54,6 @@ import android.widget.PopupMenu;
 import android.widget.PopupMenu.OnMenuItemClickListener;
 import android.widget.TextView;
 
-import org.videolan.libvlc.LibVLC;
-import org.videolan.libvlc.LibVlcException;
 import org.videolan.libvlc.LibVlcUtil;
 import org.videolan.libvlc.Media;
 import org.videolan.vlc.MediaDatabase;
@@ -65,7 +63,7 @@ import org.videolan.vlc.MediaWrapper;
 import org.videolan.vlc.R;
 import org.videolan.vlc.Thumbnailer;
 import org.videolan.vlc.audio.AudioServiceController;
-import org.videolan.vlc.gui.BrowserFragment;
+import org.videolan.vlc.gui.MediaBrowserFragment;
 import org.videolan.vlc.gui.CommonDialogs;
 import org.videolan.vlc.gui.MainActivity;
 import org.videolan.vlc.gui.SecondaryActivity;
@@ -81,7 +79,7 @@ import java.util.List;
 import java.util.concurrent.BrokenBarrierException;
 import java.util.concurrent.CyclicBarrier;
 
-public class VideoGridFragment extends BrowserFragment implements ISortable, IVideoBrowser, SwipeRefreshLayout.OnRefreshListener, AdapterView.OnItemClickListener {
+public class VideoGridFragment extends MediaBrowserFragment implements ISortable, IVideoBrowser, SwipeRefreshLayout.OnRefreshListener, AdapterView.OnItemClickListener {
 
     public final static String TAG = "VLC/VideoListFragment";
 
diff --git a/vlc-android/src/org/videolan/vlc/util/AndroidDevices.java b/vlc-android/src/org/videolan/vlc/util/AndroidDevices.java
index bcdfa79..f7a25cb 100644
--- a/vlc-android/src/org/videolan/vlc/util/AndroidDevices.java
+++ b/vlc-android/src/org/videolan/vlc/util/AndroidDevices.java
@@ -31,6 +31,7 @@ import java.util.List;
 import java.util.StringTokenizer;
 
 import org.videolan.libvlc.LibVlcUtil;
+import org.videolan.vlc.R;
 import org.videolan.vlc.VLCApplication;
 
 import android.annotation.TargetApi;
@@ -41,6 +42,7 @@ import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
 import android.os.Environment;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.view.InputDevice;
 import android.view.MotionEvent;
 
@@ -184,4 +186,11 @@ public class AndroidDevices {
         return networkEnabled;
 
     }
+
+    public static String getStorageTitle(String path){
+        if (TextUtils.equals(Environment.getExternalStorageDirectory().getPath(), path))
+            return VLCApplication.getAppContext().getString(R.string.internal_memory);
+        else
+            return Strings.getName(path);
+    }
 }
diff --git a/vlc-android/src/org/videolan/vlc/util/Util.java b/vlc-android/src/org/videolan/vlc/util/Util.java
index c13e653..b9bc06d 100644
--- a/vlc-android/src/org/videolan/vlc/util/Util.java
+++ b/vlc-android/src/org/videolan/vlc/util/Util.java
@@ -326,6 +326,10 @@ public class Util {
     public static boolean canWrite(String path){
         if (path == null)
             return false;
+        if (path.startsWith("file://"))
+            path = path.substring(7);
+        if (!path.startsWith("/"))
+            return false;
         if (path.startsWith(Environment.getExternalStorageDirectory().getPath()))
             return true;
         if (LibVlcUtil.isLolliPopOrLater())
diff --git a/vlc-android/tv/src/org/videolan/vlc/gui/tv/TvUtil.java b/vlc-android/tv/src/org/videolan/vlc/gui/tv/TvUtil.java
index 78732a7..c198d55 100644
--- a/vlc-android/tv/src/org/videolan/vlc/gui/tv/TvUtil.java
+++ b/vlc-android/tv/src/org/videolan/vlc/gui/tv/TvUtil.java
@@ -20,10 +20,9 @@
 package org.videolan.vlc.gui.tv;
 
 import org.videolan.vlc.MediaWrapper;
-import org.videolan.vlc.gui.network.NetworkFragment;
+import org.videolan.vlc.gui.browser.BaseBrowserFragment;
 import org.videolan.vlc.gui.tv.browser.VerticalGridActivity;
 import org.videolan.vlc.gui.video.VideoPlayerActivity;
-import org.videolan.vlc.interfaces.IVideoBrowser;
 import org.videolan.vlc.util.Strings;
 
 import android.app.Activity;
@@ -48,7 +47,7 @@ public class TvUtil {
             } else if (mediaWrapper.getType() == MediaWrapper.TYPE_DIR){
                 Intent intent = new Intent(activity, VerticalGridActivity.class);
                 intent.putExtra(MainTvActivity.BROWSER_TYPE, MainTvActivity.HEADER_NETWORK);
-                intent.putExtra(NetworkFragment.KEY_MRL, mediaWrapper.getLocation());
+                intent.putExtra(BaseBrowserFragment.KEY_MRL, mediaWrapper.getLocation());
                 activity.startActivity(intent);
             }
         } else if (item instanceof CardPresenter.SimpleCard){
diff --git a/vlc-android/tv/src/org/videolan/vlc/gui/tv/browser/BrowserGridFragment.java b/vlc-android/tv/src/org/videolan/vlc/gui/tv/browser/BrowserGridFragment.java
index 802bb13..b80968d 100644
--- a/vlc-android/tv/src/org/videolan/vlc/gui/tv/browser/BrowserGridFragment.java
+++ b/vlc-android/tv/src/org/videolan/vlc/gui/tv/browser/BrowserGridFragment.java
@@ -34,7 +34,7 @@ import org.videolan.libvlc.Media;
 import org.videolan.libvlc.util.MediaBrowser;
 import org.videolan.vlc.MediaWrapper;
 import org.videolan.vlc.gui.audio.MediaComparators;
-import org.videolan.vlc.gui.network.NetworkFragment;
+import org.videolan.vlc.gui.browser.BaseBrowserFragment;
 import org.videolan.vlc.gui.tv.DetailsActivity;
 import org.videolan.vlc.gui.tv.MediaItemDetails;
 import org.videolan.vlc.util.VLCInstance;
@@ -52,9 +52,9 @@ public class BrowserGridFragment extends GridFragment implements MediaBrowser.Ev
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         if (savedInstanceState != null){
-            mMrl = savedInstanceState.getString(NetworkFragment.KEY_MRL);
+            mMrl = savedInstanceState.getString(BaseBrowserFragment.KEY_MRL);
         } else {
-            mMrl = getActivity().getIntent().getStringExtra(NetworkFragment.KEY_MRL);
+            mMrl = getActivity().getIntent().getStringExtra(BaseBrowserFragment.KEY_MRL);
         }
         setOnItemViewSelectedListener(this);
     }
-- 
2.1.0



More information about the Android mailing list