Bug 575750 - Implement support for OS protocol handlers on Android [r=mwu,bzbarsky]

--HG--
extra : rebase_source : 00c00eff847686211b07798f8d837e7bde02b89d
This commit is contained in:
Brad Lassey 2010-06-29 22:22:08 -04:00
parent 0ac9763118
commit 3bcc160167
9 changed files with 185 additions and 35 deletions

View File

@ -57,7 +57,7 @@ import android.util.*;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
class GeckoAppShell
{
@ -362,14 +362,41 @@ class GeckoAppShell
Intent intent = new Intent();
intent.setType(aMimeType);
List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
int numAttr = 2;
int numAttr = 4;
String[] ret = new String[list.size() * numAttr];
for (int i = 0; i < list.size(); i++) {
ret[i * numAttr] = list.get(i).loadLabel(pm).toString();
if (list.get(i).isDefault)
ret[i * numAttr + 1] = "default";
else
ret[i * numAttr + 1] = "";
ResolveInfo resolveInfo = list.get(i);
ret[i * numAttr] = resolveInfo.loadLabel(pm).toString();
if (resolveInfo.isDefault)
ret[i * numAttr + 1] = "default";
else
ret[i * numAttr + 1] = "";
ret[i * numAttr + 2] = resolveInfo.activityInfo.applicationInfo.packageName;
ret[i * numAttr + 3] = resolveInfo.activityInfo.name;
}
return ret;
}
static String[] getHandlersForProtocol(String aScheme) {
PackageManager pm =
GeckoApp.surfaceView.getContext().getPackageManager();
Intent intent = new Intent();
Uri uri = new Uri.Builder().scheme(aScheme).build();
intent.setData(uri);
List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
int numAttr = 4;
String[] ret = new String[list.size() * numAttr];
for (int i = 0; i < list.size(); i++) {
ResolveInfo resolveInfo = list.get(i);
ret[i * numAttr] = resolveInfo.loadLabel(pm).toString();
if (resolveInfo.isDefault)
ret[i * numAttr + 1] = "default";
else
ret[i * numAttr + 1] = "";
ret[i * numAttr + 2] = resolveInfo.activityInfo.applicationInfo.packageName;
ret[i * numAttr + 3] = resolveInfo.activityInfo.name;
}
return ret;
}
@ -378,10 +405,17 @@ class GeckoAppShell
return android.webkit.MimeTypeMap.getSingleton().getMimeTypeFromExtension(aFileExt);
}
static boolean openUriExternal(String aUriSpec, String aMimeType) {
static boolean openUriExternal(String aUriSpec, String aMimeType,
String aPackageName, String aClassName) {
// XXX: It's not clear if we should set the action to view or leave it open
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(android.net.Uri.parse(aUriSpec), aMimeType);
if (aMimeType.length() > 0)
intent.setDataAndType(Uri.parse(aUriSpec), aMimeType);
else
intent.setData(Uri.parse(aUriSpec));
if (aPackageName.length() > 0 && aClassName.length() > 0)
intent.setClassName(aPackageName, aClassName);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
GeckoApp.surfaceView.getContext().startActivity(intent);

View File

@ -36,13 +36,19 @@
* ***** END LICENSE BLOCK ***** */
#include "nsAndroidHandlerApp.h"
#include "AndroidBridge.h"
NS_IMPL_ISUPPORTS1(nsAndroidHandlerApp, nsIHandlerApp)
nsAndroidHandlerApp::nsAndroidHandlerApp(nsAString& aName,
nsAString& aDescription) :
mName(aName), mDescription(aDescription)
nsAndroidHandlerApp::nsAndroidHandlerApp(const nsAString& aName,
const nsAString& aDescription,
const nsAString& aPackageName,
const nsAString& aClassName,
const nsACString& aMimeType) :
mName(aName), mDescription(aDescription), mPackageName(aPackageName),
mClassName(aClassName), mMimeType(aMimeType)
{
}
@ -85,6 +91,10 @@ nsresult nsAndroidHandlerApp::Equals(nsIHandlerApp *aHandlerApp, PRBool *aRetval
nsresult nsAndroidHandlerApp::LaunchWithURI(nsIURI *aURI, nsIInterfaceRequestor *aWindowContext)
{
// XXX: need implementation
return NS_ERROR_NOT_IMPLEMENTED;
nsCString uriSpec;
aURI->GetSpec(uriSpec);
return mozilla::AndroidBridge::Bridge()->
OpenUriExternal(uriSpec, mMimeType, mPackageName, mClassName) ?
NS_OK : NS_ERROR_FAILURE;
}

View File

@ -45,12 +45,16 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIHANDLERAPP
public:
nsAndroidHandlerApp(nsAString& aName, nsAString& aDescription);
nsAndroidHandlerApp(const nsAString& aName, const nsAString& aDescription,
const nsAString& aPackageName, const nsAString& aClassName,
const nsACString& aMimeType);
virtual ~nsAndroidHandlerApp();
private:
nsString mName;
nsString mDescription;
nsCString mMimeType;
nsString mClassName;
nsString mPackageName;
};
#endif

View File

@ -76,8 +76,10 @@ nsMIMEInfoAndroid::GetMimeInfoForMimeType(const nsACString& aMimeType)
if (len == 0)
return nsnull;
nsCOMPtr<nsMIMEInfoAndroid> info = new nsMIMEInfoAndroid(aMimeType);
for (jsize i = 0; i < len; i+=2) {
nsAndroidHandlerApp* app = new nsAndroidHandlerApp(*stringArray[i], empty);
for (jsize i = 0; i < len; i+=4) {
nsAndroidHandlerApp* app =
new nsAndroidHandlerApp(*stringArray[i], empty, *stringArray[i + 2],
*stringArray[i + 3], aMimeType);
info->mHandlerApps->AppendElement(app, PR_FALSE);
if (stringArray[i + 1] > 0)
info->mPrefApp = app;
@ -95,6 +97,37 @@ nsMIMEInfoAndroid::GetMimeInfoForFileExt(const nsACString& aFileExt)
return GetMimeInfoForMimeType(mimeType);
}
nsresult
nsMIMEInfoAndroid::GetMimeInfoForProtocol(const nsACString &aScheme,
PRBool *found,
nsIHandlerInfo **info)
{
mozilla::AndroidBridge* bridge = mozilla::AndroidBridge::Bridge();
nsStringArray stringArray;
bridge->GetHandlersForProtocol(nsCAutoString(aScheme).get(), &stringArray);
const nsString &empty = EmptyString();
const nsCString &emptyC = EmptyCString();
PRInt32 len = stringArray.Count();
if (len == 0) {
*found = PR_FALSE;
return NS_OK;
}
*found = PR_TRUE;
nsMIMEInfoAndroid *mimeinfo = new nsMIMEInfoAndroid(emptyC);
for (jsize i = 0; i < len; i+=4) {
nsAndroidHandlerApp* app =
new nsAndroidHandlerApp(*stringArray[i], empty, *stringArray[i + 2],
*stringArray[i + 3], emptyC);
mimeinfo->mHandlerApps->AppendElement(app, PR_FALSE);
if (!stringArray[i + 1]->IsEmpty()) {
mimeinfo->mPrefApp = app;
}
}
*info = mimeinfo;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::GetType(nsACString& aType)
{
@ -197,7 +230,9 @@ nsMIMEInfoAndroid::SetPreferredAction(nsHandlerInfoAction aPrefAction)
NS_IMETHODIMP
nsMIMEInfoAndroid::GetAlwaysAskBeforeHandling(PRBool* aAlwaysAsk)
{
*aAlwaysAsk = mAlwaysAsk;
// the chooser dialog currently causes a crash on Android, avoid this by returning false here
// but be sure to return mAlwaysAsk when that gets fixed (bug 584896)
*aAlwaysAsk = PR_FALSE;
return NS_OK;
}
@ -328,8 +363,8 @@ nsMIMEInfoAndroid::LaunchWithFile(nsIFile *aFile)
}
nsMIMEInfoAndroid::nsMIMEInfoAndroid(const nsACString& aMIMEType) :
mMimeType(aMIMEType), mAlwaysAsk(PR_TRUE),
mPrefAction(nsIMIMEInfo::useHelperApp),
mMimeType(aMIMEType), mAlwaysAsk(PR_TRUE),
mPrefAction(nsIMIMEInfo::useHelperApp),
mSystemChooser(this)
{
mPrefApp = &mSystemChooser;

View File

@ -46,6 +46,9 @@ class nsMIMEInfoAndroid : public nsIMIMEInfo
public:
static already_AddRefed<nsIMIMEInfo> GetMimeInfoForMimeType(const nsACString& aMimeType);
static already_AddRefed<nsIMIMEInfo> GetMimeInfoForFileExt(const nsACString& aFileExt);
static nsresult GetMimeInfoForProtocol(const nsACString &aScheme,
PRBool *found,
nsIHandlerInfo **info);
NS_DECL_ISUPPORTS
NS_DECL_NSIMIMEINFO

View File

@ -71,6 +71,14 @@ nsresult
nsOSHelperAppService::OSProtocolHandlerExists(const char* aScheme,
PRBool* aExists)
{
*aExists = PR_FALSE;
*aExists = mozilla::AndroidBridge::Bridge()->GetHandlersForProtocol(aScheme);
return NS_OK;
}
nsresult nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
PRBool *found,
nsIHandlerInfo **info)
{
return nsMIMEInfoAndroid::GetMimeInfoForProtocol(aScheme, found, info);
}

View File

@ -55,6 +55,12 @@ public:
virtual NS_HIDDEN_(nsresult)
OSProtocolHandlerExists(const char* aScheme,
PRBool* aExists);
NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
PRBool *found,
nsIHandlerInfo **_retval);
};
#endif /* nsOSHelperAppService_h */

View File

@ -40,6 +40,7 @@
#include "mozilla/dom/ContentChild.h"
#include <pthread.h>
#include <prthread.h>
#include "nsXPCOMStrings.h"
#include "AndroidBridge.h"
@ -100,7 +101,8 @@ AndroidBridge::Init(JNIEnv *jEnv,
jScheduleRestart = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "scheduleRestart", "()V");
jNotifyXreExit = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "onXreExit", "()V");
jGetHandlersForMimeType = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getHandlersForMimeType", "(Ljava/lang/String;)[Ljava/lang/String;");
jOpenUriExternal = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "openUriExternal", "(Ljava/lang/String;Ljava/lang/String;)Z");
jGetHandlersForProtocol = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getHandlersForProtocol", "(Ljava/lang/String;)[Ljava/lang/String;");
jOpenUriExternal = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "openUriExternal", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z");
jGetMimeTypeFromExtension = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getMimeTypeFromExtension", "(Ljava/lang/String;)Ljava/lang/String;");
jMoveTaskToBack = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "moveTaskToBack", "()V");
@ -254,38 +256,81 @@ AndroidBridge::NotifyXreExit()
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jNotifyXreExit);
}
void
PRBool
AndroidBridge::GetHandlersForMimeType(const char *aMimeType, nsStringArray* aStringArray)
{
NS_PRECONDITION(aStringArray != nsnull, "null array pointer passed in");
AutoLocalJNIFrame jniFrame;
NS_ConvertUTF8toUTF16 wMimeType(aMimeType);
jstring jstr = mJNIEnv->NewString(wMimeType.get(), wMimeType.Length());
jobject obj = mJNIEnv->CallStaticObjectMethod(mGeckoAppShellClass,
jGetHandlersForMimeType,
jobject obj = mJNIEnv->CallStaticObjectMethod(mGeckoAppShellClass,
jGetHandlersForMimeType,
jstr);
jobjectArray arr = static_cast<jobjectArray>(obj);
if (!arr)
return;
return PR_FALSE;
jsize len = mJNIEnv->GetArrayLength(arr);
for (jsize i = 0; i < len; i+=2) {
if (!aStringArray)
return len > 0;
for (jsize i = 0; i < len; i++) {
jstring jstr = static_cast<jstring>(mJNIEnv->GetObjectArrayElement(arr, i));
nsJNIString jniStr(jstr);
aStringArray->AppendString(jniStr);
}
aStringArray->InsertStringAt(jniStr, i);
}
return PR_TRUE;
}
PRBool
AndroidBridge::OpenUriExternal(nsCString& aUriSpec, nsCString& aMimeType)
AndroidBridge::GetHandlersForProtocol(const char *aScheme, nsStringArray* aStringArray)
{
NS_PRECONDITION(aStringArray != nsnull, "null array pointer passed in");
AutoLocalJNIFrame jniFrame;
NS_ConvertUTF8toUTF16 wScheme(aScheme);
jstring jstr = mJNIEnv->NewString(wScheme.get(), wScheme.Length());
jobject obj = mJNIEnv->CallStaticObjectMethod(mGeckoAppShellClass,
jGetHandlersForProtocol,
jstr);
jobjectArray arr = static_cast<jobjectArray>(obj);
if (!arr)
return PR_FALSE;
jsize len = mJNIEnv->GetArrayLength(arr);
if (!aStringArray)
return len > 0;
for (jsize i = 0; i < len; i++) {
jstring jstr = static_cast<jstring>(mJNIEnv->GetObjectArrayElement(arr, i));
nsJNIString jniStr(jstr);
aStringArray->InsertStringAt(jniStr, i);
}
return PR_TRUE;
}
PRBool
AndroidBridge::OpenUriExternal(const nsACString& aUriSpec, const nsACString& aMimeType,
const nsAString& aPackageName, const nsAString& aClassName)
{
AutoLocalJNIFrame jniFrame;
NS_ConvertUTF8toUTF16 wUriSpec(aUriSpec);
NS_ConvertUTF8toUTF16 wMimeType(aMimeType);
const PRUnichar* wPackageName;
PRUint32 packageNameLen = NS_StringGetData(aPackageName, &wPackageName);
const PRUnichar* wClassName;
PRUint32 classNameLen = NS_StringGetData(aClassName, &wClassName);
jstring jstrUri = mJNIEnv->NewString(wUriSpec.get(), wUriSpec.Length());
jstring jstrType = mJNIEnv->NewString(wMimeType.get(), wMimeType.Length());
jstring jstrPackage = mJNIEnv->NewString(wPackageName, packageNameLen);
jstring jstrClass = mJNIEnv->NewString(wClassName, classNameLen);
return mJNIEnv->CallStaticBooleanMethod(mGeckoAppShellClass,
jOpenUriExternal,
jstrUri, jstrType);
jstrUri, jstrType, jstrPackage, jstrClass);
}
void
@ -377,5 +422,5 @@ mozilla_AndroidBridge_AttachThread(PRBool asDaemon)
extern "C" JNIEnv * GetJNIForThread()
{
return mozilla::AndroidBridge::JNIForThread();
return mozilla::AndroidBridge::JNIForThread();
}

View File

@ -114,9 +114,13 @@ public:
void SetSurfaceView(jobject jobj);
AndroidGeckoSurfaceView& SurfaceView() { return mSurfaceView; }
void GetHandlersForMimeType(const char *aMimeType, nsStringArray* aStringArray);
PRBool GetHandlersForProtocol(const char *aScheme, nsStringArray* aStringArray = nsnull);
PRBool OpenUriExternal(nsCString& aUriSpec, nsCString& aMimeType);
PRBool GetHandlersForMimeType(const char *aMimeType, nsStringArray* aStringArray = nsnull);
PRBool OpenUriExternal(const nsACString& aUriSpec, const nsACString& aMimeType,
const nsAString& aPackageName = EmptyString(),
const nsAString& aClassName = EmptyString());
void GetMimeTypeFromExtension(const nsCString& aFileExt, nsCString& aMimeType);
@ -173,6 +177,7 @@ protected:
jmethodID jScheduleRestart;
jmethodID jGetOutstandingDrawEvents;
jmethodID jGetHandlersForMimeType;
jmethodID jGetHandlersForProtocol;
jmethodID jOpenUriExternal;
jmethodID jGetMimeTypeFromExtension;
jmethodID jMoveTaskToBack;