2012-08-29 15 views
40

私の目標は、内部ストレージにXMLファイルを作成し、それを共有インテントで送信することです。私はこのコード内部ストレージからファイルを作成して共有する

FileOutputStream outputStream = context.openFileOutput(fileName, Context.MODE_WORLD_READABLE); 
PrintStream printStream = new PrintStream(outputStream); 
String xml = this.writeXml(); // get XML here 
printStream.println(xml); 
printStream.close(); 

を使用してXMLファイルを作成することができるよ

私はそれを共有するために、出力ファイルにURIを取得しようとこだわっています。私は最初のウリにファイルを変換してファイルにアクセスしようとした

File outFile = context.getFileStreamPath(fileName); 
return Uri.fromFile(outFile); 

これはファイルを返します。///data/data/com.my.package/files/myfile.xmlを私がすることはできませんこれを電子メールに添付してアップロードするなど。

ファイルの長さを手動でチェックすると適切であり、妥当なファイルサイズがあることを示します。

次はコンテンツプロバイダを作成し、そのファイルを参照しようとしましたが、ファイルの有効なハンドルではありません。 ContentProviderはこれまでどおりとは言えません。

Uri uri = Uri.parse("content://" + CachedFileProvider.AUTHORITY + "/" + fileName); 
return uri; 

これはコンテンツを返します。//com.my.package.provider/myfile.xmlを、私は、ファイルをチェックし、それがゼロの長さです。

ファイルに正しくアクセスするにはどうすればよいですか?コンテンツプロバイダでファイルを作成する必要はありますか?もしそうなら、どうですか?ここで

更新

は私が共有するために使用しているコードです。 Gmailを選択すると添付ファイルとして表示されますが、送信するとエラーが発生します添付ファイルを表示できませんでした。到着したメールには添付ファイルがありません。

public void onClick(View view) { 
    Log.d(TAG, "onClick " + view.getId()); 

    switch (view.getId()) { 
     case R.id.share_cancel: 
      setResult(RESULT_CANCELED, getIntent()); 
      finish(); 
      break; 

     case R.id.share_share: 

      MyXml xml = new MyXml(); 
      Uri uri; 
      try { 
       uri = xml.writeXmlToFile(getApplicationContext(), "myfile.xml"); 
       //uri is "file:///data/data/com.my.package/files/myfile.xml" 
       Log.d(TAG, "Share URI: " + uri.toString() + " path: " + uri.getPath()); 

       File f = new File(uri.getPath()); 
       Log.d(TAG, "File length: " + f.length()); 
       // shows a valid file size 

       Intent shareIntent = new Intent(); 
       shareIntent.setAction(Intent.ACTION_SEND); 
       shareIntent.putExtra(Intent.EXTRA_STREAM, uri); 
       shareIntent.setType("text/plain"); 
       startActivity(Intent.createChooser(shareIntent, "Share")); 
      } catch (FileNotFoundException e) { 
       e.printStackTrace(); 
      } 

      break; 
    } 
} 

私はcreateChooser(...)内側からここで投げExceptionがあることに気づいたが、それがスローされますなぜ私は把握することはできません。

E/ActivityThread(572):アクティビティ com.android.internal.app.ChooserActivityは もともとここに登録されましたIntentReceiver [email protected]が漏れました。あなたは のunregisterReceiver()への呼び出しを逃していますか?

私はこのエラーを調査しており、何も明らかではありません。これらのリンクの両方は、私が受信機の登録を解除する必要があることを示唆しています。

Iは、受信を設定しているが、それは別の場所に設定され、/登録解除を登録するにはアプリを必要としないAlarmManagerのためです。

openFile(...)

私が作成したコンテンツプロバイダが必要な場合は、こちらをご覧ください。

public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 
    String fileLocation = getContext().getCacheDir() + "/" + uri.getLastPathSegment(); 

    ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(fileLocation), ParcelFileDescriptor.MODE_READ_ONLY); 
    return pfd; 
} 
+0

私はcontentProviderを作成するのではなく、最初のメソッドがうまくいくと思います。 ContentProviderは、ファイル全体ではなくデータを返すために使用されます。私の推測では、あなたのXMLを処理するアプリケーションはありません。あなたの共有の意図が作成されているコードを投稿することができます – nandeesh

+0

それがより明白になる場合のためにコードの多くが追加され、例外として私はそれが関連しているかどうかわかりません。 – Kirk

+0

ContentProviderを作成しましたか?もしあなたが持っていれば、あなたのコードを投稿できますか?必要がない場合は、ContentProviderを作成してopenFileメソッドをオーバーライドする必要があります。このメソッドは、content://com.my.package.provider/myfile.xml uriに関連付けられたファイルを開こうとすると、gmailによって呼び出されます。 – Rob

答えて

35

ContentProviderを介してアプリのプライベートディレクトリに保存されているファイルを公開することは可能です。これを行うことができるコンテンツプロバイダを作成する方法を示したコードの例を示します。

@Override 
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {  
    File cacheDir = getContext().getCacheDir(); 
    File privateFile = new File(cacheDir, "file.xml"); 

    return ParcelFileDescriptor.open(privateFile, ParcelFileDescriptor.MODE_READ_ONLY); 
} 

あなたは今、キャッシュディレクトリ

private void copyFileToInternal() { 
    try { 
     InputStream is = getAssets().open("file.xml"); 

     File cacheDir = getCacheDir(); 
     File outFile = new File(cacheDir, "file.xml"); 

     OutputStream os = new FileOutputStream(outFile.getAbsolutePath()); 

     byte[] buff = new byte[1024]; 
     int len; 
     while ((len = is.read(buff)) > 0) { 
      os.write(buff, 0, len); 
     } 
     os.flush(); 
     os.close(); 
     is.close(); 

    } catch (IOException e) { 
     e.printStackTrace(); // TODO: should close streams properly here 
    } 
} 

