1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bluetooth.avrcpcontroller;
18 
19 import android.util.Log;
20 
21 import java.util.Objects;
22 
23 /**
24  * Describes a single native or variant format available for an image, coming from a
25  * BipImageProperties object.
26  *
27  * This is not an object by specification, per say. It abstracts all the various native and variant
28  * formats available in a given set of image properties.
29  *
30  * This BipImageFormat can be used to choose a specific BipImageDescriptor when downloading an image
31  *
32  * Examples:
33  *   <native encoding="JPEG" pixel="1280*1024” size="1048576"/>
34  *   <variant encoding="JPEG" pixel="640*480"/>
35  *   <variant encoding="JPEG" pixel="160*120"/>
36  *   <variant encoding="GIF" pixel="80*60-640*480"/>
37  */
38 public class BipImageFormat {
39     private static final String TAG = "avrcpcontroller.BipImageFormat";
40 
41     public static final int FORMAT_NATIVE = 0;
42     public static final int FORMAT_VARIANT = 1;
43 
44     /**
45      * Create a native BipImageFormat from the given string fields
46      */
parseNative(String encoding, String pixel, String size)47     public static BipImageFormat parseNative(String encoding, String pixel, String size) {
48         return new BipImageFormat(BipImageFormat.FORMAT_NATIVE, encoding, pixel, size, null, null);
49     }
50 
51     /**
52      * Create a variant BipImageFormat from the given string fields
53      */
parseVariant(String encoding, String pixel, String maxSize, String transformation)54     public static BipImageFormat parseVariant(String encoding, String pixel, String maxSize,
55             String transformation) {
56         return new BipImageFormat(BipImageFormat.FORMAT_VARIANT, encoding, pixel, null, maxSize,
57                 transformation);
58     }
59 
60     /**
61      * Create a native BipImageFormat from the given parameters
62      */
createNative(BipEncoding encoding, BipPixel pixel, int size)63     public static BipImageFormat createNative(BipEncoding encoding, BipPixel pixel, int size) {
64         return new BipImageFormat(BipImageFormat.FORMAT_NATIVE, encoding, pixel, size, -1, null);
65     }
66 
67     /**
68      * Create a variant BipImageFormat from the given parameters
69      */
createVariant(BipEncoding encoding, BipPixel pixel, int maxSize, BipTransformation transformation)70     public static BipImageFormat createVariant(BipEncoding encoding, BipPixel pixel, int maxSize,
71             BipTransformation transformation) {
72         return new BipImageFormat(BipImageFormat.FORMAT_VARIANT, encoding, pixel, -1, maxSize,
73                 transformation);
74     }
75 
76     /**
77      * The 'flavor' of this image format, from the format constants above.
78      */
79     private final int mFormatType;
80 
81     /**
82      * The encoding method in which this image is available, required by the specification
83      */
84     private final BipEncoding mEncoding;
85 
86     /**
87      * The pixel size or range of pixel sizes in which the image is available, required by the
88      * specification
89      */
90     private final BipPixel mPixel;
91 
92     /**
93      * The list of supported image transformation methods, any of:
94      *   - 'stretch' : Image server is capable of stretching the image to fit a space
95      *   - 'fill' : Image server is capable of filling the image padding data to fit a space
96      *   - 'crop' : Image server is capable of cropping the image down to fit a space
97      *
98      * Used by the variant type only
99      */
100     private final BipTransformation mTransformation;
101 
102     /**
103      * Size in bytes of the image.
104      *
105      * Used by the native type only
106      */
107     private final int mSize;
108 
109     /**
110      * The estimated maximum size of an image after a transformation is performed.
111      *
112      * Used by the variant type only
113      */
114     private final int mMaxSize;
115 
BipImageFormat(int type, BipEncoding encoding, BipPixel pixel, int size, int maxSize, BipTransformation transformation)116     private BipImageFormat(int type, BipEncoding encoding, BipPixel pixel, int size, int maxSize,
117             BipTransformation transformation) {
118         mFormatType = type;
119         mEncoding = Objects.requireNonNull(encoding, "Encoding cannot be null");
120         mPixel = Objects.requireNonNull(pixel, "Pixel cannot be null");
121         mTransformation = transformation;
122         mSize = size;
123         mMaxSize = maxSize;
124     }
125 
BipImageFormat(int type, String encoding, String pixel, String size, String maxSize, String transformation)126     private BipImageFormat(int type, String encoding, String pixel, String size, String maxSize,
127             String transformation) {
128         mFormatType = type;
129         mEncoding = new BipEncoding(encoding);
130         mPixel = new BipPixel(pixel);
131         mTransformation = new BipTransformation(transformation);
132         mSize = parseInt(size);
133         mMaxSize = parseInt(maxSize);
134     }
135 
parseInt(String s)136     private static int parseInt(String s) {
137         if (s == null) return -1;
138         try {
139             return Integer.parseInt(s);
140         } catch (NumberFormatException e) {
141             error("Failed to parse '" + s + "'");
142         }
143         return -1;
144     }
145 
getType()146     public int getType() {
147         return mFormatType;
148     }
149 
getEncoding()150     public BipEncoding getEncoding() {
151         return mEncoding;
152     }
153 
getPixel()154     public BipPixel getPixel() {
155         return mPixel;
156     }
157 
getTransformation()158     public BipTransformation getTransformation() {
159         return mTransformation;
160     }
161 
getSize()162     public int getSize() {
163         return mSize;
164     }
165 
getMaxSize()166     public int getMaxSize() {
167         return mMaxSize;
168     }
169 
170     @Override
equals(Object o)171     public boolean equals(Object o) {
172         if (o == this) return true;
173         if (!(o instanceof BipImageFormat)) return false;
174 
175         BipImageFormat f = (BipImageFormat) o;
176         return f.getType() == getType()
177                 && f.getEncoding() == getEncoding()
178                 && f.getPixel() == getPixel()
179                 && f.getTransformation() == getTransformation()
180                 && f.getSize() == getSize()
181                 && f.getMaxSize() == getMaxSize();
182     }
183 
184     @Override
toString()185     public String toString() {
186         if (mEncoding == null || mEncoding.getType() == BipEncoding.UNKNOWN || mPixel == null
187                 || mPixel.getType() == BipPixel.TYPE_UNKNOWN) {
188             error("Missing required fields [ " + (mEncoding == null ? "encoding " : "")
189                     + (mPixel == null ? "pixel " : ""));
190             return null;
191         }
192 
193         StringBuilder sb = new StringBuilder();
194         switch (mFormatType) {
195             case FORMAT_NATIVE:
196                 sb.append("<native");
197                 sb.append(" encoding=\"" + mEncoding.toString() + "\"");
198                 sb.append(" pixel=\"" + mPixel.toString() + "\"");
199                 if (mSize > -1) {
200                     sb.append(" size=\"" + mSize + "\"");
201                 }
202                 sb.append(" />");
203                 return sb.toString();
204             case FORMAT_VARIANT:
205                 sb.append("<variant");
206                 sb.append(" encoding=\"" + mEncoding.toString() + "\"");
207                 sb.append(" pixel=\"" + mPixel.toString() + "\"");
208                 if (mTransformation != null && mTransformation.supportsAny()) {
209                     sb.append(" transformation=\"" + mTransformation.toString() + "\"");
210                 }
211                 if (mSize > -1) {
212                     sb.append(" size=\"" + mSize + "\"");
213                 }
214                 if (mMaxSize > -1) {
215                     sb.append(" maxsize=\"" + mMaxSize + "\"");
216                 }
217                 sb.append(" />");
218                 return sb.toString();
219             default:
220                 error("Unsupported format type '" + mFormatType + "'");
221         }
222         return null;
223     }
224 
error(String msg)225     private static void error(String msg) {
226         Log.e(TAG, msg);
227     }
228 }
229