拡張性 - AndroidExtensibility - Android

Android レンダラーは、次のような複数のシナリオをサポートするように拡張できます。The Android renderer can be extended to support multiple scenarios including:

カード要素のカスタム解析Custom Parsing of Card Elements

定義したカード要素をサポートするようにパーサーを拡張することができます。You may extend the parser to support card elements that you have defined. たとえば、次のような新しい要素の型があるとします。For example, say we have a new element type that looks like this:

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

次の行は、BaseCardElement から拡張する CardElement に解析する方法を示します。Then the following lines demonstrate how to parse it into a CardElement that extends from the BaseCardElement:

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 オブジェクトを取得する方法は次のとおりです。And this is how to register the parser and get an AdaptiveCard object that contains the custom element:

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

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

次に、カスタム要素をレンダリングしますNext comes rendering the custom element

カード要素のカスタム レンダリングCustom Rendering of Card Elements

重要

重大な変更の一覧List of Breaking Changes

v1.2 の破壊的変更Breaking changes for v1.2

型の独自のカスタムレンダラーを定義するには、まず、からを拡張するクラスを作成する必要があり BaseCardElementRenderer ます。To define our own custom renderer for our type, we must first create a class that extends from 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;
    }
}

次に、このレンダラーを次のように登録します。We then register this renderer like so:

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

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

v1.2 の破壊的変更Breaking changes for v1.2

メソッドがパラメーターを含むように変更され、 render BaseCardElementRenderer を RenderedAdaptiveCard ContainerStyle 拡張するクラスが次のようになるように、ContainerStyle が現在含まれている renderargs に対して変更されました。The render method was changed to include the RenderedAdaptiveCard parameter and ContainerStyle was changed for a RenderArgs where the ContainerStyle is now contained so a class that extends BaseCardElementRenderer should look like this

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)
    { }
}

カードアクションのカスタム解析Custom Parsing of Card Actions

同様に、v2.0 でカスタムカード要素を解析すると、カスタムアクションを解析することができました。Similarly to parsing custom card elements in v1.2 the possibility to parse custom actions was introduced. たとえば、次のような新しいアクションの種類があるとします。For example, say we have a new action type that looks like this:

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

次の行は、をから拡張する ActionElement に解析する方法を示してい BaseActionElement ます。Then the following lines demonstrate how to parse it into a ActionElement that extends from the 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;
    }
}

次の行は、パーサーを登録し、カスタムの action 要素を含む AdaptiveCard オブジェクトを取得する方法を示しています。And the next lines demonstrate how to register the parser and get an AdaptiveCard object that contains the custom action element:

// 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);

次にカスタムアクションを表示しますNext comes rendering the custom action

アクションのカスタム表示Custom Rendering of Actions

型の独自のカスタムアクションレンダラーを定義するには、まず、からを拡張するクラスを作成する必要があり BaseActionElementRenderer ます。To define our own custom action renderer for our type, we must first create a class that extends from 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;
    }
}

次に、このレンダラーを次のように登録します。We then register this renderer like so:

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

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

アクションのカスタム レンダリングCustom rendering of actions

重要

アクションのカスタム レンダリングへの変更は v1.2 で計画されていますが、まだ完了していませんChanges to the custom rendering of actions are planned for v1.2 but are not completed yet

カスタム画像の読み込みCustom image loading

IOnlineImageLoaderIOnlineImageLoader

重要

登録できる IOnlineImageLoader は 1 つのみであり、既定の画像取得方法よりも優先されますOnly one IOnlineImageLoader can be registered and it takes precedence against the default way of retrieving images

オンライン ソースからダウンロードまたは取得することができない画像、または取得する前に以前の手順が必要な画像を開発者が取得できるように、このような状況を解決する IOnlineImageLoader が追加されました。In order to allow developers to get images that could not just be downloaded or retrieved from an online source or required previous steps before they could be retrieved, the IOnlineImageLoader was added to solve this kind of situations. OnlineImageLoader を実装するために必要なことは、メソッドの実装のみですTo implement an OnlineImageLoader you must only implement the method

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

猫の画像に関するすべての画像を変更する OnlineImageLoader の例を次に示しますHere's an example of an OnlineImageLoader which changes all images for a cat image

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);
    }
}

最後に、このイメージ ローダーを登録するために必要なことは、この行をコードに追加することのみです。Finally, to register this image loader, you must only add this line to your code.

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

IResourceResolver (IOnlineImageLoader の廃止)IResourceResolver (deprecates IOnlineImageLoader)

v1.2 では、完全な ResourceResolver のサポートが Android レンダラーに追加されました。For v1.2, the support for full ResourceResolvers was added to the android renderer. リソース リゾルバーの実装は IOnlineImageLoader の実装とよく似ていますが、ResourceResolver があるので、開発者は、1 枚のカードであらゆる種類のソースから画像を取得する方法を複数追加することができます。これを実行するには、画像の取得時にクエリの対象になる固有のプレフィックスに個々の ResourceResolver をリンクします。The implementation of a resource resolver is really similar to that of a IOnlineImageLoader but having ResourceResolvers allows a developer to add multiple ways to retrieve images from any kind of source in a single card, this is done by linking each ResourceResolver to a unique prefix which will be queried when trying to retrieve an image.

このような ResourceResolver を実装できますYou can implement a ResourceResolver similar to this

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 を登録することができますAs mentioned previously, you can register multiple ResourceResolvers, to register a ResourceResolver you can do this

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

IOnlineImageLoader を IResourceResolver に変換するTransforming an IOnlineImageLoader to an IResourceResolver

IOnlineImageLoader の IResourceResolver への変換はとても簡単なタスクです。これは、後者のメソッドは、可能な限り IOnlineImageLoader と同じように維持されるように試行されたためですTransforming an IOnlineImageLoader to an IResourceResolver is a fairly easy task as the methods for the latter were tried to keep as similar as the IOnlineImageLoader as possible

 // 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

ご覧のように、最大の変更点は次のとおりですAs you can see, the biggest changes are

  • loadOnlineImage(String, GenericImageLoaderAsync) の名前が resolveImageResource(String, GenericImageLoaderAsync) に変更されました。loadOnlineImage(String, GenericImageLoaderAsync) was renamed to resolveImageResource(String, GenericImageLoaderAsync)
  • resolveImageResource(String, GenericImageLoaderAsync) resolveImageResource(String, GenericImageLoaderAsync, int) 最大幅が必要なシナリオをサポートするために、のオーバーロードがとして追加されました。an overload for resolveImageResource(String, GenericImageLoaderAsync) was added as resolveImageResource(String, GenericImageLoaderAsync, int) in order to support scenarios where the max width is required

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

重要

IOnlineMediaLoader MediaDataSource API レベル23または Android M で追加されたものが必要であることに注意してくださいRemember IOnlineMediaLoader requires MediaDataSource which was added in API level 23 or Android M

メディア要素の追加だけでなく、基礎となる mediaPlayer 要素に使用される MediaDataSource を開発者がオーバーライドできるようにする IOnlineMediaLoader インターフェイスも追加されました。Along with the inclusion of the media element, also was the inclusion of the IOnlineMediaLoader interface which allows developers to override the MediaDataSource used for the underlying mediaPlayer element. (android M が必要です)(Requires android M)

最初に実行する必要があることは、IOnlineImageLoader を実装するクラスの作成ですThe first needed thing to do is creating a class that implements 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 クラスを登録できますOnce this class has been implemented, you can register your OnlineMediaLoader class by adding

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