Upload Handler.

Change-Id: Ia6ed884ac558f4f5371c227bad992f3efc0feaee
diff --git a/src/com/android/browser/UploadDialog.java b/src/com/android/browser/UploadDialog.java
new file mode 100644
index 0000000..d96c486
--- /dev/null
+++ b/src/com/android/browser/UploadDialog.java
@@ -0,0 +1,109 @@
+/*
+    * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+    *
+    * Redistribution and use in source and binary forms, with or without
+    * modification, are permitted provided that the following conditions are
+    * met:
+    * * Redistributions of source code must retain the above copyright
+    * notice, this list of conditions and the following disclaimer.
+    * * Redistributions in binary form must reproduce the above
+    * copyright notice, this list of conditions and the following
+    * disclaimer in the documentation and/or other materials provided
+    * with the distribution.
+    * * Neither the name of The Linux Foundation nor the names of its
+    * contributors may be used to endorse or promote products derived
+    * from this software without specific prior written permission.
+    *
+    * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+    * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+    * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+    * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+    * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+    * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+    * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+    * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+    *
+    */
+
+package com.android.browser;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UploadDialog extends AppItem {
+    public List<ResolveInfo> apps;
+    private Activity activity;
+    private List<Intent> uploadIntents;
+
+    public UploadDialog(Activity activity) {
+        super(null);
+        this.activity = activity;
+        this.apps = null;
+    }
+
+    public void getUploadableApps(List<Intent> intents) {
+
+        ArrayList<ResolveInfo> uploadApps = new ArrayList<ResolveInfo>();
+
+        PackageManager pm = activity.getPackageManager();
+
+        for (Intent currentIntent: intents) {
+            List<ResolveInfo> appsList = pm.queryIntentActivities(currentIntent,
+                PackageManager.MATCH_DEFAULT_ONLY);
+            for (ResolveInfo res : appsList) {
+                uploadApps.add(res);
+            }
+        }
+
+        this.apps = uploadApps;
+        this.uploadIntents = intents;
+    }
+
+    public void loadView(final UploadHandler uploadHandler) {
+
+        final AppAdapter adapter = new AppAdapter(activity, activity.getPackageManager(),
+                                    R.layout.app_row, this.apps);
+
+
+        AlertDialog.Builder builderSingle = new AlertDialog.Builder(activity);
+        builderSingle.setIcon(R.mipmap.ic_launcher_browser_swe);
+        builderSingle.setTitle(activity.getString(R.string.choose_upload));
+
+        builderSingle.setAdapter(adapter, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int position) {
+                dialog.dismiss();
+                Intent chooserIntent = Intent.createChooser(uploadIntents.get(position), "Upload Via");
+                uploadHandler.initiateActivity(chooserIntent);
+            }
+        });
+
+
+        builderSingle.setOnCancelListener(new DialogInterface.OnCancelListener()
+            {
+               @Override
+                public void onCancel(DialogInterface dialog)
+                 {
+                    uploadHandler.setHandled(false);
+                    dialog.dismiss();
+                }
+        });
+
+        builderSingle.show();
+
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/browser/UploadHandler.java b/src/com/android/browser/UploadHandler.java
index e3eda33..655d642 100644
--- a/src/com/android/browser/UploadHandler.java
+++ b/src/com/android/browser/UploadHandler.java
@@ -18,8 +18,14 @@
 
 import android.app.Activity;
 import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
 import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ActivityInfo;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Environment;
