OSDN Git Service

Prevent possible OutOfBoundsException
[android-x86/packages-apps-CMFileManager.git] / src / com / cyanogenmod / filemanager / ui / widgets / NavigationView.java
1 /*
2  * Copyright (C) 2012 The CyanogenMod Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.cyanogenmod.filemanager.ui.widgets;
18
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.SharedPreferences;
22 import android.content.res.TypedArray;
23 import android.os.AsyncTask;
24 import android.os.storage.StorageVolume;
25 import android.util.AttributeSet;
26 import android.util.Log;
27 import android.view.View;
28 import android.view.animation.AccelerateInterpolator;
29 import android.view.animation.AlphaAnimation;
30 import android.view.animation.Animation;
31 import android.widget.AdapterView;
32 import android.widget.ListAdapter;
33 import android.widget.ListView;
34 import android.widget.RelativeLayout;
35 import android.widget.Toast;
36
37 import com.cyanogenmod.filemanager.FileManagerApplication;
38 import com.cyanogenmod.filemanager.R;
39 import com.cyanogenmod.filemanager.adapters.FileSystemObjectAdapter;
40 import com.cyanogenmod.filemanager.adapters.FileSystemObjectAdapter.OnSelectionChangedListener;
41 import com.cyanogenmod.filemanager.console.CancelledOperationException;
42 import com.cyanogenmod.filemanager.console.ConsoleAllocException;
43 import com.cyanogenmod.filemanager.console.VirtualMountPointConsole;
44 import com.cyanogenmod.filemanager.listeners.OnHistoryListener;
45 import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener;
46 import com.cyanogenmod.filemanager.listeners.OnSelectionListener;
47 import com.cyanogenmod.filemanager.model.Directory;
48 import com.cyanogenmod.filemanager.model.FileSystemObject;
49 import com.cyanogenmod.filemanager.model.ParentDirectory;
50 import com.cyanogenmod.filemanager.model.Symlink;
51 import com.cyanogenmod.filemanager.parcelables.NavigationViewInfoParcelable;
52 import com.cyanogenmod.filemanager.parcelables.SearchInfoParcelable;
53 import com.cyanogenmod.filemanager.preferences.AccessMode;
54 import com.cyanogenmod.filemanager.preferences.DisplayRestrictions;
55 import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
56 import com.cyanogenmod.filemanager.preferences.NavigationLayoutMode;
57 import com.cyanogenmod.filemanager.preferences.ObjectIdentifier;
58 import com.cyanogenmod.filemanager.preferences.Preferences;
59 import com.cyanogenmod.filemanager.ui.ThemeManager;
60 import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
61 import com.cyanogenmod.filemanager.ui.policy.DeleteActionPolicy;
62 import com.cyanogenmod.filemanager.ui.policy.IntentsActionPolicy;
63 import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerListener;
64 import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerResponder;
65 import com.cyanogenmod.filemanager.util.CommandHelper;
66 import com.cyanogenmod.filemanager.util.DialogHelper;
67 import com.cyanogenmod.filemanager.util.ExceptionUtil;
68 import com.cyanogenmod.filemanager.util.ExceptionUtil.OnRelaunchCommandResult;
69 import com.cyanogenmod.filemanager.util.FileHelper;
70 import com.cyanogenmod.filemanager.util.StorageHelper;
71
72 import java.io.File;
73 import java.util.ArrayList;
74 import java.util.HashMap;
75 import java.util.List;
76 import java.util.Map;
77
78 /**
79  * The file manager implementation view (contains the graphical representation and the input
80  * management for a file manager; shows the folders/files, the mode view, react touch events,
81  * navigate, ...).
82  */
83 public class NavigationView extends RelativeLayout implements
84 AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener,
85 BreadcrumbListener, OnSelectionChangedListener, OnSelectionListener, OnRequestRefreshListener {
86
87     private static final String TAG = "NavigationView"; //$NON-NLS-1$
88
89     /**
90      * An interface to communicate selection changes events.
91      */
92     public interface OnNavigationSelectionChangedListener {
93         /**
94          * Method invoked when the selection changed.
95          *
96          * @param navView The navigation view that generate the event
97          * @param selectedItems The new selected items
98          */
99         void onSelectionChanged(NavigationView navView, List<FileSystemObject> selectedItems);
100     }
101
102     /**
103      * An interface to communicate a request for show the menu associated
104      * with an item.
105      */
106     public interface OnNavigationRequestMenuListener {
107         /**
108          * Method invoked when a request to show the menu associated
109          * with an item is started.
110          *
111          * @param navView The navigation view that generate the event
112          * @param item The item for which the request was started
113          */
114         void onRequestMenu(NavigationView navView, FileSystemObject item);
115     }
116
117     /**
118      * An interface to communicate a request when the user choose a file.
119      */
120     public interface OnFilePickedListener {
121         /**
122          * Method invoked when a request when the user choose a file.
123          *
124          * @param item The item choose
125          */
126         void onFilePicked(FileSystemObject item);
127     }
128
129     /**
130      * An interface to communicate a change of the current directory
131      */
132     public interface OnDirectoryChangedListener {
133         /**
134          * Method invoked when the current directory changes
135          *
136          * @param item The newly active directory
137          */
138         void onDirectoryChanged(FileSystemObject item);
139     }
140
141     /**
142      * The navigation view mode
143      * @hide
144      */
145     public enum NAVIGATION_MODE {
146         /**
147          * The navigation view acts as a browser, and allow open files itself.
148          */
149         BROWSABLE,
150         /**
151          * The navigation view acts as a picker of files
152          */
153         PICKABLE,
154     }
155
156     /**
157      * A listener for flinging events from {@link FlingerListView}
158      */
159     private final OnItemFlingerListener mOnItemFlingerListener = new OnItemFlingerListener() {
160
161         @Override
162         public boolean onItemFlingerStart(
163                 AdapterView<?> parent, View view, int position, long id) {
164             try {
165                 // Response if the item can be removed
166                 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)parent.getAdapter();
167
168                 // Short circuit to protect OOBE
169                 if (position < 0 || position >= adapter.getCount()) {
170                     return false;
171                 }
172
173                 FileSystemObject fso = adapter.getItem(position);
174                 if (fso != null) {
175                     if (fso instanceof ParentDirectory) {
176                         return false;
177                     }
178                     return true;
179                 }
180             } catch (Exception e) {
181                 ExceptionUtil.translateException(getContext(), e, true, false);
182             }
183             return false;
184         }
185
186         @Override
187         public void onItemFlingerEnd(OnItemFlingerResponder responder,
188                 AdapterView<?> parent, View view, int position, long id) {
189
190             try {
191                 // Response if the item can be removed
192                 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)parent.getAdapter();
193                 FileSystemObject fso = adapter.getItem(position);
194                 if (fso != null) {
195                     DeleteActionPolicy.removeFileSystemObject(
196                             getContext(),
197                             fso,
198                             NavigationView.this,
199                             NavigationView.this,
200                             responder);
201                     return;
202                 }
203
204                 // Cancels the flinger operation
205                 responder.cancel();
206
207             } catch (Exception e) {
208                 ExceptionUtil.translateException(getContext(), e, true, false);
209                 responder.cancel();
210             }
211         }
212     };
213
214     private class NavigationTask extends AsyncTask<String, Integer, List<FileSystemObject>> {
215         private final boolean mUseCurrent;
216         private final boolean mAddToHistory;
217         private final boolean mReload;
218         private boolean mHasChanged;
219         private boolean mIsNewHistory;
220         private String mNewDirChecked;
221         private final SearchInfoParcelable mSearchInfo;
222         private final FileSystemObject mScrollTo;
223
224         public NavigationTask(boolean useCurrent, boolean addToHistory, boolean reload,
225                 SearchInfoParcelable searchInfo, FileSystemObject scrollTo) {
226             super();
227             this.mUseCurrent = useCurrent;
228             this.mAddToHistory = addToHistory;
229             this.mSearchInfo = searchInfo;
230             this.mReload = reload;
231             this.mScrollTo = scrollTo;
232         }
233
234         /**
235          * {@inheritDoc}
236          */
237         @Override
238         protected List<FileSystemObject> doInBackground(String... params) {
239             // Check navigation security (don't allow to go outside the ChRooted environment if one
240             // is created)
241             mNewDirChecked = checkChRootedNavigation(params[0]);
242
243             //Check that it is really necessary change the directory
244             if (!mReload && NavigationView.this.mCurrentDir != null &&
245                     NavigationView.this.mCurrentDir.compareTo(mNewDirChecked) == 0) {
246                 return null;
247             }
248
249             mHasChanged = !(NavigationView.this.mCurrentDir != null &&
250                     NavigationView.this.mCurrentDir.compareTo(mNewDirChecked) == 0);
251             mIsNewHistory = (NavigationView.this.mCurrentDir != null);
252
253             try {
254                 //Reset the custom title view and returns to breadcrumb
255                 if (NavigationView.this.mTitle != null) {
256                     NavigationView.this.mTitle.post(new Runnable() {
257                         @Override
258                         public void run() {
259                             try {
260                                 NavigationView.this.mTitle.restoreView();
261                             } catch (Exception e) {
262                                 e.printStackTrace();
263                             }
264                         }
265                     });
266                 }
267
268
269                 //Start of loading data
270                 if (NavigationView.this.mBreadcrumb != null) {
271                     try {
272                         NavigationView.this.mBreadcrumb.startLoading();
273                     } catch (Throwable ex) {
274                         /**NON BLOCK**/
275                     }
276                 }
277
278                 //Get the files, resolve links and apply configuration
279                 //(sort, hidden, ...)
280                 List<FileSystemObject> files = NavigationView.this.mFiles;
281                 if (!mUseCurrent) {
282                     files = CommandHelper.listFiles(getContext(), mNewDirChecked, null);
283                 }
284                 return files;
285
286             } catch (final ConsoleAllocException e) {
287                 //Show exception and exists
288                 NavigationView.this.post(new Runnable() {
289                     @Override
290                     public void run() {
291                         Context ctx = getContext();
292                         Log.e(TAG, ctx.getString(
293                                 R.string.msgs_cant_create_console), e);
294                         DialogHelper.showToast(ctx,
295                                 R.string.msgs_cant_create_console,
296                                 Toast.LENGTH_LONG);
297                         ((Activity)ctx).finish();
298                     }
299                 });
300
301             } catch (Exception ex) {
302                 //End of loading data
303                 if (NavigationView.this.mBreadcrumb != null) {
304                     try {
305                         NavigationView.this.mBreadcrumb.endLoading();
306                     } catch (Throwable ex2) {
307                         /**NON BLOCK**/
308                     }
309                 }
310                 if (ex instanceof CancelledOperationException) {
311                     return null;
312                 }
313
314                 //Capture exception (attach task, and use listener to do the anim)
315                 ExceptionUtil.attachAsyncTask(
316                         ex,
317                         new AsyncTask<Object, Integer, Boolean>() {
318                             private List<FileSystemObject> mTaskFiles = null;
319                             @Override
320                             @SuppressWarnings({
321                                 "unchecked", "unqualified-field-access"
322                             })
323                             protected Boolean doInBackground(Object... taskParams) {
324                                 mTaskFiles = (List<FileSystemObject>)taskParams[0];
325                                 return Boolean.TRUE;
326                             }
327
328                             @Override
329                             @SuppressWarnings("unqualified-field-access")
330                             protected void onPostExecute(Boolean result) {
331                                 if (!result.booleanValue()) {
332                                     return;
333                                 }
334                                 onPostExecuteTask(
335                                         mTaskFiles, mAddToHistory, mIsNewHistory, mHasChanged,
336                                         mSearchInfo, mNewDirChecked, mScrollTo);
337                             }
338                         });
339                 final OnRelaunchCommandResult exListener =
340                         new OnRelaunchCommandResult() {
341                     @Override
342                     public void onSuccess() {
343                         done();
344                     }
345                     @Override
346                     public void onFailed(Throwable cause) {
347                         done();
348                     }
349                     @Override
350                     public void onCancelled() {
351                         done();
352                     }
353                     private void done() {
354                         // Do animation
355                         fadeEfect(false);
356                     }
357                 };
358                 ExceptionUtil.translateException(
359                         getContext(), ex, false, true, exListener);
360             }
361             return null;
362         }
363
364         /**
365          * {@inheritDoc}
366          */
367         @Override
368         protected void onCancelled(List<FileSystemObject> result) {
369             onCancelled();
370         }
371
372         /**
373          * {@inheritDoc}
374          */
375         @Override
376         protected void onPostExecute(List<FileSystemObject> files) {
377             // This means an exception. This method will be recalled then
378             if (files != null) {
379                 onPostExecuteTask(files, mAddToHistory, mIsNewHistory, mHasChanged,
380                         mSearchInfo, mNewDirChecked, mScrollTo);
381
382                 // Do animation
383                 fadeEfect(false);
384             }
385         }
386
387         /**
388          * Method that performs a fade animation.
389          *
390          * @param out Fade out (true); Fade in (false)
391          */
392         void fadeEfect(final boolean out) {
393             Activity activity = (Activity)getContext();
394             activity.runOnUiThread(new Runnable() {
395                 @Override
396                 public void run() {
397                     Animation fadeAnim = out ?
398                             new AlphaAnimation(1, 0) :
399                                 new AlphaAnimation(0, 1);
400                             fadeAnim.setDuration(50L);
401                             fadeAnim.setFillAfter(true);
402                             fadeAnim.setInterpolator(new AccelerateInterpolator());
403                             NavigationView.this.startAnimation(fadeAnim);
404                 }
405             });
406         }
407     };
408
409     private int mId;
410     private String mCurrentDir;
411     private NavigationLayoutMode mCurrentMode;
412     /**
413      * @hide
414      */
415     List<FileSystemObject> mFiles;
416     private FileSystemObjectAdapter mAdapter;
417
418     private OnHistoryListener mOnHistoryListener;
419     private OnNavigationSelectionChangedListener mOnNavigationSelectionChangedListener;
420     private OnNavigationRequestMenuListener mOnNavigationRequestMenuListener;
421     private OnFilePickedListener mOnFilePickedListener;
422     private OnDirectoryChangedListener mOnDirectoryChangedListener;
423
424     private boolean mChRooted;
425
426     private NAVIGATION_MODE mNavigationMode;
427
428     // Restrictions
429     private Map<DisplayRestrictions, Object> mRestrictions;
430
431     /**
432      * @hide
433      */
434     Breadcrumb mBreadcrumb;
435     /**
436      * @hide
437      */
438     NavigationCustomTitleView mTitle;
439     /**
440      * @hide
441      */
442     AdapterView<?> mAdapterView;
443
444     //The layout for icons mode
445     private static final int RESOURCE_MODE_ICONS_LAYOUT = R.layout.navigation_view_icons;
446     private static final int RESOURCE_MODE_ICONS_ITEM = R.layout.navigation_view_icons_item;
447     //The layout for simple mode
448     private static final int RESOURCE_MODE_SIMPLE_LAYOUT = R.layout.navigation_view_simple;
449     private static final int RESOURCE_MODE_SIMPLE_ITEM = R.layout.navigation_view_simple_item;
450     //The layout for details mode
451     private static final int RESOURCE_MODE_DETAILS_LAYOUT = R.layout.navigation_view_details;
452     private static final int RESOURCE_MODE_DETAILS_ITEM = R.layout.navigation_view_details_item;
453
454     //The current layout identifier (is shared for all the mode layout)
455     private static final int RESOURCE_CURRENT_LAYOUT = R.id.navigation_view_layout;
456
457     /**
458      * Constructor of <code>NavigationView</code>.
459      *
460      * @param context The current context
461      * @param attrs The attributes of the XML tag that is inflating the view.
462      */
463     public NavigationView(Context context, AttributeSet attrs) {
464         super(context, attrs);
465         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Navigable);
466         try {
467             init(a);
468         } finally {
469             a.recycle();
470         }
471     }
472
473     /**
474      * Constructor of <code>NavigationView</code>.
475      *
476      * @param context The current context
477      * @param attrs The attributes of the XML tag that is inflating the view.
478      * @param defStyle The default style to apply to this view. If 0, no style
479      *        will be applied (beyond what is included in the theme). This may
480      *        either be an attribute resource, whose value will be retrieved
481      *        from the current theme, or an explicit style resource.
482      */
483     public NavigationView(Context context, AttributeSet attrs, int defStyle) {
484         super(context, attrs, defStyle);
485         TypedArray a = context.obtainStyledAttributes(
486                 attrs, R.styleable.Navigable, defStyle, 0);
487         try {
488             init(a);
489         } finally {
490             a.recycle();
491         }
492     }
493
494     /**
495      * Invoked when the instance need to be saved.
496      *
497      * @return NavigationViewInfoParcelable The serialized info
498      */
499     public NavigationViewInfoParcelable onSaveState() {
500         //Return the persistent the data
501         NavigationViewInfoParcelable parcel = new NavigationViewInfoParcelable();
502         parcel.setId(this.mId);
503         parcel.setCurrentDir(this.mCurrentDir);
504         parcel.setChRooted(this.mChRooted);
505         parcel.setSelectedFiles(this.mAdapter.getSelectedItems());
506         parcel.setFiles(this.mFiles);
507
508         int firstVisiblePosition = mAdapterView.getFirstVisiblePosition();
509         if (firstVisiblePosition >= 0 && firstVisiblePosition < mAdapter.getCount()) {
510             FileSystemObject firstVisible = mAdapter
511                     .getItem(firstVisiblePosition);
512             parcel.setFirstVisible(firstVisible);
513         }
514
515         return parcel;
516     }
517
518     /**
519      * Invoked when the instance need to be restored.
520      *
521      * @param info The serialized info
522      * @return boolean If can restore
523      */
524     public boolean onRestoreState(NavigationViewInfoParcelable info) {
525         //Restore the data
526         this.mId = info.getId();
527         this.mCurrentDir = info.getCurrentDir();
528         this.mChRooted = info.getChRooted();
529         this.mFiles = info.getFiles();
530         this.mAdapter.setSelectedItems(info.getSelectedFiles());
531
532         final FileSystemObject firstVisible = info.getFirstVisible();
533
534         //Update the views
535         refresh(firstVisible);
536         return true;
537     }
538
539     /**
540      * Method that initializes the view. This method loads all the necessary
541      * information and create an appropriate layout for the view.
542      *
543      * @param tarray The type array
544      */
545     private void init(TypedArray tarray) {
546         // Retrieve the mode
547         this.mNavigationMode = NAVIGATION_MODE.BROWSABLE;
548         int mode = tarray.getInteger(
549                 R.styleable.Navigable_navigation,
550                 NAVIGATION_MODE.BROWSABLE.ordinal());
551         if (mode >= 0 && mode < NAVIGATION_MODE.values().length) {
552             this.mNavigationMode = NAVIGATION_MODE.values()[mode];
553         }
554
555         // Initialize default restrictions (no restrictions)
556         this.mRestrictions = new HashMap<DisplayRestrictions, Object>();
557
558         //Initialize variables
559         this.mFiles = new ArrayList<FileSystemObject>();
560
561         // Is ChRooted environment?
562         if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
563             // Pick mode is always ChRooted
564             this.mChRooted = true;
565         } else {
566             this.mChRooted =
567                     FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;
568         }
569
570         //Retrieve the default configuration
571         if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
572             SharedPreferences preferences = Preferences.getSharedPreferences();
573             int viewMode = preferences.getInt(
574                     FileManagerSettings.SETTINGS_LAYOUT_MODE.getId(),
575                     ((ObjectIdentifier)FileManagerSettings.
576                             SETTINGS_LAYOUT_MODE.getDefaultValue()).getId());
577             changeViewMode(NavigationLayoutMode.fromId(viewMode));
578         } else {
579             // Pick mode has always a details layout
580             changeViewMode(NavigationLayoutMode.DETAILS);
581         }
582     }
583
584     /**
585      * Method that returns the display restrictions to apply to this view.
586      *
587      * @return Map<DisplayRestrictions, Object> The restrictions to apply
588      */
589     public Map<DisplayRestrictions, Object> getRestrictions() {
590         return this.mRestrictions;
591     }
592
593     /**
594      * Method that sets the display restrictions to apply to this view.
595      *
596      * @param mRestrictions The restrictions to apply
597      */
598     public void setRestrictions(Map<DisplayRestrictions, Object> mRestrictions) {
599         this.mRestrictions = mRestrictions;
600     }
601
602     /**
603      * Method that returns the current file list of the navigation view.
604      *
605      * @return List<FileSystemObject> The current file list of the navigation view
606      */
607     public List<FileSystemObject> getFiles() {
608         if (this.mFiles == null) {
609             return null;
610         }
611         return new ArrayList<FileSystemObject>(this.mFiles);
612     }
613
614     /**
615      * Method that returns the current file list of the navigation view.
616      *
617      * @return List<FileSystemObject> The current file list of the navigation view
618      */
619     public List<FileSystemObject> getSelectedFiles() {
620         if (this.mAdapter != null && this.mAdapter.getSelectedItems() != null) {
621             return new ArrayList<FileSystemObject>(this.mAdapter.getSelectedItems());
622         }
623         return null;
624     }
625
626     /**
627      * Method that returns the custom title fragment associated with this navigation view.
628      *
629      * @return NavigationCustomTitleView The custom title view fragment
630      */
631     public NavigationCustomTitleView getCustomTitle() {
632         return this.mTitle;
633     }
634
635     /**
636      * Method that associates the custom title fragment with this navigation view.
637      *
638      * @param title The custom title view fragment
639      */
640     public void setCustomTitle(NavigationCustomTitleView title) {
641         this.mTitle = title;
642     }
643
644     /**
645      * Method that returns the breadcrumb associated with this navigation view.
646      *
647      * @return Breadcrumb The breadcrumb view fragment
648      */
649     public Breadcrumb getBreadcrumb() {
650         return this.mBreadcrumb;
651     }
652
653     /**
654      * Method that associates the breadcrumb with this navigation view.
655      *
656      * @param breadcrumb The breadcrumb view fragment
657      */
658     public void setBreadcrumb(Breadcrumb breadcrumb) {
659         this.mBreadcrumb = breadcrumb;
660         this.mBreadcrumb.addBreadcrumbListener(this);
661     }
662
663     /**
664      * Method that sets the listener for communicate history changes.
665      *
666      * @param onHistoryListener The listener for communicate history changes
667      */
668     public void setOnHistoryListener(OnHistoryListener onHistoryListener) {
669         this.mOnHistoryListener = onHistoryListener;
670     }
671
672     /**
673      * Method that sets the listener which communicates selection changes.
674      *
675      * @param onNavigationSelectionChangedListener The listener reference
676      */
677     public void setOnNavigationSelectionChangedListener(
678             OnNavigationSelectionChangedListener onNavigationSelectionChangedListener) {
679         this.mOnNavigationSelectionChangedListener = onNavigationSelectionChangedListener;
680     }
681
682     /**
683      * Method that sets the listener for menu item requests.
684      *
685      * @param onNavigationRequestMenuListener The listener reference
686      */
687     public void setOnNavigationOnRequestMenuListener(
688             OnNavigationRequestMenuListener onNavigationRequestMenuListener) {
689         this.mOnNavigationRequestMenuListener = onNavigationRequestMenuListener;
690     }
691
692     /**
693      * @return the mOnFilePickedListener
694      */
695     public OnFilePickedListener getOnFilePickedListener() {
696         return this.mOnFilePickedListener;
697     }
698
699     /**
700      * Method that sets the listener for picked items
701      *
702      * @param onFilePickedListener The listener reference
703      */
704     public void setOnFilePickedListener(OnFilePickedListener onFilePickedListener) {
705         this.mOnFilePickedListener = onFilePickedListener;
706     }
707
708     /**
709      * Method that sets the listener for directory changes
710      *
711      * @param onDirectoryChangedListener The listener reference
712      */
713     public void setOnDirectoryChangedListener(
714             OnDirectoryChangedListener onDirectoryChangedListener) {
715         this.mOnDirectoryChangedListener = onDirectoryChangedListener;
716     }
717
718     /**
719      * Method that sets if the view should use flinger gesture detection.
720      *
721      * @param useFlinger If the view should use flinger gesture detection
722      */
723     public void setUseFlinger(boolean useFlinger) {
724         if (this.mCurrentMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
725             // Not supported
726             return;
727         }
728         // Set the flinger listener (only when navigate)
729         if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
730             if (this.mAdapterView instanceof FlingerListView) {
731                 if (useFlinger) {
732                     ((FlingerListView)this.mAdapterView).
733                     setOnItemFlingerListener(this.mOnItemFlingerListener);
734                 } else {
735                     ((FlingerListView)this.mAdapterView).setOnItemFlingerListener(null);
736                 }
737             }
738         }
739     }
740
741     /**
742      * Method that forces the view to scroll to the file system object passed.
743      *
744      * @param fso The file system object
745      */
746     public void scrollTo(final FileSystemObject fso) {
747
748         this.mAdapterView.post(new Runnable() {
749
750             @Override
751             public void run() {
752                 if (fso != null) {
753                     try {
754                         int position = mAdapter.getPosition(fso);
755                         mAdapterView.setSelection(position);
756
757                         // Make the scrollbar appear
758                         if (position > 0) {
759                             mAdapterView.scrollBy(0, 1);
760                             mAdapterView.scrollBy(0, -1);
761                         }
762
763                     } catch (Exception e) {
764                         mAdapterView.setSelection(0);
765                     }
766                 } else {
767                     mAdapterView.setSelection(0);
768                 }
769             }
770         });
771
772     }
773
774     /**
775      * Method that refresh the view data.
776      */
777     public void refresh() {
778         refresh(false);
779     }
780
781     /**
782      * Method that refresh the view data.
783      *
784      * @param restore Restore previous position
785      */
786     public void refresh(boolean restore) {
787         FileSystemObject fso = null;
788         // Try to restore the previous scroll position
789         if (restore) {
790             try {
791                 if (this.mAdapterView != null && this.mAdapter != null) {
792                     int position = this.mAdapterView.getFirstVisiblePosition();
793                     fso = this.mAdapter.getItem(position);
794                 }
795             } catch (Throwable _throw) {/**NON BLOCK**/}
796         }
797         refresh(fso);
798     }
799
800     /**
801      * Method that refresh the view data.
802      *
803      * @param scrollTo Scroll to object
804      */
805     public void refresh(FileSystemObject scrollTo) {
806         //Check that current directory was set
807         if (this.mCurrentDir == null || this.mFiles == null) {
808             return;
809         }
810
811         //Reload data
812         changeCurrentDir(this.mCurrentDir, false, true, false, null, scrollTo);
813     }
814
815     /**
816      * Method that recycles this object
817      */
818     public void recycle() {
819         if (this.mAdapter != null) {
820             this.mAdapter.dispose();
821         }
822     }
823
824     /**
825      * Method that change the view mode.
826      *
827      * @param newMode The new mode
828      */
829     @SuppressWarnings("unchecked")
830     public void changeViewMode(final NavigationLayoutMode newMode) {
831         //Check that it is really necessary change the mode
832         if (this.mCurrentMode != null && this.mCurrentMode.compareTo(newMode) == 0) {
833             return;
834         }
835
836         // If we should set the listview to response to flinger gesture detection
837         boolean useFlinger =
838                 Preferences.getSharedPreferences().getBoolean(
839                         FileManagerSettings.SETTINGS_USE_FLINGER.getId(),
840                         ((Boolean)FileManagerSettings.
841                                 SETTINGS_USE_FLINGER.
842                                 getDefaultValue()).booleanValue());
843
844         //Creates the new layout
845         AdapterView<ListAdapter> newView = null;
846         int itemResourceId = -1;
847         if (newMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
848             newView = (AdapterView<ListAdapter>)inflate(
849                     getContext(), RESOURCE_MODE_ICONS_LAYOUT, null);
850             itemResourceId = RESOURCE_MODE_ICONS_ITEM;
851
852         } else if (newMode.compareTo(NavigationLayoutMode.SIMPLE) == 0) {
853             newView =  (AdapterView<ListAdapter>)inflate(
854                     getContext(), RESOURCE_MODE_SIMPLE_LAYOUT, null);
855             itemResourceId = RESOURCE_MODE_SIMPLE_ITEM;
856
857             // Set the flinger listener (only when navigate)
858             if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
859                 if (useFlinger && newView instanceof FlingerListView) {
860                     ((FlingerListView)newView).
861                     setOnItemFlingerListener(this.mOnItemFlingerListener);
862                 }
863             }
864
865         } else if (newMode.compareTo(NavigationLayoutMode.DETAILS) == 0) {
866             newView =  (AdapterView<ListAdapter>)inflate(
867                     getContext(), RESOURCE_MODE_DETAILS_LAYOUT, null);
868             itemResourceId = RESOURCE_MODE_DETAILS_ITEM;
869
870             // Set the flinger listener (only when navigate)
871             if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
872                 if (useFlinger && newView instanceof FlingerListView) {
873                     ((FlingerListView)newView).
874                     setOnItemFlingerListener(this.mOnItemFlingerListener);
875                 }
876             }
877         }
878
879         //Get the current adapter and its adapter list
880         List<FileSystemObject> files = new ArrayList<FileSystemObject>(this.mFiles);
881         final AdapterView<ListAdapter> current =
882                 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
883         FileSystemObjectAdapter adapter =
884                 new FileSystemObjectAdapter(
885                         getContext(),
886                         new ArrayList<FileSystemObject>(),
887                         itemResourceId,
888                         this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0);
889         adapter.setOnSelectionChangedListener(this);
890
891         //Remove current layout
892         if (current != null) {
893             if (current.getAdapter() != null) {
894                 //Save selected items before dispose adapter
895                 FileSystemObjectAdapter currentAdapter =
896                         ((FileSystemObjectAdapter)current.getAdapter());
897                 adapter.setSelectedItems(currentAdapter.getSelectedItems());
898                 currentAdapter.dispose();
899             }
900             removeView(current);
901         }
902         this.mFiles = files;
903         adapter.addAll(files);
904
905         //Set the adapter
906         this.mAdapter = adapter;
907         newView.setAdapter(this.mAdapter);
908         newView.setOnItemClickListener(NavigationView.this);
909
910         //Add the new layout
911         this.mAdapterView = newView;
912         addView(newView, 0);
913         this.mCurrentMode = newMode;
914
915         // Pick mode doesn't implements the onlongclick
916         if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
917             this.mAdapterView.setOnItemLongClickListener(this);
918         } else {
919             this.mAdapterView.setOnItemLongClickListener(null);
920         }
921
922         //Save the preference (only in navigation browse mode)
923         if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
924             try {
925                 Preferences.savePreference(
926                         FileManagerSettings.SETTINGS_LAYOUT_MODE, newMode, true);
927             } catch (Exception ex) {
928                 Log.e(TAG, "Save of view mode preference fails", ex); //$NON-NLS-1$
929             }
930         }
931     }
932
933     /**
934      * Method that removes a {@link FileSystemObject} from the view
935      *
936      * @param fso The file system object
937      */
938     public void removeItem(FileSystemObject fso) {
939         // Delete also from internal list
940         if (fso != null) {
941             int cc = this.mFiles.size()-1;
942             for (int i = cc; i >= 0; i--) {
943                 FileSystemObject f = this.mFiles.get(i);
944                 if (f != null && f.compareTo(fso) == 0) {
945                     this.mFiles.remove(i);
946                     break;
947                 }
948             }
949         }
950         this.mAdapter.remove(fso);
951     }
952
953     /**
954      * Method that removes a file system object from his path from the view
955      *
956      * @param path The file system object path
957      */
958     public void removeItem(String path) {
959         FileSystemObject fso = this.mAdapter.getItem(path);
960         if (fso != null) {
961             this.mAdapter.remove(fso);
962         }
963     }
964
965     /**
966      * Method that returns the current directory.
967      *
968      * @return String The current directory
969      */
970     public String getCurrentDir() {
971         return this.mCurrentDir;
972     }
973
974     /**
975      * Method that changes the current directory of the view.
976      *
977      * @param newDir The new directory location
978      */
979     public void changeCurrentDir(final String newDir) {
980         changeCurrentDir(newDir, true, false, false, null, null);
981     }
982
983     /**
984      * Method that changes the current directory of the view.
985      *
986      * @param newDir The new directory location
987      * @param addToHistory Add the directory to history
988      */
989     public void changeCurrentDir(final String newDir, boolean addToHistory) {
990         changeCurrentDir(newDir, addToHistory, false, false, null, null);
991     }
992
993     /**
994      * Method that changes the current directory of the view.
995      *
996      * @param newDir The new directory location
997      * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
998      */
999     public void changeCurrentDir(final String newDir, SearchInfoParcelable searchInfo) {
1000         changeCurrentDir(newDir, true, false, false, searchInfo, null);
1001     }
1002
1003     /**
1004      * Method that changes the current directory of the view.
1005      *
1006      * @param newDir The new directory location
1007      * @param addToHistory Add the directory to history
1008      * @param reload Force the reload of the data
1009      * @param useCurrent If this method must use the actual data (for back actions)
1010      * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
1011      * @param scrollTo If not null, then listview must scroll to this item
1012      */
1013     private void changeCurrentDir(
1014             final String newDir, final boolean addToHistory,
1015             final boolean reload, final boolean useCurrent,
1016             final SearchInfoParcelable searchInfo, final FileSystemObject scrollTo) {
1017         NavigationTask task = new NavigationTask(useCurrent, addToHistory, reload,
1018                 searchInfo, scrollTo);
1019         task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, newDir);
1020     }
1021
1022     /**
1023      * Remove all unmounted files in the current selection
1024      */
1025     public void removeUnmountedSelection() {
1026         List<FileSystemObject> selection = mAdapter.getSelectedItems();
1027         int cc = selection.size() - 1;
1028         for (int i = cc; i >= 0; i--) {
1029             FileSystemObject item = selection.get(i);
1030             VirtualMountPointConsole vc =
1031                     VirtualMountPointConsole.getVirtualConsoleForPath(item.getFullPath());
1032             if (vc != null && !vc.isMounted()) {
1033                 selection.remove(i);
1034             }
1035         }
1036         mAdapter.setSelectedItems(selection);
1037         mAdapter.notifyDataSetChanged();
1038
1039         // Do not call the selection listener. This method is supposed to be called by the
1040         // listener itself
1041     }
1042
1043
1044     /**
1045      * Method invoked when a execution ends.
1046      *
1047      * @param files The files obtains from the list
1048      * @param addToHistory If add path to history
1049      * @param isNewHistory If is new history
1050      * @param hasChanged If current directory was changed
1051      * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
1052      * @param newDir The new directory
1053      * @param scrollTo If not null, then listview must scroll to this item
1054      * @hide
1055      */
1056     void onPostExecuteTask(
1057             List<FileSystemObject> files, boolean addToHistory, boolean isNewHistory,
1058             boolean hasChanged, SearchInfoParcelable searchInfo,
1059             String newDir, final FileSystemObject scrollTo) {
1060         try {
1061             //Check that there is not errors and have some data
1062             if (files == null) {
1063                 return;
1064             }
1065
1066             //Apply user preferences
1067             List<FileSystemObject> sortedFiles =
1068                     FileHelper.applyUserPreferences(files, this.mRestrictions, this.mChRooted);
1069
1070             //Remove parent directory if we are in the root of a chrooted environment
1071             if (this.mChRooted && StorageHelper.isStorageVolume(newDir)) {
1072                 if (files.size() > 0 && files.get(0) instanceof ParentDirectory) {
1073                     files.remove(0);
1074                 }
1075             }
1076
1077             //Add to history?
1078             if (addToHistory && hasChanged && isNewHistory) {
1079                 if (this.mOnHistoryListener != null) {
1080                     //Communicate the need of a history change
1081                     this.mOnHistoryListener.onNewHistory(onSaveState());
1082                 }
1083             }
1084
1085             //Load the data
1086             loadData(sortedFiles);
1087             this.mFiles = sortedFiles;
1088             if (searchInfo != null) {
1089                 searchInfo.setSuccessNavigation(true);
1090             }
1091
1092             //Change the breadcrumb
1093             if (this.mBreadcrumb != null) {
1094                 this.mBreadcrumb.changeBreadcrumbPath(newDir, this.mChRooted);
1095             }
1096
1097             //If scrollTo is null, the position will be set to 0
1098             scrollTo(scrollTo);
1099
1100             //The current directory is now the "newDir"
1101             this.mCurrentDir = newDir;
1102             if (this.mOnDirectoryChangedListener != null) {
1103                 FileSystemObject dir = FileHelper.createFileSystemObject(new File(newDir));
1104                 this.mOnDirectoryChangedListener.onDirectoryChanged(dir);
1105             }
1106         } finally {
1107             //If calling activity is search, then save the search history
1108             if (searchInfo != null) {
1109                 this.mOnHistoryListener.onNewHistory(searchInfo);
1110             }
1111
1112             //End of loading data
1113             try {
1114                 NavigationView.this.mBreadcrumb.endLoading();
1115             } catch (Throwable ex) {
1116                 /**NON BLOCK**/
1117             }
1118         }
1119     }
1120
1121     /**
1122      * Method that loads the files in the adapter.
1123      *
1124      * @param files The files to load in the adapter
1125      * @hide
1126      */
1127     @SuppressWarnings("unchecked")
1128     private void loadData(final List<FileSystemObject> files) {
1129         //Notify data to adapter view
1130         final AdapterView<ListAdapter> view =
1131                 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
1132         FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)view.getAdapter();
1133         adapter.setNotifyOnChange(false);
1134         adapter.clear();
1135         adapter.addAll(files);
1136         adapter.notifyDataSetChanged();
1137     }
1138
1139     /**
1140      * {@inheritDoc}
1141      */
1142     @Override
1143     public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
1144         // Different actions depending on user preference
1145
1146         // Get the adapter and the fso
1147         FileSystemObjectAdapter adapter = ((FileSystemObjectAdapter)parent.getAdapter());
1148         FileSystemObject fso = adapter.getItem(position);
1149
1150         // Parent directory hasn't actions
1151         if (fso instanceof ParentDirectory) {
1152             return false;
1153         }
1154
1155         // Pick mode doesn't implements the onlongclick
1156         if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
1157             return false;
1158         }
1159
1160         onRequestMenu(fso);
1161         return true; //Always consume the event
1162     }
1163
1164     /**
1165      * Method that opens or navigates to the {@link FileSystemObject}
1166      *
1167      * @param fso The file system object
1168      */
1169     public void open(FileSystemObject fso) {
1170         open(fso, null);
1171     }
1172
1173     /**
1174      * Method that opens or navigates to the {@link FileSystemObject}
1175      *
1176      * @param fso The file system object
1177      * @param searchInfo The search info
1178      */
1179     public void open(FileSystemObject fso, SearchInfoParcelable searchInfo) {
1180         // If is a folder, then navigate to
1181         if (FileHelper.isDirectory(fso)) {
1182             changeCurrentDir(fso.getFullPath(), searchInfo);
1183         } else {
1184             // Open the file with the preferred registered app
1185             IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1186         }
1187     }
1188
1189     /**
1190      * {@inheritDoc}
1191      */
1192     @Override
1193     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1194         try {
1195             FileSystemObject fso = ((FileSystemObjectAdapter)parent.getAdapter()).getItem(position);
1196             if (fso instanceof ParentDirectory) {
1197                 changeCurrentDir(fso.getParent(), true, false, false, null, null);
1198                 return;
1199             } else if (fso instanceof Directory) {
1200                 changeCurrentDir(fso.getFullPath(), true, false, false, null, null);
1201                 return;
1202             } else if (fso instanceof Symlink) {
1203                 Symlink symlink = (Symlink)fso;
1204                 if (symlink.getLinkRef() != null && symlink.getLinkRef() instanceof Directory) {
1205                     changeCurrentDir(
1206                             symlink.getLinkRef().getFullPath(), true, false, false, null, null);
1207                     return;
1208                 }
1209
1210                 // Open the link ref
1211                 fso = symlink.getLinkRef();
1212             }
1213
1214             // Open the file (edit or pick)
1215             if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
1216                 // Open the file with the preferred registered app
1217                 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1218             } else {
1219                 // Request a file pick selection
1220                 if (this.mOnFilePickedListener != null) {
1221                     this.mOnFilePickedListener.onFilePicked(fso);
1222                 }
1223             }
1224         } catch (Throwable ex) {
1225             ExceptionUtil.translateException(getContext(), ex);
1226         }
1227     }
1228
1229     /**
1230      * {@inheritDoc}
1231      */
1232     @Override
1233     public void onRequestRefresh(Object o, boolean clearSelection) {
1234         if (o instanceof FileSystemObject) {
1235             refresh((FileSystemObject)o);
1236         } else if (o == null) {
1237             refresh();
1238         }
1239         if (clearSelection) {
1240             onDeselectAll();
1241         }
1242     }
1243
1244     /**
1245      * {@inheritDoc}
1246      */
1247     @Override
1248     public void onRequestBookmarksRefresh() {
1249         // Ignore
1250     }
1251
1252     /**
1253      * {@inheritDoc}
1254      */
1255     @Override
1256     public void onRequestRemove(Object o, boolean clearSelection) {
1257         if (o != null && o instanceof FileSystemObject) {
1258             removeItem((FileSystemObject)o);
1259         } else {
1260             onRequestRefresh(null, clearSelection);
1261         }
1262         if (clearSelection) {
1263             onDeselectAll();
1264         }
1265     }
1266
1267     /**
1268      * {@inheritDoc}
1269      */
1270     @Override
1271     public void onNavigateTo(Object o) {
1272         // Ignored
1273     }
1274
1275     @Override
1276     public void onCancel() {
1277         // nop
1278     }
1279
1280     /**
1281      * {@inheritDoc}
1282      */
1283     @Override
1284     public void onBreadcrumbItemClick(BreadcrumbItem item) {
1285         changeCurrentDir(item.getItemPath(), true, true, false, null, null);
1286     }
1287
1288     /**
1289      * {@inheritDoc}
1290      */
1291     @Override
1292     public void onSelectionChanged(final List<FileSystemObject> selectedItems) {
1293         if (this.mOnNavigationSelectionChangedListener != null) {
1294             this.mOnNavigationSelectionChangedListener.onSelectionChanged(this, selectedItems);
1295         }
1296     }
1297
1298     /**
1299      * Method invoked when a request to show the menu associated
1300      * with an item is started.
1301      *
1302      * @param item The item for which the request was started
1303      */
1304     public void onRequestMenu(final FileSystemObject item) {
1305         if (this.mOnNavigationRequestMenuListener != null) {
1306             this.mOnNavigationRequestMenuListener.onRequestMenu(this, item);
1307         }
1308     }
1309
1310     /**
1311      * {@inheritDoc}
1312      */
1313     @Override
1314     public void onToggleSelection(FileSystemObject fso) {
1315         if (this.mAdapter != null) {
1316             this.mAdapter.toggleSelection(fso);
1317         }
1318     }
1319
1320     /**
1321      * {@inheritDoc}
1322      */
1323     @Override
1324     public void onDeselectAll() {
1325         if (this.mAdapter != null) {
1326             this.mAdapter.deselectedAll();
1327         }
1328     }
1329
1330     /**
1331      * {@inheritDoc}
1332      */
1333     @Override
1334     public void onSelectAllVisibleItems() {
1335         if (this.mAdapter != null) {
1336             this.mAdapter.selectedAllVisibleItems();
1337         }
1338     }
1339
1340     /**
1341      * {@inheritDoc}
1342      */
1343     @Override
1344     public void onDeselectAllVisibleItems() {
1345         if (this.mAdapter != null) {
1346             this.mAdapter.deselectedAllVisibleItems();
1347         }
1348     }
1349
1350     /**
1351      * {@inheritDoc}
1352      */
1353     @Override
1354     public List<FileSystemObject> onRequestSelectedFiles() {
1355         return this.getSelectedFiles();
1356     }
1357
1358     /**
1359      * {@inheritDoc}
1360      */
1361     @Override
1362     public List<FileSystemObject> onRequestCurrentItems() {
1363         return this.getFiles();
1364     }
1365
1366     /**
1367      * {@inheritDoc}
1368      */
1369     @Override
1370     public String onRequestCurrentDir() {
1371         return this.mCurrentDir;
1372     }
1373
1374     /**
1375      * Method that creates a ChRooted environment, protecting the user to break anything
1376      * in the device
1377      * @hide
1378      */
1379     public void createChRooted() {
1380         // If we are in a ChRooted environment, then do nothing
1381         if (this.mChRooted) return;
1382         this.mChRooted = true;
1383
1384         //Change to first storage volume
1385         StorageVolume[] volumes =
1386                 StorageHelper.getStorageVolumes(getContext(), false);
1387         if (volumes != null && volumes.length > 0) {
1388             changeCurrentDir(volumes[0].getPath(), false, true, false, null, null);
1389         }
1390     }
1391
1392     /**
1393      * Method that exits from a ChRooted environment
1394      * @hide
1395      */
1396     public void exitChRooted() {
1397         // If we aren't in a ChRooted environment, then do nothing
1398         if (!this.mChRooted) return;
1399         this.mChRooted = false;
1400
1401         // Refresh
1402         refresh();
1403     }
1404
1405     /**
1406      * Method that ensures that the user don't go outside the ChRooted environment
1407      *
1408      * @param newDir The new directory to navigate to
1409      * @return String
1410      */
1411     private String checkChRootedNavigation(String newDir) {
1412         // If we aren't in ChRooted environment, then there is nothing to check
1413         if (!this.mChRooted) return newDir;
1414
1415         // Check if the path is owned by one of the storage volumes
1416         if (!StorageHelper.isPathInStorageVolume(newDir)) {
1417             StorageVolume[] volumes = StorageHelper.getStorageVolumes(getContext(), false);
1418             if (volumes != null && volumes.length > 0) {
1419                 return volumes[0].getPath();
1420             }
1421         }
1422         return newDir;
1423     }
1424
1425     /**
1426      * Method that applies the current theme to the activity
1427      */
1428     public void applyTheme() {
1429         //- Breadcrumb
1430         if (getBreadcrumb() != null) {
1431             getBreadcrumb().applyTheme();
1432         }
1433
1434         //- Redraw the adapter view
1435         Theme theme = ThemeManager.getCurrentTheme(getContext());
1436         theme.setBackgroundDrawable(getContext(), this, "background_drawable"); //$NON-NLS-1$
1437         if (this.mAdapter != null) {
1438             this.mAdapter.notifyThemeChanged();
1439         }
1440         if (this.mAdapterView instanceof ListView) {
1441             ((ListView)this.mAdapterView).setDivider(
1442                     theme.getDrawable(getContext(), "horizontal_divider_drawable")); //$NON-NLS-1$
1443         }
1444         refresh();
1445     }
1446
1447 }