2017-08-02 54 views
0

我有一些在Android上選擇文件的代碼。我在某處從網上挑選它。實際上,代碼在一種情況下工作,但在我的應用程序中,它不起作用。 Android認爲用戶在實際沒有的時候已經取消了文件選擇。Xamarin Android - FilePickerActivity - 無理由取消

下面的代碼:

private Task<FileData> PickAndOpenFile(bool isSave) 
{ 
    var id = GetRequestId(); 

    var ntcs = new TaskCompletionSource<FileData>(id); 

    if (Interlocked.CompareExchange(ref _CompletionSource, ntcs, null) != null) 
    { 
     throw new InvalidOperationException("Only one operation can be active at a time"); 
    } 

    try 
    { 
     var pickerIntent = new Intent(_Context, typeof(FilePickerActivity)); 
     pickerIntent.SetFlags(ActivityFlags.NewTask); 

     _Context.StartActivity(pickerIntent); 

     EventHandler<FilePickerEventArgs> handler = null; 
     EventHandler<EventArgs> cancelledHandler = null; 

     handler = (s, e) => 
     { 
      var tcs = Interlocked.Exchange(ref _CompletionSource, null); 

      FilePickerActivity.FilePicked -= handler; 

      Stream fileStream = isSave ? File.OpenRead(e.FilePath) : File.OpenWrite(e.FilePath); 

      tcs?.SetResult(new FileData { FileName = e.FileName, FileStream = fileStream }); 
     }; 

     cancelledHandler = (s, e) => 
     { 
      var tcs = Interlocked.Exchange(ref _CompletionSource, null); 

      FilePickerActivity.FilePickCancelled -= cancelledHandler; 

      tcs?.SetResult(null); 
     }; 

     FilePickerActivity.FilePickCancelled += cancelledHandler; 
     FilePickerActivity.FilePicked += handler; 
    } 
    catch (Exception exAct) 
    { 
     Debug.Write(exAct); 
    } 

    return _CompletionSource.Task; 
} 

看到這正常運行,請複製此回購和運行Android樣品。選擇文件示例位於第二個選項卡上。 https://github.com/MelbourneDeveloper/Adapt.Presentation.git

但是,在我的應用程序,我有整整像這樣使用相同的代碼:

  var filePicker = App.PlatformSpecificStartupArguments.PresentationFactory.CreateFilePicker(); 
      using (var fileData = await filePicker.PickAndOpenFileForReading()) 
      { 
       if (fileData == null) 
       { 
        return; 
       } 

       using (var readFileStream = fileData.FileStream) 
       { 
        var fileExtension = Path.GetExtension(fileData.FileName); 
        var relativeUri = Guid.NewGuid() + fileExtension; 
        await OnPictureTakenAsync(this, new PictureTakenEventArgs(readFileStream, relativeUri, fileData.FileName)); 
       } 
      } 

但是,當我拿起文件(選擇從現有圖像的圖像),代碼進入cancelledHandler代替。這顯然是一個權限問題,因爲我得到的設備日誌此錯誤消息:

08-02 15:15:28.697 LGE的Nexus 5X錯誤18911個DatabaseUtils java.lang.SecurityException異常:權限拒絕:閱讀com.android。 providers.media.MediaProvider uri content:// media/external/images/media from pid = 18684,uid = 10221需要android.permission.READ_EXTERNAL_STORAGE或grantUriPermission() at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java: 608) at android.content.ContentProvider $ Transport.enforceReadPermission(ContentProvider.java:483) at android.content.ContentProvider $ Transport.query(ContentProvider.java:212) at android.content.ContentProviderNative.onTransact(ContentProviderNati ve.java:112) 在android.os.Binder.execTransact(Binder.java:565)

於是,我打開在Visual Studio的級別此權限在我的Android清單,但問題仍然存在。我針對的失敗的Android版本是6.0。您可以看到,在上面提到的回購中,目標版本設置爲「使用SDK版本進行編譯」。而且我還沒有將READ_EXTERNAL_STORAGE添加到該示例應用程序。

回答

-2

有請求的權限在我的Git回購這裏的樣本: https://github.com/MelbourneDeveloper/Adapt.Presentation

正如上面提到的,後來的Android版本有一個新的權限要求的系統。在早期的API中,應用程序要麼有權限,要麼沒有。在版本6和更高版本中,需要請求權限,並且用戶必須提供響應。我已經修好我的圖書館來反映這一點。這段代碼大部分是由James Montemagno最初寫的,但我認爲它並不適用。您必須爲外部存儲調用RequestPermissionsAsync。

