2012-02-21 5 views
4

파일을 업로드하는 데 사용하는 웹 페이지가 있습니다. 사용자가 <input type="file" />으로 파일을 선택하고 제출 버튼을 누르면 모든 것이 정상적으로 작동합니다. 이제 안드로이드 용 C#에서 안드로이드 용 안드로이드 애플리케이션을 만들어야합니다.이 애플리케이션은 간단한 웹뷰를 포함하고 있으며 웹 버전처럼 작동해야합니다.Android 용 모노, WebView 입력 필드 filechooser가 작동하지 않습니다.

하지만 문제가 발생했습니다. Choose file 버튼을 클릭하면 파일 대화 상자가 열리지 않습니다.

나는 며칠 동안이 문제를 봤지만 아무 해결책도 찾지 못했습니다. workaround on Java platform처럼 보이지만 C#에서는 작동하지 않습니다.

다른 사람에게 아이디어를 만들어서 어떻게 작동시키는가?

답변

2

나는 어떻게 작동시키는 지 알고 있습니다. 그 중 일부는 늪지대의 표준 인 "가상의 방법을 묶는 법"이며, 그 일부는 순수한 사악한 악이다.

첫째, 우리는 "중개자"가 필요합니다. WebChromeClientopenFileChooser() 메서드를 선언하지 않으므로 OpenFileWebChromeClient이라는 버전을 선언해야합니다. 그것은 virtualOpenFileChooser 방법을 선언하고, 오버라이드 (override) 할 수 있도록 그것에 대한 바인딩을 제공합니다

using System; 

using Android.App; 
using Android.Content; 
using Android.Runtime; 
using Android.OS; 
using Android.Webkit; 

namespace Scratch.FileUpload 
{ 
    [Register ("android/webkit/WebChromeClient", DoNotGenerateAcw=true)] 
    class OpenFileWebChromeClient : WebChromeClient { 

     static IntPtr id_openFileChooser; 
     [Register ("openFileChooser", "(Landroid/webkit/ValueCallback;)V", "GetOpenFileChooserHandler")] 
     public virtual void OpenFileChooser (IValueCallback uploadMsg) 
     { 
      if (id_openFileChooser == IntPtr.Zero) 
       id_openFileChooser = JNIEnv.GetMethodID (ThresholdClass, "openFileChooser", "(Landroid/webkit/ValueCallback;)V"); 

      if (GetType() == ThresholdType) 
       JNIEnv.CallVoidMethod (Handle, id_openFileChooser, new JValue (JNIEnv.ToJniHandle (uploadMsg))); 
      else 
       JNIEnv.CallNonvirtualVoidMethod (Handle, ThresholdClass, id_openFileChooser, new JValue (JNIEnv.ToJniHandle (uploadMsg))); 
     } 

#pragma warning disable 0169 
     static Delegate cb_openFileChooser; 
     static Delegate GetOpenFileChooserHandler() 
     { 
      if (cb_openFileChooser == null) 
       cb_openFileChooser = JNINativeWrapper.CreateDelegate ((Action<IntPtr, IntPtr, IntPtr>) n_OpenFileChooser); 
      return cb_openFileChooser; 
     } 

     static void n_OpenFileChooser (IntPtr jnienv, IntPtr native__this, IntPtr native_uploadMsg) 
     { 
      OpenFileWebChromeClient __this = Java.Lang.Object.GetObject<OpenFileWebChromeClient> (native__this, JniHandleOwnership.DoNotTransfer); 
      var uploadMsg = Java.Lang.Object.GetObject<IValueCallback> (native_uploadMsg, JniHandleOwnership.DoNotTransfer); 
      __this.OpenFileChooser (uploadMsg); 
     } 
#pragma warning restore 0169 
    } 
} 

다음, C#을 익명 내부 클래스 부족하기 때문에, 우리는 여기에 이름이 명시 적으로 클래스, MyOpenFileWebChromeClient 필요

namespace Scratch.FileUpload { 
    class MyOpenFileWebChromeClient : OpenFileWebChromeClient { 

