[AIR] Adoebe AIRで、AndroidのFileProviderを使ったANEを作りたい

2019/03/2

こんにちは。きんくまです。

もうやっている人もいないと思うので、個人的なメモです。その2

Androidです。Intentで他のアプリに、Uriを連携したいです。

以前は以下のようなことができていました。

File file = new File("path");
Uri myuri = Uri.fromFile(file);

それで、バージョンがあがってセキュリティが厳しくなって、この方法だと他のアプリに連携できなくなっていました。
調べたところ、FileProviderというのを使うといいみたい。

FileProvider | Android Developers

file:// 形式じゃなくて、 content:// 形式で送ります。詳しい実装方法は他にゆずります。

それで、これを使ったANEを作りたかったのだけど、すごくハマってしまい時間がかかってしまいました。

大いなる参考サイト
DigitalStrawberry/ANE-Share: Adobe AIR native extension for sharing text and bitmap content.

このソースをみつつやっていたのですが、細かいところでAndroid Studioのバージョンが上がっていたりして、うまくいきませんでした。

FileProviderの入っているsuppport-v4のjarを取り出す

FileProviderは android.support.v4 という外部パッケージに入っています。
なので、こいつを何とかしないといけない。

普通にgradleにこういう風に書いてもだめだった。上の参考のANEはうまくいっているので、何か関係しているのかもしれない。
設定をimplementationじゃなくてAPIにするとか、、。

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    implementation 'com.android.support:support-v4:27.1.1'
}

で、結局最後のsuppor-v4の行は消しました。

Android SDKの以下のパス

Android/sdk/extras/android/m2repository/com/android/support

ここにほしいやつがあります。この中から以下のものを取り出しました。

support-compat-25.3.1.aar
support-core-ui-25.3.1.aar
support-core-utils-25.3.1.aar
support-fragment-25.3.1.aar
support-media-compat-25.3.1.aar
support-v4-25.3.1.aar

どこにリストがあるかというとここです。
v4 Support Libraries

どうやらsuppor-v4は、あるバージョンから、ライブラリの機能が複数のファイルに分割されたみたいです。

で、aarというのはライブラリの形式です。こいつをzipに拡張子をかえて、解凍します。

すると、中に classes.jarというのを含んだフォルダができあがりますので、classes.jarをそれぞれのファイル名に書き換えて取り出します。

support-compat-25.3.1.jar
support-core-ui-25.3.1.jar
support-core-utils-25.3.1.jar
support-fragment-25.3.1.jar
support-media-compat-25.3.1.jar
support-v4-25.3.1.jar

これらをAnrdoidライブラリプロジェクトのlibsフォルダに追加します。FlashRuntimeExtensions.jarと同じところ。
それで、ネイティブのコードを書いていきます。

今回、メールに複数添付ファイルをするANEを作ったので、こんな感じです。

package com.kumade.ane.mailanelibandroid;


import java.io.File;
import java.util.ArrayList;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v4.content.FileProvider;
import android.util.Log;

import com.adobe.fre.FREArray;
import com.adobe.fre.FREContext;
import com.adobe.fre.FREFunction;
import com.adobe.fre.FREObject;

public class SendMailFunction implements FREFunction {
    public final String TAG = "com.kumade.mail";
    public String[] toRecipients;
    public String[] ccRecipients;
    public String[] bccRecipients;
    public String subject;
    public String bodyText;
    public String[] attachementPaths;

    @Override
    public FREObject call(FREContext context, FREObject[] args) {
        readArguments(args);
        sendMail(context);
        return null;
    }