@@ -32,6 +38,8 @@
 import com.android.browser.reflect.ReflectHelper;
 
 import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Vector;
 
 /**
@@ -60,12 +68,18 @@
         return mCameraFilePath;
     }
 
-    boolean handled() {
+    protected boolean handled() {
         return mHandled;
     }
 
-    void onResult(int resultCode, Intent intent) {
+    protected void setHandled(boolean handled) {
+        mHandled = handled;
+        mCaughtActivityNotFoundException = false;
+        if (!mHandled)
+            mUploadFilePaths.onReceiveValue(null);
+    }
 
+    void onResult(int resultCode, Intent intent) {
         if (resultCode == Activity.RESULT_CANCELED && mCaughtActivityNotFoundException) {
             // Couldn't resolve an activity, we are going to try again so skip
             // this result.
@@ -102,19 +116,8 @@
                 filePath = result.getPath();
                 hasGoodFilePath = filePath != null && !filePath.isEmpty();
             } else if ("content".equals(scheme)) {
-                ContentResolver cr = mController.getActivity().getContentResolver();
-                String[] projection = {"_data"};
-                Cursor c = cr.query(result, projection, null, null, null);
-                try {
-                    if (c != null && c.moveToFirst()) {
-                        filePath = c.getString(0);
-                        hasGoodFilePath = filePath != null && !filePath.isEmpty();
-                    }
-                } finally {
-                    if (c != null) {
-                        c.close();
-                    }
-                }
+                filePath = getFilePath(mController.getContext(), result);
+                hasGoodFilePath = filePath != null && !filePath.isEmpty();
             }
         }
 
@@ -132,6 +135,7 @@
         }
 
         if (mUploadMessage != null) {
+
             if (!isDRMFileType) {
                 mUploadMessage.onReceiveValue(result);
             } else {
@@ -152,6 +156,124 @@
         mCaughtActivityNotFoundException = false;
     }
 
+
+    public String getDocumentId(final Uri uri) {
+        String id = null;
+        try {
+            Object[] params  = {(android.net.Uri)uri};
+            Class[] type = new Class[] {Class.forName("android.net.Uri") };
+            id = (String) ReflectHelper.invokeMethod(
+                "android.provider.DocumentsContract","getDocumentId",
+                type, params);
+
+        } catch(java.lang.ClassNotFoundException e) {
+
+        }
+        return id;
+    }
+
+
+    public String getFilePath(final Context context, final Uri uri) {
+       String id =  getDocumentId(uri);
+
+        // DocumentProvider is new API exposed in Kitkat
+        // Its a way of exposing unified file explorer
+        if (id != null) {
+            // ExternalStorageProvider
+            if (isExternalStorageDocument(uri)) {
+                final String docId = id;
+                final String[] split = docId.split(":");
+                final String type = split[0];
+
+                if ("primary".equalsIgnoreCase(type)) {
+                    return Environment.getExternalStorageDirectory() + "/" + split[1];
+                }
+            }
+            // DownloadsProvider
+            else if (isDownloadsDocument(uri)) {
+                final Uri contentUri = ContentUris.withAppendedId(
+                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+                return getDataColumn(context, contentUri, null, null);
+            }
+            // MediaProvider
+            else if (isMediaDocument(uri)) {
+                final String docId = id;
+                final String[] split = docId.split(":");
+                final String type = split[0];
+
+                Uri contentUri = null;
+                if ("image".equals(type)) {
+                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+                } else if ("video".equals(type)) {
+                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+                } else if ("audio".equals(type)) {
+                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+                }
+
+                final String selection = "_id=?";
+                final String[] selectionArgs = new String[] {
+                        split[1]
+                };
+
+                return getDataColumn(context, contentUri, selection, selectionArgs);
+            }
+        }
+        // MediaStore (and general)
+        else if ("content".equalsIgnoreCase(uri.getScheme())) {
+            return getDataColumn(context, uri, null, null);
+        }
+
+        return null;
+    }
+
+    /**
+    * Get the value of the data column for this Uri. This is useful for
+    * MediaStore Uris, and other file-based ContentProviders.
+    * @return The value of the _data column, which is typically a file path.
+    */
+    private String getDataColumn(Context context, Uri uri, String selection,
+        String[] selectionArgs) {
+
+        Cursor cursor = null;
+        final String column = "_data";
+        final String[] projection = { column };
+
+        try {
+            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
+                    null);
+            if (cursor != null && cursor.moveToFirst()) {
+                final int column_index = cursor.getColumnIndexOrThrow(column);
+                return cursor.getString(column_index);
+            }
+        } finally {
+            if (cursor != null)
+                cursor.close();
+        }
+        return null;
+    }
+
+    /**
+    * @return Whether the Uri authority is ExternalStorageProvider.
+    */
+    private boolean isExternalStorageDocument(Uri uri) {
+        return "com.android.externalstorage.documents".equals(uri.getAuthority());
+    }
+
+    /**
+    * @return Whether the Uri authority is DownloadsProvider.
+    */
+    private boolean isDownloadsDocument(Uri uri) {
+        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
+    }
+
+    /**
+    * @return Whether the Uri authority is MediaProvider.
+    */
+    public static boolean isMediaDocument(Uri uri) {
+        return "com.android.providers.media.documents".equals(uri.getAuthority());
+    }
+
+
     void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
 
         final String imageMimeType = "image/*";