     Action<IValueCallback> cb; 

     public MyOpenFileWebChromeClient(Action<IValueCallback> cb) 
     { 
      this.cb = cb; 
     } 

     public override void OpenFileChooser (IValueCallback uploadMsg) 
     { 
      cb (uploadMsg); 
     } 
    } 

활동 포트는 익명의 내부 클래스 대신 MyOpenFileWebChromeClient을 사용한다는 점을 제외하고는 귀하가 언급 한 블로그 게시물과 동일합니다.

namespace Scratch.FileUpload { 

    [Activity (Label = "Scratch.FileUpload", MainLauncher = true)] 
    public class Activity1 : Activity 
    { 
     private WebView wv; 
     private IValueCallback mUploadMessage; 
     const int FilechooserResultcode = 1; 

     protected override void OnCreate (Bundle bundle) 
     { 
      base.OnCreate (bundle); 

      wv = new WebView (this); 
      wv.SetWebViewClient(new WebViewClient()); 
      wv.SetWebChromeClient(new MyOpenFileWebChromeClient(uploadMsg => { 
         mUploadMessage = uploadMsg; 
         var intent = new Intent (Intent.ActionGetContent); 
         intent.AddCategory(Intent.CategoryOpenable); 
         intent.SetType("image/*"); 
         StartActivityForResult(Intent.CreateChooser(intent, "File Chooser"), 
          FilechooserResultcode); 
      })); 

      SetHtml(null); 

      SetContentView(wv); 
     } 

     void SetHtml(string filename) 
     { 
      string html = @"<html> 
<body> 
<h1>Hello, world!</h1> 
<p>Input Box:</p> 
<input type=""file"" /> 
<p>URI: " + filename + @" 
</body> 
</html>"; 
      wv.LoadData(html, "text/html", "utf-8"); 
     } 