    public void readArguments(FREObject[] args){

        FREArray argFREArray = null;

        //to
        try {
            Log.d(TAG, "to:");
            argFREArray = (FREArray)args[0];
            //arrayLength = getFREArrayLength(argFREArray);
            //toRecipients = new String[arrayLength];
            toRecipients = storeStringsFromFREArray(argFREArray);
            //storeToRecipients((FREArray)arg1[0]);
        }catch(Exception e){
            e.printStackTrace();
        }

        //cc
        try {
            Log.d(TAG, "cc:");
            argFREArray = (FREArray)args[1];
            //arrayLength = getFREArrayLength(argFREArray);
            //ccRecipients = new String[arrayLength];
            ccRecipients = storeStringsFromFREArray(argFREArray);
            //storeCcRecipients((FREArray)arg1[1]);
        }catch(Exception e){
            e.printStackTrace();
        }

        //bcc
        try {
            Log.d(TAG, "bcc:");
            argFREArray = (FREArray)args[2];
            //arrayLength = getFREArrayLength(argFREArray);
            //bccRecipients = new String[arrayLength];
            bccRecipients = storeStringsFromFREArray(argFREArray);
            //storeBccRecipients((FREArray)arg1[2]);
        }catch(Exception e){
            e.printStackTrace();
        }

        //subject
        try{
            subject = args[3].getAsString();
            Log.d(TAG, "subject: \n" + subject);
        }catch(Exception e){
            e.printStackTrace();
        }

        //body text
        try{
            bodyText = args[4].getAsString();
            Log.d(TAG, "bodyText: \n" + bodyText);
        }catch(Exception e){
            e.printStackTrace();
        }

        //attachment paths
        try {
            Log.d(TAG, "attachment paths:");
            argFREArray = (FREArray)args[5];
            attachementPaths = storeStringsFromFREArray(argFREArray);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    public void sendMail(FREContext context){
        final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE);
        emailIntent.setType("text/plain");
        emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);


        ArrayList<Uri> attachmentUris = new ArrayList<Uri>();

        for (String file : attachementPaths) {
            File fileIn = new File(file);
            Context appContext = context.getActivity().getApplicationContext();
            Uri myURI = FileProvider.getUriForFile(appContext, appContext.getPackageName() + ".fileprovider", fileIn);
            attachmentUris.add(myURI);
        }

        emailIntent.putExtra(Intent.EXTRA_EMAIL, toRecipients);

        if(ccRecipients.length > 0){
            emailIntent.putExtra(Intent.EXTRA_CC, ccRecipients);
        }
        if(bccRecipients.length > 0){
            emailIntent.putExtra(Intent.EXTRA_BCC, bccRecipients);
        }
        emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
        emailIntent.putExtra(Intent.EXTRA_TEXT, bodyText);
        if(attachmentUris.size() > 0){
            emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachmentUris);
        }

        context.getActivity().startActivity(Intent.createChooser(emailIntent, "Email:"));
    }

    public int getFREArrayLength(FREArray freArray) throws Exception{
        return (int)(freArray.getLength());
    }

    public String[] storeStringsFromFREArray(FREArray freArray) throws Exception{
        String[] targetStrings = new String[(int)freArray.getLength()];
        for(int i = 0; i < targetStrings.length; i++){
            FREObject fObj = freArray.getObjectAt(i);
            targetStrings[i] = fObj.getAsString();
            Log.d(TAG, "string" + i + " " + targetStrings[i]);
        }
        return targetStrings;
    }

}

あと、res/xml の中に以下のようなファイルを追加します。この辺は、さきほどのAndroid SDK公式リファレンスを参照。

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_docs" path="."/>
</paths>

Android Studioでのビルドは、以前書いた記事を参考にGradleで clean > build します。
[AS3] Android Studio でもANE用のjarを作りたい

そうすると、ANEを作るための jar はここにあります。

AndroidStudioプロジェクト/モジュール名/build/outputs/aar/モジュール名-release.aar

aarなんですが、さきほどと同じように、aar を zip に変えて解凍して出てくるclasses.jarファイルを取り出します。

ANEを作る準備ができたので、これからANEをビルドします。

android内のフォルダはこんな感じになります。androidMailANE.jarというのは、さきほどの モジュール名-release.aar からとりだしてファイル名を変えた.jarファイル。