に、他のXMLファイルをコピーしたことを確認しParcelFileDescriptorを返すためにあなたのContentProviderオーバーライドのOpenFileで

<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    package="com.example.providertest" 
    android:versionCode="1" 
    android:versionName="1.0"> 

    <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="15" /> 

    <application android:label="@string/app_name" 
    android:icon="@drawable/ic_launcher" 
    android:theme="@style/AppTheme"> 

    <activity 
     android:name=".MainActivity" 
     android:label="@string/app_name"> 
     <intent-filter> 
      <action android:name="android.intent.action.MAIN" /> 
      <category android:name="android.intent.category.LAUNCHER" /> 
     </intent-filter> 
    </activity> 

    <provider 
     android:name="MyProvider" 
     android:authorities="com.example.prov" 
     android:exported="true" 
     />   
    </application> 
</manifest> 

マニフェストアプリはあなたのプライベートファイル用のInputStreamを利用することができるはずですグラムコンテンツのURI:簡単なテストのために(コンテンツ//com.example.prov/myfile.xml)

、あなたが必要な場合は、次の

private class MyTask extends AsyncTask<String, Integer, String> { 

    @Override 
    protected String doInBackground(String... params) { 

     Uri uri = Uri.parse("content://com.example.prov/myfile.xml"); 
     InputStream is = null;   
     StringBuilder result = new StringBuilder(); 
     try { 
      is = getApplicationContext().getContentResolver().openInputStream(uri); 
      BufferedReader r = new BufferedReader(new InputStreamReader(is)); 
      String line; 
      while ((line = r.readLine()) != null) { 
       result.append(line); 
      }    
     } catch (FileNotFoundException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } finally { 
      try { if (is != null) is.close(); } catch (IOException e) { } 
     } 

     return result.toString(); 
    } 

    @Override 
    protected void onPostExecute(String result) { 
     Toast.makeText(CallerActivity.this, result, Toast.LENGTH_LONG).show(); 
     super.onPostExecute(result); 
    } 
} 
+1

返信いただきありがとうございます。プライベートデータにアクセスするためにContentProviderを使用したいと考えています。 getExternalCacheDir()は、ターゲットにする必要があるAPI 7ではサポートされていません。 – Kirk

+0

API 7では、getExternalStorageDirectory()を使用してパブリックディレクトリを取得できます。私は、ContentProviderを使用していても、ファイルシステムのアプリケーションプライベート部分にパブリックアクセスを提供することが可能であるかどうかはわかりません。 – Rob

+0

提案していただきありがとうございます。私はsdcardではなく内部ストレージに書き込むことを検討しています。どのようにそれを達成するための任意の考えですか? – Kirk

3

に似た別々のアプリからコンテンツプロバイダを呼び出しますあなたのアプリのプライベートファイル(共有用など)を見るために他のアプリを許可するには、時間を節約してv4互換ライブラリを使うだけかもしれません。FileProvider

13

ロブの答えは正しいと思いますが、 。私が理解する限り、プロバイダーの設定で:

android:exported="true" 

あなたはすべてのファイルに一般にアクセスできますか?とにかく、いくつかのファイルにのみアクセス権を与えるための方法は、以下のようにファイルパスのアクセス権を定義することです:

<provider 
    android:authorities="com.your.app.package" 
    android:name="android.support.v4.content.FileProvider" 
    android:exported="false" 
    android:grantUriPermissions="true"> 
    <meta-data 
     android:name="android.support.FILE_PROVIDER_PATHS" 
     android:resource="@xml/file_paths" /> 
</provider> 

と、以下のように、あなたのXMLディレクトリにあなたはfile_paths.xmlファイルを定義します。

​​を

今のところ、 "allfiles"はオプションと同じ種類のパブリックパーミッションを提供します。アンドロイド:exported = "true"しかし、私はサブディレクトリを定義するのが本当に欲しいとは思わないので、次の行です。次に、共有するファイルをそのディレクトリに保存するだけです。

次は、Robが言うように、このファイルのURIを取得することです。これは私がそれをやった方法です。そして、

Uri contentUri = FileProvider.getUriForFile(context, "com.your.app.package", sharedFile); 

、私はこのURIを持っているとき、私は他のアプリはそれを使用することが許可に添付しなければなりませんでした。私は、このファイルURIをカメラアプリに使用しています。とにかく、これは私が他のアプリのパッケージ情報を持って、URIに権限を付与する方法の方法である:私は私が上で動作しませんでした他のいくつかの例を取ることを望まなかったとして

PackageManager packageManager = getPackageManager(); 
List<ResolveInfo> list = packageManager.queryIntentActivities(cameraIntent, PackageManager.MATCH_DEFAULT_ONLY); 
if (list.size() < 1) { 
    return; 
} 
String packageName = list.get(0).activityInfo.packageName; 
grantUriPermission(packageName, sharedFileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); 

ClipData clipData = ClipData.newRawUri("CAMFILE", sharedFileUri); 
cameraIntent.setClipData(clipData); 
cameraIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 
cameraIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 
startActivityForResult(cameraIntent, GET_FROM_CAMERA); 

私はカメラ用のコードを残しました。しかし、この方法では、URI自体にアクセス許可を付けることができます。

カメラの主な点は、ClipDataで設定して、さらにアクセス権を設定できることです。あなたの場合は、ファイルを電子メールに添付するときにFLAG_GRANT_READ_URI_PERMISSIONだけが必要です。

ここには、私がそこに見つけたすべての投稿に基づいてFileProviderを支援するlinkがあります。カメラアプリのパッケージ情報を見つけるのにいくつかの問題がありました。

希望します。