拡張性 - Android

Android レンダラーは、次のような複数のシナリオをサポートするように拡張できます。

カード要素のカスタム解析

定義したカード要素をサポートするようにパーサーを拡張することができます。 たとえば、次のような新しい要素の型があるとします。

{
    "type" : "MyType",
    "MyTypeData" : "My data"
}

次の行は、BaseCardElement から拡張する CardElement に解析する方法を示します。

public class MyCustomCardElement extends BaseCardElement
{

    public MyCustomCardElement(CardElementType type) {
        super(type);
    }

    public String getMyTypeData()
    {
        return myTypeData;
    }

    public void setMyTypeData(String data)
    {
        myTypeData = data;
    }

    private String myTypeData;
}

public class MyCardElementParser extends BaseCardElementParser
{
    @Override
    public BaseCardElement Deserialize(ElementParserRegistration elementParserRegistration, ActionParserRegistration actionParserRegistration, JsonValue value)
    {
        MyCustomCardElement element = new CustomCardElement(CardElementType.Custom);
        element.SetElementTypeString("MyType");
        String val = value.getString();
        try {
            JSONObject obj = new JSONObject(val);
            element.setMyTypeData(obj.getString("secret"));
        } catch (JSONException e) {
            e.printStackTrace();
            element.setMyTypeData("Failed");
        }
        return element;
    }
}

また、パーサーを登録し、カスタム要素を含む AdaptiveCard オブジェクトを取得する方法は次のとおりです。

// Create an ElementParserRegistrationObject and add your parser to it
ElementParserRegistration elementParserRegistration = new ElementParserRegistration();
elementParserRegistration.AddParser("MyType", new MyCardElementParser());

AdaptiveCard adaptiveCard = AdaptiveCard.DeserializeFromString(jsonText, elementParserRegistration);

次に、カスタム要素をレンダリングします

カード要素のカスタム レンダリング

重要

重大な変更の一覧

v1.2 の破壊的変更

型に独自のカスタム レンダラーを定義するには、最初に次の値から BaseCardElementRenderer拡張されるクラスを作成する必要があります。

public class MyCardElementRenderer extends BaseCardElementRenderer
{
    @Override
    public View render(Context context, FragmentManager fragmentManager, ViewGroup viewGroup, BaseCardElement baseCardElement, Vector<IInputHandler> inputActionHandlerList, ICardActionHandler cardActionHandler, HostConfig hostConfig, ContainerStyle containerStyle) {

    	//Call findImplObj on baseCardElement to get the instance we returned at parse. We can then cast that object to our type
        CustomCardElement element = (CustomCardElement) baseCardElement.findImplObj();

        //Create some view and add it to the view group
        TextView textView = new TextView(context);
        textView.setText(element.getMyTypeData());
        textView.setAllCaps(true);
        viewGroup.addView(textView);

        //return the view
        return textView;
    }
}

次に、このレンダラーを次のように登録します。

CardRendererRegistration.getInstance().registerRenderer("MyType", new CustomBlahRenderer());

RenderedAdaptiveCard renderedCard = AdaptiveCardRenderer.getInstance().render(context, fragmentManager, adaptiveCard, cardActionHandler,  hostConfig);

v1.2 の破壊的変更

このメソッドは render パラメーターを RenderedAdaptiveCard 含むように変更され、 ContainerStyle ContainerStyle が含まれる RenderArgs に対して変更されたため、BaseCardElementRenderer を拡張するクラスは次のようになります。

public class MyCardElementRenderer extends BaseCardElementRenderer
{
    @Override
    public View render(RenderedAdaptiveCard renderedAdaptiveCard, Context context, FragmentManager fragmentManager, ViewGroup viewGroup,
                       BaseCardElement baseCardElement, ICardActionHandler cardActionHandler, HostConfig hostConfig, RenderArgs renderArgs)
    { }
}

カード アクションのカスタム解析

v1.2 でカスタム カード要素を解析するのと同様に、カスタム アクションを解析する可能性が導入されました。 たとえば、次のような新しいアクションの種類があるとします。

{
    "type" : "MyAction",
    "ActionData" : "My data"
}

次の行は、次から拡張される ActionElement に解析する方法を BaseActionElement示しています。

public class MyActionElement extends BaseActionElement
{
    public MyActionElement(ActionType type) 
    {
        super(type);
    }

    public String getActionData()
    {
        return mActionData;
    }

    public void setActionData(String s)
    {
        mActionData = s;
    }

    private String mActionData;
    public static final String MyActionId = "myAction";
}

public class MyActionParser extends ActionElementParser
{
    @Override
    public BaseActionElement Deserialize(ParseContext context, JsonValue value)
    {
        MyActionElement element = new MyActionElement(ActionType.Custom);
        element.SetElementTypeString(MyActionElement.MyActionId);
        String val = value.getString();
        try {
            JSONObject obj = new JSONObject(val);
            element.setActionData(obj.getString("ActionData"));
        } catch (JSONException e) {
            e.printStackTrace();
            element.setActionData("Failure");
        }
        return element;
    }