/// <summary> 
    /// Requests the permissions from the users 
    /// </summary> 
    public async Task<PermissionStatusDictionary> RequestPermissionsAsync(params Permission[] permissions) 
    { 
     if (_Tcs != null && !_Tcs.Task.IsCompleted) 
     { 
      _Tcs.SetCanceled(); 
      _Tcs = null; 
     } 
     lock (_Locker) 
     { 
      _Results = new PermissionStatusDictionary(); 
     } 
     if (_Activity == null) 
     { 
      Debug.WriteLine("Unable to detect current Activity. Please ensure Plugin.CurrentActivity is installed in your Android project and your Application class is registering with Application.IActivityLifecycleCallbacks."); 
      foreach (var permission in permissions) 
      { 
       if (_Results.ContainsKey(permission)) 
       { 
        continue; 
       } 

       _Results.Add(permission, PermissionStatus.Unknown); 
      } 

      return _Results; 
     } 

     var permissionsToRequest = new List<string>(); 

     foreach (var permission in permissions) 
     { 
      var result = await CheckPermissionStatusAsync(permission).ConfigureAwait(false); 
      if (result != PermissionStatus.Granted) 
      { 
       var names = GetManifestNames(permission); 
       //check to see if we can find manifest names 
       //if we can't add as unknown and continue 
       if ((names?.Count ?? 0) == 0) 
       { 
        lock (_Locker) 
        { 
         _Results.Add(permission, PermissionStatus.Unknown); 
        } 
        continue; 
       } 

       permissionsToRequest.AddRange(names); 
      } 
      else 
      { 
       //if we are granted you are good! 
       lock (_Locker) 
       { 
        _Results.Add(permission, PermissionStatus.Granted); 
       } 
      } 
     } 

     if (permissionsToRequest.Count == 0) 
     { 
      return _Results; 
     } 

     _Tcs = new TaskCompletionSource<PermissionStatusDictionary>(); 

     ActivityCompat.RequestPermissions(_Activity, permissionsToRequest.ToArray(), PermissionCode); 

     return await _Tcs.Task.ConfigureAwait(true); 
    } 

這是回調代碼:

/// <summary> 
    /// Callback that must be set when request permissions has finished 
    /// </summary> 
    private void RequestPermissionsActivity_PermissionsRequestCompleted(int requestCode, string[] permissions, acp.Permission[] grantResults) 
    { 
     if (requestCode != PermissionCode) 
     { 
      return; 
     } 

     if (_Tcs == null) 
     { 
      return; 
     } 

     for (var i = 0; i < permissions.Length; i++) 
     { 
      if (_Tcs.Task.Status == TaskStatus.Canceled) 
      { 
       return; 
      } 

      var permission = GetPermissionForManifestName(permissions[i]); 
      if (permission == Permission.Unknown) 
      { 
       continue; 
      } 

      lock (_Locker) 
      { 
       if (permission == Permission.Microphone) 
       { 
        if (!_Results.ContainsKey(Permission.Speech)) 
        { 
         _Results.Add(Permission.Speech, grantResults[i] == acp.Permission.Granted ? PermissionStatus.Granted : PermissionStatus.Denied); 
        } 
       } 
       if (!_Results.ContainsKey(permission)) 
       { 
        _Results.Add(permission, grantResults[i] == acp.Permission.Granted ? PermissionStatus.Granted : PermissionStatus.Denied); 
       } 
      } 
     } 
     _Tcs.SetResult(_Results); 
    } 
1

我在Visual Studio級別的Android Manifest中打開了此權限,但問題仍然存在。

從Android 6.0(API級別23)開始,用戶在應用程序運行時爲應用程序授予權限,而不是在安裝應用程序時授予應用程序權限。您應該在運行時requesting permissions

系統權限分爲兩類,普通和危險:

  • 普通權限不直接風險用戶的隱私。如果您的應用程序在其清單中列出了正常權限,系統將自動授予權限。
  • 危險的權限可以讓應用訪問用戶的機密數據。如果您的應用程序在其清單中列出了正常權限,系統將自動授予權限。如果您列出危險的權限,用戶必須明確地批准您的應用。

java.lang.SecurityException異常:權限拒絕:閱讀com.android.providers.media.MediaProvider ...需要android.permission。READ_EXTERNAL_STORAGE

READ_EXTERNAL_STORAGEDangerous permissions劃分,所以你應該手動檢查這個權限的API等級23以上。

Here是關於如何在運行時請求權限的示例。

+0

@Melbourne開發者,你有沒有解決問題了嗎? –

+0

謝謝。你的文章引導我回到我的最終答案。我一直在使用James Montemontagno的代碼來做這件事,但是這段代碼並不是要求準備就緒,所以我編輯了這段代碼來解決這個問題。我會寫另一篇文章,並提出完整的解決方案。 –