     protected override void OnActivityResult (int requestCode, Result resultCode, Intent data) 
     { 
      base.OnActivityResult (requestCode, resultCode, data); 

      if (requestCode == FilechooserResultcode) { 
       if (mUploadMessage == null) 
        return; 
       var result = data == null || resultCode != Result.Ok 
        ? null 
        : data.Data; 
       SetHtml(result.ToString()); 
       mUploadMessage.OnReceiveValue(result); 
       mUploadMessage = null; 
      } 
     } 
    } 
} 

슬프게도, 지금 순수 완화되지 않은 악한 행위를위한 시간이다 : 나는 또한 OnActivityResult() 수신하는 URI를 표시하는 몇 가지 논리를 업데이트했습니다. 위의 MyOpenFileWebChromeClient 선언의 문제는 M0S의 블로그가 익명의 내부 클래스 선언에 @Override을 사용할 수없는 것과 동일한 이유 때문에 작동하지 않는다는 것입니다. 앱을 빌드하는 android.jaropenFileChooser()을 선언하지 않습니다. 방법.

빌드 프로세스는 유효한 Java 코드를 포함해야하는 Android Callable Wrappers을 생성합니다. 문제는 생성 된 코드는 MyOpenFileWebChromeClient의 안드로이드 호출 가능 래퍼의 결과로 재정의 방법 및 인터페이스 방법에 대한 @Override을 사용한다는 것입니다 : 컴파일러 오류가 발생합니다 분명히

package scratch.fileupload; 


public class MyOpenFileWebChromeClient 
extends android.webkit.WebChromeClient 
{ 
    static final String __md_methods; 
    static { 
     __md_methods = 
      "n_openFileChooser:(Landroid/webkit/ValueCallback;)V:GetOpenFileChooserHandler\n" + 
      ""; 
     mono.android.Runtime.register ("Scratch.FileUpload.MyOpenFileWebChromeClient, Scratch.FileUpload, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", MyOpenFileWebChromeClient.class, __md_methods); 
    } 

    @Override 
    public void openFileChooser (android.webkit.ValueCallback p0) 
    { 
     n_openFileChooser (p0); 
    } 

    private native void n_openFileChooser (android.webkit.ValueCallback p0); 

    java.util.ArrayList refList; 
    public void monodroidAddReference (java.lang.Object obj) 
    { 
     if (refList == null) 
      refList = new java.util.ArrayList(); 
     refList.add (obj); 
    } 

    public void monodroidClearReferences() 
    { 
     if (refList != null) 
      refList.clear(); 
    } 
} 

MyOpenFileWebChromeClient.openFileChooser()@Override을, 어떻게 우리가이 일을해야합니까 ? @Override 주석을 제공함으로써!

package scratch.fileupload; 

import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 

@Target(ElementType.METHOD) 
@Retention(RetentionPolicy.SOURCE) 
public @interface Override { 
} 

장소 위의 Override.java라는 이름의 파일이 프로젝트에 추가 한 AndroidJavaSource에 자사의 빌드 작업을 설정으로.

우리는 MyOpenFileWebChromeClient 유형과 동일한 패키지에 사용자 정의 @Override 주석을 제공하기 때문에 결과 프로젝트가 작동합니다. (결과적으로 생성 된 패키지 이름이 무엇인지 알기 때문에이 작업을 수행하는 모든 패키지에 대해 별도의 @Override 주석을 제공해야합니다.) 동일한 패키지의 형식이 가져온 이름보다 우선합니다. 심지어 이름이 java.lang에서 오는 경우도 있으므로 사용자 정의 @Override 주석이 컴파일 될뿐만 아니라 java.lang.Override 주석보다 우선하여 MyOpenFileWebChromeClient android callable wrapper에서 사용됩니다.

나는 그것이 순수한 악몽이라고 말 했나요?

+1

신의 어머니! 이것이 작동하도록하는 몇 가지 해결 방법입니다. 다행히 Android 4.2 용 Mono에서 수정되었습니다! http://docs.xamarin.com/android/Releases/Mono_For_Android_4/Mono_For_Android_4.2#Bug_Fixes – Cheesebaron

+0

수정되었습니다. 파일 업로드 팝업이 내 측면의 webview에서 작동하지 않습니다. –

0

step1 = 파일 업로드가 작동하므로 Android 매니페스트에서 읽기/쓰기 권한을 부여해야합니다. in 주 활동 .cs

step2= 
private Action<int, Result, Intent> resultCallbackvalue; 

public void StartActivity(Intent intent, int requestCode, Action<int, Result, Intent> resultCallback) 
{ 
this.resultCallbackvalue = resultCallback; 
StartActivityForResult(intent, requestCode); 
} 
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) 
{ 
base.OnActivityResult(requestCode, resultCode, data); 
if (this.resultCallbackvalue != null) 
{ 
this.resultCallbackvalue(requestCode, resultCode, data); 
this.resultCallbackvalue = null; 
} 
} 
step3= add ExtendedChromeClient,cs Inheriting from : WebChromeClient 

private static int filechooser = 1; 
private IValueCallback message; 
private MainActivity activity = null; 

    public ExtendedChromeClient(MainActivity context) 
    { 
     this.activity = context; 
    } 

    public override bool OnShowFileChooser(WebView webView, IValueCallback filePathCallback, FileChooserParams fileChooserParams) 
    { 
     this.message = filePathCallback; 
     Intent chooserIntent = fileChooserParams.CreateIntent(); 
     chooserIntent.AddCategory(Intent.CategoryOpenable); 
     this.activity.StartActivity(Intent.CreateChooser(chooserIntent, "File Chooser"), filechooser, this.OnActivityResult); 
     return true; 
    } 

    private void OnActivityResult(int requestCode, Result resultCode, Intent data) 
    { 
     if (data != null) 
     { 
      if (requestCode == filechooser) 
      { 
       if (null == this.message) 
       { 
        return; 
       } 

       this.message.OnReceiveValue(WebChromeClient.FileChooserParams.ParseResult((int)resultCode, data)); 
       this.message = null; 
      } 
     } 
    } 
관련 문제