    @Override
    public BaseActionElement DeserializeFromString(ParseContext context, String jsonString)
    {
        MyActionElement element = new MyActionElement(ActionType.Custom);
        element.SetElementTypeString(MyActionElement.MyActionId);
        try {
            JSONObject obj = new JSONObject(jsonString);
            element.setBackwardString(obj.getString("ActionData"));
        } catch (JSONException e) {
            e.printStackTrace();
            element.setBackwardString("Failure");
        }
        return element;
    }
}

次の行は、パーサーを登録し、カスタム アクション要素を含む AdaptiveCard オブジェクトを取得する方法を示しています。

// Create an ActionParserRegistration and add your parser to it
ActionParserRegistration actionParserRegistration = new ActionParserRegistration();
actionParserRegistration.AddParser(MyActionElement.MyActionId, new MyActionParser());

ParseContext context = new ParseContext(null, actionParserRegistration);
ParseResult parseResult = AdaptiveCard.DeserializeFromString(jsonText, AdaptiveCardRenderer.VERSION, context);

次に、カスタム アクションをレンダリングします

アクションのカスタム レンダリング

型に独自のカスタム アクション レンダラーを定義するには、最初に次の値から BaseActionElementRenderer拡張されるクラスを作成する必要があります。

public class MyActionRenderer extends BaseActionElementRenderer
{
    @Override
    public Button render(RenderedAdaptiveCard renderedCard,
                         Context context,
                         FragmentManager fragmentManager,
                         ViewGroup viewGroup,
                         BaseActionElement baseActionElement,
                         ICardActionHandler cardActionHandler,
                         HostConfig hostConfig,
                         RenderArgs renderArgs)
    {
        Button myActionButton = new Button(context);

        CustomActionElement customAction = (CustomActionElement) baseActionElement.findImplObj();

        myActionButton.setBackgroundColor(getResources().getColor(R.color.greenActionColor));
        myActionButton.setText(customAction.getMessage());
        myActionButton.setAllCaps(false);
        myActionButton.setOnClickListener(new BaseActionElementRenderer.ActionOnClickListener(renderedCard, baseActionElement, cardActionHandler));

        viewGroup.addView(myActionButton);

        return myActionButton;
    }
}

次に、このレンダラーを次のように登録します。

CardRendererRegistration.getInstance().registerActionRenderer("myAction", new CustomActionRenderer());

RenderedAdaptiveCard renderedCard = AdaptiveCardRenderer.getInstance().render(context, fragmentManager, adaptiveCard, cardActionHandler, hostConfig);

アクションのカスタム レンダリング

重要

アクションのカスタム レンダリングへの変更は v1.2 で計画されていますが、まだ完了していません

カスタム画像の読み込み

IOnlineImageLoader

重要

登録できる IOnlineImageLoader は 1 つのみであり、既定の画像取得方法よりも優先されます

オンライン ソースからダウンロードまたは取得することができない画像、または取得する前に以前の手順が必要な画像を開発者が取得できるように、このような状況を解決する IOnlineImageLoader が追加されました。 OnlineImageLoader を実装するために必要なことは、メソッドの実装のみです

public HttpRequestResult<Bitmap> loadOnlineImage(String url, GenericImageLoaderAsync loader) throws IOException, URISyntaxException

猫の画像に関するすべての画像を変更する OnlineImageLoader の例を次に示します

public class OnlineImageLoader implements IOnlineImageLoader
{
    public OnlineImageLoader(){}

    @Override
    public HttpRequestResult<Bitmap> loadOnlineImage(String url, GenericImageLoaderAsync loader) throws IOException, URISyntaxException
    {
        String catImageUri = "http://adaptivecards.io/content/cats/1.png";
        byte[] bytes = HttpRequestHelper.get(catImageUri);
        if (bytes == null)
        {
            throw new IOException("Failed to retrieve content from " + catImageUri);
        }

        Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

        if (bitmap == null)
        {
            throw new IOException("Failed to convert content to bitmap: " + new String(bytes));
        }

        return new HttpRequestResult<>(bitmap);
    }
}

最後に、このイメージ ローダーを登録するために必要なことは、この行をコードに追加することのみです。

CardRendererRegistration.getInstance().registerOnlineImageLoader(new OnlineImageLoader());

IResourceResolver (IOnlineImageLoader の廃止)

v1.2 では、完全な ResourceResolver のサポートが Android レンダラーに追加されました。 リソース リゾルバーの実装は IOnlineImageLoader の実装とよく似ていますが、ResourceResolver があるので、開発者は、1 枚のカードであらゆる種類のソースから画像を取得する方法を複数追加することができます。これを実行するには、画像の取得時にクエリの対象になる固有のプレフィックスに個々の ResourceResolver をリンクします。

このような ResourceResolver を実装できます

public class ResourceResolver implements IResourceResolver
{
    @Override
    public HttpRequestResult<Bitmap> resolveImageResource(String uri, GenericImageLoaderAsync genericImageLoaderAsync) throws IOException, URISyntaxException
    {
        Bitmap bitmap;
        String dataUri = AdaptiveBase64Util.ExtractDataFromUri(uri);
        CharVector decodedDataUri = AdaptiveBase64Util.Decode(dataUri);
        byte[] decodedByteArray = Util.getBytes(decodedDataUri);
        bitmap = BitmapFactory.decodeByteArray(decodedByteArray, 0, decodedByteArray.length);

        return new HttpRequestResult<>(bitmap);
    }