@@ -273,6 +395,7 @@
 
         // Ensure it is not still set from a previous upload.
         mCameraFilePath = null;
+        List<Intent> intentList = new ArrayList<Intent>();
 
         if (mimeType.equals(imageMimeType)) {
             if (capture) {
@@ -284,9 +407,8 @@
                 // Specified just 'image/*', capture=false, or no capture value.
                 // In all these cases we show a traditional picker filetered on accept type
                 // so launch an intent for both the Camera and image/* OPENABLE.
-                Intent chooser = createChooserIntent(createCameraIntent());
-                chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(imageMimeType));
-                startActivity(chooser);
+                intentList.add(createCameraIntent());
+                createUploadDialog(imageMimeType, intentList);
                 return;
             }
         } else if (mimeType.equals(videoMimeType)) {
@@ -299,9 +421,8 @@
                 // Specified just 'video/*', capture=false, or no capture value.
                 // In all these cases we show an intent for the traditional file picker, filtered
                 // on accept type so launch an intent for both camcorder and video/* OPENABLE.
-                Intent chooser = createChooserIntent(createCamcorderIntent());
-                chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(videoMimeType));
-                startActivity(chooser);
+                intentList.add(createCamcorderIntent());
+                createUploadDialog(videoMimeType, intentList);
                 return;
             }
         } else if (mimeType.equals(audioMimeType)) {
@@ -314,16 +435,15 @@
                 // Specified just 'audio/*',  capture=false, or no capture value.
                 // In all these cases so go ahead and launch an intent for both the sound
                 // recorder and audio/* OPENABLE.
-                Intent chooser = createChooserIntent(createSoundRecorderIntent());
-                chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(audioMimeType));
-                startActivity(chooser);
+                intentList.add(createSoundRecorderIntent());
+                createUploadDialog(audioMimeType, intentList);
                 return;
             }
         }
 
         // No special handling based on the accept type was necessary, so trigger the default
         // file upload chooser.
-        startActivity(createDefaultOpenableIntent());
+        createUploadDialog("*/*", null);
     }
 
 
@@ -356,9 +476,47 @@
         Intent chooser = createChooserIntent(createCameraIntent(), createCamcorderIntent(),
                 createSoundRecorderIntent());
         chooser.putExtra(Intent.EXTRA_INTENT, i);
+
         return chooser;
     }
 
+
+    private void createUploadDialog(String openableMimeType, List<Intent> intentList) {
+
+        Intent openable = new Intent(Intent.ACTION_GET_CONTENT);
+        openable.addCategory(Intent.CATEGORY_OPENABLE);
+        openable.setType(openableMimeType);
+
+        if (openableMimeType.equals("*/*") && intentList == null) {
+            intentList = new ArrayList<Intent>();
+            intentList.add(createCameraIntent());
+            intentList.add(createCamcorderIntent());
+            intentList.add(createSoundRecorderIntent());
+        }
+
+        // get all openable apps list and create corresponading intents
+        PackageManager pm = mController.getActivity().getPackageManager();
+        List<ResolveInfo> openableAppsList = pm.queryIntentActivities(openable, 0);
+        for (int j = 0, n = openableAppsList.size(); j < n; j++ ) {
+                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
+                i.setType(openableMimeType);
+                ActivityInfo activityInfo = openableAppsList.get(j).activityInfo;
+                ComponentName name = new ComponentName(activityInfo.applicationInfo.packageName,
+                                            activityInfo.name);
+                i.setComponent(name);
+                intentList.add(i);
+        }
+
+
+        UploadDialog upDialog = new UploadDialog(mController.getActivity());
+        upDialog.getUploadableApps(intentList);
+        upDialog.loadView(this);
+    }
+
+    public void initiateActivity(Intent intent) {
+        startActivity(intent);
+    }
+
     private Intent createChooserIntent(Intent... intents) {
         Intent chooser = new Intent(Intent.ACTION_CHOOSER);
         chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents);