OSDN Git Service

Add special case for Gallery2 when opening image files.
[android-x86/packages-apps-CMFileManager.git] / src / com / cyanogenmod / filemanager / ui / policy / IntentsActionPolicy.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.policy;
18
19 import android.content.ComponentName;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.content.DialogInterface.OnCancelListener;
23 import android.content.DialogInterface.OnDismissListener;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ResolveInfo;
28 import android.net.Uri;
29 import android.os.Bundle;
30 import android.provider.MediaStore;
31 import android.util.Log;
32 import android.widget.Toast;
33
34 import com.cyanogenmod.filemanager.R;
35 import com.cyanogenmod.filemanager.activities.ShortcutActivity;
36 import com.cyanogenmod.filemanager.console.secure.SecureConsole;
37 import com.cyanogenmod.filemanager.model.FileSystemObject;
38 import com.cyanogenmod.filemanager.model.RegularFile;
39 import com.cyanogenmod.filemanager.providers.SecureResourceProvider;
40 import com.cyanogenmod.filemanager.providers.SecureResourceProvider.AuthorizationResource;
41 import com.cyanogenmod.filemanager.ui.dialogs.AssociationsDialog;
42 import com.cyanogenmod.filemanager.util.DialogHelper;
43 import com.cyanogenmod.filemanager.util.ExceptionUtil;
44 import com.cyanogenmod.filemanager.util.FileHelper;
45 import com.cyanogenmod.filemanager.util.MediaHelper;
46 import com.cyanogenmod.filemanager.util.MimeTypeHelper;
47 import com.cyanogenmod.filemanager.util.MimeTypeHelper.MimeTypeCategory;
48 import com.cyanogenmod.filemanager.util.ResourcesHelper;
49
50 import java.io.File;
51 import java.util.ArrayList;
52 import java.util.Collections;
53 import java.util.Comparator;
54 import java.util.List;
55
56 /**
57  * A class with the convenience methods for resolve intents related actions
58  */
59 public final class IntentsActionPolicy extends ActionsPolicy {
60
61     private static final String TAG = "IntentsActionPolicy"; //$NON-NLS-1$
62
63     private static boolean DEBUG = false;
64
65     // The preferred package when sorting intents
66     private static final String PREFERRED_PACKAGE = "com.cyanogenmod.filemanager"; //$NON-NLS-1$
67
68     /**
69      * Extra field for the internal action
70      */
71     public static final String EXTRA_INTERNAL_ACTION =
72             "com.cyanogenmod.filemanager.extra.INTERNAL_ACTION"; //$NON-NLS-1$
73
74     /**
75      * Category for all the internal app viewers
76      */
77     public static final String CATEGORY_INTERNAL_VIEWER =
78             "com.cyanogenmod.filemanager.category.INTERNAL_VIEWER"; //$NON-NLS-1$
79
80     /**
81      * Category for all the app editor
82      */
83     public static final String CATEGORY_EDITOR =
84             "com.cyanogenmod.filemanager.category.EDITOR"; //$NON-NLS-1$
85
86     /**
87      * The package name of Gallery2.
88      */
89     public static final String GALLERY2_PACKAGE = "com.android.gallery3d";
90
91     /**
92      * Method that opens a {@link FileSystemObject} with the default registered application
93      * by the system, or ask the user for select a registered application.
94      *
95      * @param ctx The current context
96      * @param fso The file system object
97      * @param choose If allow the user to select the application to open with
98      * @param onCancelListener The cancel listener
99      * @param onDismissListener The dismiss listener
100      */
101     public static void openFileSystemObject(
102             final Context ctx, final FileSystemObject fso, final boolean choose,
103             OnCancelListener onCancelListener, OnDismissListener onDismissListener) {
104         try {
105             // Create the intent to open the file
106             Intent intent = new Intent();
107             intent.setAction(android.content.Intent.ACTION_VIEW);
108
109             // Obtain the mime/type and passed it to intent
110             String mime = MimeTypeHelper.getMimeType(ctx, fso);
111             if (mime != null) {
112                 intent.setDataAndType(getUriFromFile(ctx, fso), mime);
113             } else {
114                 intent.setData(getUriFromFile(ctx, fso));
115             }
116
117             // Resolve the intent
118             resolveIntent(
119                     ctx,
120                     intent,
121                     choose,
122                     createInternalIntents(ctx,  fso),
123                     0,
124                     R.string.associations_dialog_openwith_title,
125                     R.string.associations_dialog_openwith_action,
126                     true, onCancelListener, onDismissListener);
127
128         } catch (Exception e) {
129             ExceptionUtil.translateException(ctx, e);
130         }
131     }
132
133     /**
134      * Method that sends a {@link FileSystemObject} with the default registered application
135      * by the system, or ask the user for select a registered application.
136      *
137      * @param ctx The current context
138      * @param fso The file system object
139      * @param onCancelListener The cancel listener
140      * @param onDismissListener The dismiss listener
141      */
142     public static void sendFileSystemObject(
143             final Context ctx, final FileSystemObject fso,
144             OnCancelListener onCancelListener, OnDismissListener onDismissListener) {
145         try {
146             // Create the intent to
147             Intent intent = new Intent();
148             intent.setAction(android.content.Intent.ACTION_SEND);
149             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
150             intent.setType(MimeTypeHelper.getMimeType(ctx, fso));
151             Uri uri = getUriFromFile(ctx, fso);
152             intent.putExtra(Intent.EXTRA_STREAM, uri);
153
154             // Resolve the intent
155             resolveIntent(
156                     ctx,
157                     intent,
158                     true,
159                     null,
160                     0,
161                     R.string.associations_dialog_sendwith_title,
162                     R.string.associations_dialog_sendwith_action,
163                     false, onCancelListener, onDismissListener);
164
165         } catch (Exception e) {
166             ExceptionUtil.translateException(ctx, e);
167         }
168     }
169
170     /**
171      * Method that sends a {@link FileSystemObject} with the default registered application
172      * by the system, or ask the user for select a registered application.
173      *
174      * @param ctx The current context
175      * @param fsos The file system objects
176      * @param onCancelListener The cancel listener
177      * @param onDismissListener The dismiss listener
178      */
179     public static void sendMultipleFileSystemObject(
180             final Context ctx, final List<FileSystemObject> fsos,
181             OnCancelListener onCancelListener, OnDismissListener onDismissListener) {
182         try {
183             // Create the intent to
184             Intent intent = new Intent();
185             intent.setAction(android.content.Intent.ACTION_SEND_MULTIPLE);
186             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
187
188             // Create an array list of the uris to send
189             ArrayList<Uri> uris = new ArrayList<Uri>();
190             int cc = fsos.size();
191             String lastMimeType = null;
192             boolean sameMimeType = true;
193             for (int i = 0; i < cc; i++) {
194                 FileSystemObject fso = fsos.get(i);
195
196                 // Folders are not allowed
197                 if (FileHelper.isDirectory(fso)) continue;
198
199                 // Check if we can use a unique mime/type
200                 String mimeType = MimeTypeHelper.getMimeType(ctx, fso);
201                 if (mimeType == null) {
202                     sameMimeType = false;
203                 }
204                 if (sameMimeType &&
205                     (mimeType != null && lastMimeType != null &&
206                      mimeType.compareTo(lastMimeType) != 0)) {
207                     sameMimeType = false;
208                 }
209                 lastMimeType = mimeType;
210
211                 // Add the uri
212                 uris.add(getUriFromFile(ctx, fso));
213             }
214             if (sameMimeType) {
215                 intent.setType(lastMimeType);
216             } else {
217                 intent.setType(MimeTypeHelper.ALL_MIME_TYPES);
218             }
219             intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
220
221             // Resolve the intent
222             resolveIntent(
223                     ctx,
224                     intent,
225                     true,
226                     null,
227                     0,
228                     R.string.associations_dialog_sendwith_title,
229                     R.string.associations_dialog_sendwith_action,
230                     false, onCancelListener, onDismissListener);
231
232         } catch (Exception e) {
233             ExceptionUtil.translateException(ctx, e);
234         }
235     }
236
237     /**
238      * Method that resolve
239      *
240      * @param ctx The current context
241      * @param intent The intent to resolve
242      * @param choose If allow the user to select the application to select the registered
243      * application. If no preferred app or more than one exists the dialog is shown.
244      * @param internals The list of internals intents that can handle the action
245      * @param icon The icon of the dialog
246      * @param title The title of the dialog
247      * @param action The button title of the dialog
248      * @param allowPreferred If allow the user to mark the selected app as preferred
249      * @param onCancelListener The cancel listener
250      * @param onDismissListener The dismiss listener
251      */
252     private static void resolveIntent(
253             Context ctx, Intent intent, boolean choose, List<Intent> internals,
254             int icon, int title, int action, boolean allowPreferred,
255             OnCancelListener onCancelListener, OnDismissListener onDismissListener) {
256         //Retrieve the activities that can handle the file
257         final PackageManager packageManager = ctx.getPackageManager();
258         if (DEBUG) {
259             intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
260         }
261         List<ResolveInfo> info =
262                 packageManager.
263                     queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
264         Collections.sort(info, new Comparator<ResolveInfo>() {
265             @Override
266             public int compare(ResolveInfo lhs, ResolveInfo rhs) {
267                 boolean isLshCMFM =
268                         lhs.activityInfo.packageName.compareTo(PREFERRED_PACKAGE) == 0;
269                 boolean isRshCMFM =
270                         rhs.activityInfo.packageName.compareTo(PREFERRED_PACKAGE) == 0;
271                 if (isLshCMFM && !isRshCMFM) {
272                     return -1;
273                 }
274                 if (!isLshCMFM && isRshCMFM) {
275                     return 1;
276                 }
277                 return lhs.activityInfo.name.compareTo(rhs.activityInfo.name);
278             }
279         });
280
281         // Add the internal editors
282         int count = 0;
283         if (internals != null) {
284             int cc = internals.size();
285             for (int i = 0; i < cc; i++) {
286                 Intent ii = internals.get(i);
287                 List<ResolveInfo> ie =
288                         packageManager.
289                             queryIntentActivities(ii, 0);
290                 if (ie.size() > 0) {
291                     ResolveInfo rie = ie.get(0);
292
293                     // Only if the internal is not in the query list
294                     boolean exists = false;
295                     int ccc = info.size();
296                     for (int j = 0; j < ccc; j++) {
297                         ResolveInfo ri = info.get(j);
298                         if (ri.activityInfo.packageName.compareTo(
299                                 rie.activityInfo.packageName) == 0 &&
300                             ri.activityInfo.name.compareTo(
301                                     rie.activityInfo.name) == 0) {
302
303                             // Mark as internal
304                             if (ri.activityInfo.metaData == null) {
305                                 ri.activityInfo.metaData = new Bundle();
306                                 ri.activityInfo.metaData.putString(
307                                         EXTRA_INTERNAL_ACTION, ii.getAction());
308                                 ri.activityInfo.metaData.putBoolean(
309                                         CATEGORY_INTERNAL_VIEWER, true);
310                             }
311                             exists = true;
312                             break;
313                         }
314                     }
315                     if (exists) {
316                         continue;
317                     }
318
319                     // Mark as internal
320                     if (rie.activityInfo.metaData == null) {
321                         rie.activityInfo.metaData = new Bundle();
322                         rie.activityInfo.metaData.putString(EXTRA_INTERNAL_ACTION, ii.getAction());
323                         rie.activityInfo.metaData.putBoolean(CATEGORY_INTERNAL_VIEWER, true);
324                     }
325
326                     // Only one result must be matched
327                     info.add(count, rie);
328                     count++;
329                 }
330             }
331         }
332
333         // No registered application
334         if (info.size() == 0) {
335             DialogHelper.showToast(ctx, R.string.msgs_not_registered_app, Toast.LENGTH_SHORT);
336             if (onDismissListener != null) {
337                 onDismissListener.onDismiss(null);
338             }
339             return;
340         }
341
342         // Retrieve the preferred activity that can handle the file. We only want the
343         // resolved activity if the activity is a preferred activity. Other case, the
344         // resolved activity was never added by addPreferredActivity
345         ResolveInfo mPreferredInfo = findPreferredActivity(ctx, intent, info);
346
347         // Is a simple open and we have an application that can handle the file?
348         //---
349         // If we have a preferred application, then use it
350         if (!choose && (mPreferredInfo  != null && mPreferredInfo.match != 0)) {
351             ctx.startActivity(getIntentFromResolveInfo(mPreferredInfo, intent));
352             if (onDismissListener != null) {
353                 onDismissListener.onDismiss(null);
354             }
355             return;
356         }
357         // If there are only one activity (app or internal editor), then use it
358         if (!choose && info.size() == 1) {
359             ResolveInfo ri = info.get(0);
360             ctx.startActivity(getIntentFromResolveInfo(ri, intent));
361             if (onDismissListener != null) {
362                 onDismissListener.onDismiss(null);
363             }
364             return;
365         }
366
367         // If we have multiples apps and there is not a preferred application then show
368         // open with dialog
369         AssociationsDialog dialog =
370                 new AssociationsDialog(
371                         ctx,
372                         icon,
373                         ctx.getString(title),
374                         ctx.getString(action),
375                         intent,
376                         info,
377                         mPreferredInfo,
378                         allowPreferred,
379                         onCancelListener,
380                         onDismissListener);
381         dialog.show();
382     }
383
384     /**
385      * Method that creates a shortcut in the desktop of the device of {@link FileSystemObject}.
386      *
387      * @param ctx The current context
388      * @param fso The file system object
389      */
390     public static void createShortcut(Context ctx, FileSystemObject fso) {
391         try {
392             // Create the intent that will handle the shortcut
393             Intent shortcutIntent = new Intent(ctx, ShortcutActivity.class);
394             shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
395             shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
396             if (FileHelper.isDirectory(fso)) {
397                 shortcutIntent.putExtra(
398                         ShortcutActivity.EXTRA_TYPE,ShortcutActivity.SHORTCUT_TYPE_NAVIGATE);
399             } else {
400                 shortcutIntent.putExtra(
401                         ShortcutActivity.EXTRA_TYPE, ShortcutActivity.SHORTCUT_TYPE_OPEN);
402             }
403             shortcutIntent.putExtra(ShortcutActivity.EXTRA_FSO, fso.getFullPath());
404
405             // Obtain the icon drawable (don't use here the themeable drawable)
406             String resid = MimeTypeHelper.getIcon(ctx, fso);
407             int dwid =
408                     ResourcesHelper.getIdentifier(
409                             ctx.getResources(), "drawable", resid); //$NON-NLS-1$
410
411             // The intent to send to broadcast for register the shortcut intent
412             Intent intent = new Intent();
413             intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
414             intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, fso.getName());
415             intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
416                     Intent.ShortcutIconResource.fromContext(ctx, dwid));
417             intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); //$NON-NLS-1$
418             ctx.sendBroadcast(intent);
419
420             // Show the confirmation
421             DialogHelper.showToast(
422                     ctx, R.string.shortcut_creation_success_msg, Toast.LENGTH_SHORT);
423
424         } catch (Exception e) {
425             Log.e(TAG, "Failed to create the shortcut", e); //$NON-NLS-1$
426             DialogHelper.showToast(
427                     ctx, R.string.shortcut_creation_failed_msg, Toast.LENGTH_SHORT);
428         }
429     }
430
431     /**
432      * This method creates a list of internal activities that could handle the fso.
433      *
434      * @param ctx The current context
435      * @param fso The file system object to open
436      */
437     private static List<Intent> createInternalIntents(Context ctx, FileSystemObject fso) {
438         List<Intent> intents = new ArrayList<Intent>();
439         intents.addAll(createEditorIntent(ctx, fso));
440         return intents;
441     }
442
443     /**
444      * This method creates a list of internal activities for editing files
445      *
446      * @param ctx The current context
447      * @param fso FileSystemObject
448      */
449     private static List<Intent> createEditorIntent(Context ctx, FileSystemObject fso) {
450         List<Intent> intents = new ArrayList<Intent>();
451         MimeTypeCategory category = MimeTypeHelper.getCategory(ctx, fso);
452
453         //- Internal Editor. This editor can handle TEXT and NONE mime categories but
454         //  not system files, directories, ..., only regular files (no symlinks)
455         if (fso instanceof RegularFile &&
456             (category.compareTo(MimeTypeCategory.NONE) == 0 ||
457              category.compareTo(MimeTypeCategory.EXEC) == 0 ||
458              category.compareTo(MimeTypeCategory.TEXT) == 0)) {
459             Intent editorIntent = new Intent();
460             editorIntent.setAction(Intent.ACTION_VIEW);
461             editorIntent.addCategory(CATEGORY_INTERNAL_VIEWER);
462             editorIntent.addCategory(CATEGORY_EDITOR);
463             intents.add(editorIntent);
464         }
465
466         return intents;
467     }
468
469     /**
470      * Method that returns an {@link Intent} from his {@link ResolveInfo}
471      *
472      * @param ri The ResolveInfo
473      * @param request The requested intent
474      * @return Intent The intent
475      */
476     public static final Intent getIntentFromResolveInfo(ResolveInfo ri, Intent request) {
477         Intent intent =
478                 getIntentFromComponentName(
479                     new ComponentName(
480                         ri.activityInfo.applicationInfo.packageName,
481                         ri.activityInfo.name),
482                     request);
483         boolean isInternalEditor = isInternalEditor(ri);
484         if (isInternalEditor) {
485             String a = Intent.ACTION_VIEW;
486             if (ri.activityInfo.metaData != null) {
487                 a = ri.activityInfo.metaData.getString(
488                         IntentsActionPolicy.EXTRA_INTERNAL_ACTION,
489                         Intent.ACTION_VIEW);
490             }
491             intent.setAction(a);
492         } else {
493             // Opening image files with Gallery2 will behave incorrectly when started
494             // as a new task. We want to be able to return to CMFM with the back button.
495             if (!(Intent.ACTION_VIEW.equals(intent.getAction())
496                   && isGallery2(ri)
497                   && intent.getData() != null
498                   && MediaStore.AUTHORITY.equals(intent.getData().getAuthority()))) {
499                 // Create a new stack for the activity
500                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
501             }
502             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
503         }
504
505         // Grant access to resources if needed
506         grantSecureAccessIfNeeded(intent, ri);
507
508         return intent;
509     }
510
511     /**
512      * Method that add grant access to secure resources if needed
513      *
514      * @param intent The intent to grant access
515      * @param ri The resolved info associated with the intent
516      */
517     public static final void grantSecureAccessIfNeeded(Intent intent, ResolveInfo ri) {
518         // If this intent will be serve by the SecureResourceProvider then this uri must
519         // be granted before we start it, only for external apps. The internal editor
520         // must receive an file scheme uri
521         Uri uri = intent.getData();
522         String authority = null;
523         if (uri != null) {
524             authority = uri.getAuthority();
525             grantSecureAccess(intent, authority, ri, uri);
526         } else if (intent.getExtras() != null) {
527             Object obj = intent.getExtras().get(Intent.EXTRA_STREAM);
528             if (obj instanceof Uri) {
529                 uri = (Uri) intent.getExtras().get(Intent.EXTRA_STREAM);
530                 authority = uri.getAuthority();
531                 grantSecureAccess(intent, authority, ri, uri);
532             } else if (obj instanceof ArrayList) {
533                 ArrayList<Uri> uris = (ArrayList<Uri>) intent.getExtras().get(Intent.EXTRA_STREAM);
534                 for (Uri u : uris) {
535                     authority = u.getAuthority();
536                     grantSecureAccess(intent, authority, ri, u);
537                 }
538             }
539         }
540     }
541
542     private static final void grantSecureAccess(Intent intent, String authority, ResolveInfo ri,
543             Uri uri) {
544         if (authority != null && authority.equals(SecureResourceProvider.AUTHORITY)) {
545             boolean isInternalEditor = isInternalEditor(ri);
546             if (isInternalEditor) {
547                 // remove the authorization and change request to file scheme
548                 AuthorizationResource auth = SecureResourceProvider.revertAuthorization(uri);
549                 intent.setData(Uri.fromFile(new File(auth.mFile.getFullPath())));
550
551             } else {
552                 // Grant access to the package
553                 SecureResourceProvider.grantAuthorizationUri(uri,
554                         ri.activityInfo.applicationInfo.packageName);
555             }
556         }
557     }
558
559     /**
560      * Method that returns an {@link Intent} from his {@link ComponentName}
561      *
562      * @param cn The ComponentName
563      * @param request The requested intent
564      * @return Intent The intent
565      */
566     public static final Intent getIntentFromComponentName(ComponentName cn, Intent request) {
567         Intent intent = new Intent(request);
568         intent.setFlags(
569                 intent.getFlags() &~
570                 Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
571         intent.addFlags(
572                 Intent.FLAG_ACTIVITY_FORWARD_RESULT |
573                 Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
574         intent.setComponent(
575                 new ComponentName(
576                         cn.getPackageName(),
577                         cn.getClassName()));
578         return intent;
579     }
580
581     /**
582      * Method that returns if the selected resolve info is about an internal viewer
583      *
584      * @param ri The resolve info
585      * @return boolean  If the selected resolve info is about an internal viewer
586      * @hide
587      */
588     public static final boolean isInternalEditor(ResolveInfo ri) {
589         return ri.activityInfo.metaData != null &&
590                 ri.activityInfo.metaData.getBoolean(
591                         IntentsActionPolicy.CATEGORY_INTERNAL_VIEWER, false);
592     }
593
594     public static final boolean isGallery2(ResolveInfo ri) {
595         return GALLERY2_PACKAGE.equals(ri.activityInfo.packageName);
596     }
597
598     /**
599      * Method that retrieve the finds the preferred activity, if one exists. In case
600      * of multiple preferred activity exists the try to choose the better
601      *
602      * @param ctx The current context
603      * @param intent The query intent
604      * @param info The initial info list
605      * @return ResolveInfo The resolved info
606      */
607     private static final ResolveInfo findPreferredActivity(
608             Context ctx, Intent intent, List<ResolveInfo> info) {
609
610         final PackageManager packageManager = ctx.getPackageManager();
611
612         // Retrieve the preferred activity that can handle the file. We only want the
613         // resolved activity if the activity is a preferred activity. Other case, the
614         // resolved activity was never added by addPreferredActivity
615         List<ResolveInfo> pref = new ArrayList<ResolveInfo>();
616         int cc = info.size();
617         for (int i = 0; i < cc; i++) {
618             ResolveInfo ri = info.get(i);
619             if (isInternalEditor(ri)) continue;
620             if (ri.activityInfo == null || ri.activityInfo.packageName == null) continue;
621             List<ComponentName> prefActList = new ArrayList<ComponentName>();
622             List<IntentFilter> intentList = new ArrayList<IntentFilter>();
623             IntentFilter filter = new IntentFilter();
624             filter.addAction(intent.getAction());
625             try {
626                 filter.addDataType(intent.getType());
627             } catch (Exception ex) {/**NON BLOCK**/}
628             intentList.add(filter);
629             packageManager.getPreferredActivities(
630                     intentList, prefActList, ri.activityInfo.packageName);
631             if (prefActList.size() > 0) {
632                 pref.add(ri);
633             }
634         }
635
636         // No preferred activity is selected
637         if (pref.size() == 0) {
638             return null;
639         }
640
641         // Sort and return the first activity
642         Collections.sort(pref, new Comparator<ResolveInfo>() {
643             @Override
644             public int compare(ResolveInfo lhs, ResolveInfo rhs) {
645                 if (lhs.priority > rhs.priority) {
646                     return -1;
647                 } else if (lhs.priority < rhs.priority) {
648                     return 1;
649                 }
650                 if (lhs.preferredOrder > rhs.preferredOrder) {
651                     return -1;
652                 } else if (lhs.preferredOrder < rhs.preferredOrder) {
653                     return 1;
654                 }
655                 if (lhs.isDefault && !rhs.isDefault) {
656                     return -1;
657                 } else if (!lhs.isDefault && rhs.isDefault) {
658                     return 1;
659                 }
660                 if (lhs.match > rhs.match) {
661                     return -1;
662                 } else if (lhs.match > rhs.match) {
663                     return 1;
664                 }
665                 return 0;
666             }
667         });
668         return pref.get(0);
669     }
670
671     /**
672      * Method that returns the best Uri for the file (content uri, file uri, ...)
673      *
674      * @param ctx The current context
675      * @param file The file to resolve
676      */
677     private static Uri getUriFromFile(Context ctx, FileSystemObject fso) {
678         // If the passed object is secure file then we have to provide access with
679         // the internal resource provider
680         if (fso.isSecure() && SecureConsole.isVirtualStorageResource(fso.getFullPath())
681                 && fso instanceof RegularFile) {
682             RegularFile file = (RegularFile) fso;
683             return SecureResourceProvider.createAuthorizationUri(file);
684         }
685
686         // Try to resolve media data or return a file uri
687         final File file = new File(fso.getFullPath());
688         Uri uri = MediaHelper.fileToContentUri(ctx, file);
689         if (uri == null) {
690             uri = Uri.fromFile(file);
691         }
692         return uri;
693     }
694 }