    @Override
    public HttpRequestResult<Bitmap> resolveImageResource(String uri, GenericImageLoaderAsync genericImageLoaderAsync, int maxWidth) throws IOException, URISyntaxException
    {
        Bitmap bitmap;
        String dataUri = AdaptiveBase64Util.ExtractDataFromUri(uri);
        CharVector decodedDataUri = AdaptiveBase64Util.Decode(dataUri);

        if (uri.startsWith("data:image/svg")) {
            String svgString = AdaptiveBase64Util.ExtractDataFromUri(uri);
            String decodedSvgString = URLDecoder.decode(svgString, "UTF-8");
            Sharp sharp = Sharp.loadString(decodedSvgString);
            Drawable drawable = sharp.getDrawable();
            bitmap = ImageUtil.drawableToBitmap(drawable, maxWidth);
        }
        else
        {
            try
            {
                return genericImageLoaderAsync.loadDataUriImage(uri);
            }
            catch (Exception e)
            {
                return new HttpRequestResult<>(e);
            }
        }

        return new HttpRequestResult<>(bitmap);
    }
}

前述のように、これを実行できる ResourceResolver を登録するために、複数の ResourceResolver を登録することができます

 CardRendererRegistration.getInstance().registerResourceResolver("data", new ResourceResolver());
 CardRendererRegistration.getInstance().registerResourceResolver("anotherPrefix", new AnotherResourceResolver());

IOnlineImageLoader を IResourceResolver に変換する

IOnlineImageLoader の IResourceResolver への変換はとても簡単なタスクです。これは、後者のメソッドは、可能な限り IOnlineImageLoader と同じように維持されるように試行されたためです

 // IOnlineImageLoader
 public HttpRequestResult<Bitmap> loadOnlineImage(String url, GenericImageLoaderAsync loader) throws IOException, URISyntaxException

 // IResourceResolver
 public HttpRequestResult<Bitmap> resolveImageResource(String uri, GenericImageLoaderAsync genericImageLoaderAsync) throws IOException, URISyntaxException
 public HttpRequestResult<Bitmap> resolveImageResource(String uri, GenericImageLoaderAsync genericImageLoaderAsync, int maxWidth) throws IOException, URISyntaxException

ご覧のように、最大の変更点は次のとおりです

  • loadOnlineImage(String, GenericImageLoaderAsync) の名前が resolveImageResource(String, GenericImageLoaderAsync) に変更されました。
  • のオーバーロード resolveImageResource(String, GenericImageLoaderAsync) は、最大幅が必要なシナリオをサポートするために追加 resolveImageResource(String, GenericImageLoaderAsync, int) されました

カスタム メディアの読み込み

重要

API レベル 23 または Android M で追加された要件MediaDataSourceを覚えておいてくださいIOnlineMediaLoader

メディア要素の追加だけでなく、基礎となる mediaPlayer 要素に使用される MediaDataSource を開発者がオーバーライドできるようにする IOnlineMediaLoader インターフェイスも追加されました。 (android M が必要です)

最初に実行する必要があることは、IOnlineImageLoader を実装するクラスの作成です

@RequiresApi(api = Build.VERSION_CODES.M)
public class OnlineMediaLoader implements IOnlineMediaLoader
{
    /* This class checks if the media source exists */
    public class OnlineFileAvailableChecker extends AsyncTask<String, Void, Boolean>
    {
        public OnlineFileAvailableChecker(String uri)
        {
            m_uri = uri;
        }

        @Override
        protected Boolean doInBackground(String... strings) {
            // if the provided uri is a valid uri or is valid with the resource resolver, then use that
            // otherwise, try to get the media from a local file
            try
            {
                HttpRequestHelper.query(m_uri);
                return true;
            }
            catch (Exception e)
            {
                // Do nothing if the media was not found at all
                e.printStackTrace();
                return false;
            }
        }

        private String m_uri;
   }

       
   @Override
   public MediaDataSource loadOnlineMedia(MediaSourceVector mediaSources, IMediaDataSourceOnPreparedListener mediaDataSourceOnPreparedListener)
   {
       final long mediaSourcesSize = mediaSources.size();
       for(int i = 0; i < mediaSourcesSize; i++)
       {
           String mediaUri = mediaSources.get(i).GetUrl();

           OnlineFileAvailableChecker checker = new OnlineFileAvailableChecker(mediaUri);
           try
           {
               Boolean fileExists = checker.execute("").get();
               if(fileExists)
               {
                   return new MediaDataSourceImpl(mediaUri, mediaDataSourceOnPreparedListener);
               }
           }
           catch (Exception e) { }
       }
       return null;
    }
}

このクラスが実装されたら、次を追加して OnlineMediaLoader クラスを登録できます

  CardRendererRegistration.getInstance().registerOnlineMediaLoader(new OnlineMediaLoader());