res/xml/ のところは階層になるようにそれぞれフォルダ作ってください。

androidMailANE.jar
library.swf
res/xml/filepaths.xml
support-compat-25.3.1.jar
support-core-ui-25.3.1.jar
support-core-utils-25.3.1.jar
support-fragment-25.3.1.jar
support-media-compat-25.3.1.jar
support-v4-25.3.1.jar

それで、android_options.xmlというのを作ります。

<platform xmlns="http://ns.adobe.com/air/extension/32.0">
    <packagedDependencies>
        <packagedDependency>support-compat-25.3.1.jar</packagedDependency>
        <packagedDependency>support-core-ui-25.3.1.jar</packagedDependency>
        <packagedDependency>support-core-utils-25.3.1.jar</packagedDependency>
        <packagedDependency>support-fragment-25.3.1.jar</packagedDependency>
        <packagedDependency>support-media-compat-25.3.1.jar</packagedDependency>
        <packagedDependency>support-v4-25.3.1.jar</packagedDependency>
    </packagedDependencies>
    
    <packagedResources>
        <packagedResource>
            <packageName>com.kumade.ane.mailanelibandroid</packageName>
            <folderName>res</folderName>
        </packagedResource>
    </packagedResources>
</platform>

ようやくANEのビルドです。ここで、Android-ARM の -platformoptions をつけ忘れて、数日苦しんだのはナイショです、、。

#adt command ========
adt -package \
-target ane ${ANEName} extension.xml \
-platform iPhone-ARM \
  -platformoptions ios_options.xml \
  -C ${iOSDir} . \
-swc ${ASLibSwcName} \
-platform Android-ARM \
  -platformoptions android_options.xml \
  -C android . \
-platform default \
  -C default . 

これで、ANEのビルドができました。

使うとき

普通にANEを追加するところまではいいのですが、application.xmlに以下の部分を追加する必要があります。

	<android>
        <manifestAdditions><![CDATA[
			<manifest android:installLocation="auto">
			    <!--このあたりのパーミッションは適当。WRITE_EXTERNAL_STORAGEだけ必要かも -->
			    <uses-permission android:name="android.permission.INTERNET"/>
			    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
			    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
			    
				<application>

			        <provider
                        android:name="android.support.v4.content.FileProvider"
                        android:authorities="air.あなたのアプリID.fileprovider"
                        android:grantUriPermissions="true"
                        android:exported="false">
                        <meta-data
                            android:name="android.support.FILE_PROVIDER_PATHS"
                            android:resource="@xml/filepaths" />
                    </provider>
	        	</application>
			</manifest>
			
		]]></manifestAdditions>
    </android>

android:authorities=”air.あなたのアプリID.fileprovider”

となっています。AndroidSDKの説明ページではここのところが “アプリID.fileprovider” となっているのですが、AIR for Androidは applicaiton.xml で指定したIDに “air” という接頭詞を自動でつけるので注意が必要です。

あと注意するのは、AS3の方で、ファイルの書き込み権限が必要か見て、必要そうならアラートを出してあげることぐらいでしょうか。

		private function requestFilePermissionOnAndroidIfNeeds():Boolean{
            // DeviceInfoは自作のユーティリティクラスなので気にしないでくださいまし
            if(DeviceInfo.sharedInfo.isAndroid){
                var myFile:File = File.applicationDirectory;
                if(File.permissionStatus == PermissionStatus.GRANTED){
                    return true;
                }else{
                    try {
                        myFile.requestPermission();
                    }catch(e:Error){
                        trace("Permission Error");
                    }
					return false;
                }
            }else{
                return true;
            }
		}

同じようなところで苦しむ人が、日本にあと2人くらいはいるんじゃないかと思って書いてみました。
ロストテクノロジーのつらみですなーw

LINEで送る
Pocket

自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

LINEスタンプ作りました!
毎日使える。とぼけたウサギ。LINEスタンプ販売中! 毎日使える。とぼけたウサギ

ページトップへ戻る