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.SparseArray;
20 
21 import java.util.HashMap;
22 
23 /**
24  * Represents an encoding method in which a BIP image is available.
25  *
26  * The encodings supported by this profile include:
27  *   - JPEG
28  *   - GIF
29  *   - WBMP
30  *   - PNG
31  *   - JPEG2000
32  *   - BMP
33  *   - USR-xxx
34  *
35  * The tag USR-xxx is used to represent proprietary encodings. The tag shall begin with the string
36  * “USR-” but the implementer assigns the characters of the second half of the string. This tag can
37  * be used by a manufacturer to enable its devices to exchange images under a proprietary encoding.
38  *
39  * Example proprietary encoding:
40  *
41  *   - USR-NOKIA-FORMAT1
42  */
43 public class BipEncoding {
44     public static final int JPEG = 0;
45     public static final int PNG = 1;
46     public static final int BMP = 2;
47     public static final int GIF = 3;
48     public static final int JPEG2000 = 4;
49     public static final int WBMP = 5;
50     public static final int USR_XXX = 6;
51     public static final int UNKNOWN = 7; // i.e 'not assigned' or 'not assigned anything valid'
52 
53     private static final HashMap sEncodingNamesToIds = new HashMap<String, Integer>();
54     static {
55         sEncodingNamesToIds.put("JPEG", JPEG);
56         sEncodingNamesToIds.put("GIF", GIF);
57         sEncodingNamesToIds.put("WBMP", WBMP);
58         sEncodingNamesToIds.put("PNG", PNG);
59         sEncodingNamesToIds.put("JPEG2000", JPEG2000);
60         sEncodingNamesToIds.put("BMP", BMP);
61     }
62 
63     private static final SparseArray sIdsToEncodingNames = new SparseArray<String>();
64     static {
sIdsToEncodingNames.put(JPEG, "JPEG")65         sIdsToEncodingNames.put(JPEG, "JPEG");
sIdsToEncodingNames.put(GIF, "GIF")66         sIdsToEncodingNames.put(GIF, "GIF");
sIdsToEncodingNames.put(WBMP, "WBMP")67         sIdsToEncodingNames.put(WBMP, "WBMP");
sIdsToEncodingNames.put(PNG, "PNG")68         sIdsToEncodingNames.put(PNG, "PNG");
sIdsToEncodingNames.put(JPEG2000, "JPEG2000")69         sIdsToEncodingNames.put(JPEG2000, "JPEG2000");
sIdsToEncodingNames.put(BMP, "BMP")70         sIdsToEncodingNames.put(BMP, "BMP");
sIdsToEncodingNames.put(UNKNOWN, "UNKNOWN")71         sIdsToEncodingNames.put(UNKNOWN, "UNKNOWN");
72     }
73 
74     /**
75      * The integer ID of the type that this encoding is
76      */
77     private final int mType;
78 
79     /**
80      * If an encoding is type USR_XXX then it has an extension that defines the encoding
81      */
82     private final String mProprietaryEncodingId;
83 
84     /**
85      * Create an encoding object based on a AVRCP specification defined string of the encoding name
86      *
87      * @param encoding The encoding name
88      */
BipEncoding(String encoding)89     public BipEncoding(String encoding) {
90         if (encoding == null) {
91             throw new ParseException("Encoding input invalid");
92         }
93         encoding = encoding.trim();
94         mType = determineEncoding(encoding.toUpperCase());
95 
96         String proprietaryEncodingId = null;
97         if (mType == USR_XXX) {
98             proprietaryEncodingId = encoding.substring(4).toUpperCase();
99         }
100         mProprietaryEncodingId = proprietaryEncodingId;
101 
102         // If we don't have a type by now, we've failed to parse the encoding
103         if (mType == UNKNOWN) {
104             throw new ParseException("Failed to determine type of '" + encoding + "'");
105         }
106     }
107 
108     /**
109      * Create an encoding object based on one of the constants for the available formats
110      *
111      * @param encoding A constant representing an available encoding
112      * @param proprietaryId A string representing the Id of a propreitary encoding. Only used if the
113      *                      encoding type is BipEncoding.USR_XXX
114      */
BipEncoding(int encoding, String proprietaryId)115     public BipEncoding(int encoding, String proprietaryId) {
116         if (encoding < 0 || encoding > USR_XXX) {
117             throw new IllegalArgumentException("Received invalid encoding type '" + encoding + "'");
118         }
119         mType = encoding;
120 
121         String proprietaryEncodingId = null;
122         if (mType == USR_XXX) {
123             if (proprietaryId == null) {
124                 throw new IllegalArgumentException("Received invalid user defined encoding id '"
125                         + proprietaryId + "'");
126             }
127             proprietaryEncodingId = proprietaryId.toUpperCase();
128         }
129         mProprietaryEncodingId = proprietaryEncodingId;
130     }
131 
BipEncoding(int encoding)132     public BipEncoding(int encoding) {
133         this(encoding, null);
134     }
135 
136     /**
137      * Returns the encoding type
138      *
139      * @return Integer type ID of the encoding
140      */
getType()141     public int getType() {
142         return mType;
143     }
144 
145     /**
146      * Returns the ID portion of an encoding if it's a proprietary encoding
147      *
148      * @return String ID of a proprietary encoding, or null if the encoding is not proprietary
149      */
getProprietaryEncodingId()150     public String getProprietaryEncodingId() {
151         return mProprietaryEncodingId;
152     }
153 
154     /**
155      * Determines if an encoding is supported by Android's Graphics Framework
156      *
157      * Android's Bitmap/BitmapFactory can handle BMP, GIF, JPEG, PNG, WebP, and HEIF formats.
158      *
159      * @return True if the encoding is supported, False otherwise.
160      */
isAndroidSupported()161     public boolean isAndroidSupported() {
162         return mType == BipEncoding.JPEG || mType == BipEncoding.PNG || mType == BipEncoding.BMP
163                 || mType == BipEncoding.GIF;
164     }
165 
166     /**
167      * Determine the encoding type based on an input string
168      */
determineEncoding(String encoding)169     private static int determineEncoding(String encoding) {
170         Integer type = (Integer) sEncodingNamesToIds.get(encoding);
171         if (type != null) return type.intValue();
172         if (encoding != null && encoding.length() >= 4 && encoding.substring(0, 4).equals("USR-")) {
173             return USR_XXX;
174         }
175         return UNKNOWN;
176     }
177 
178     @Override
equals(Object o)179     public boolean equals(Object o) {
180         if (o == this) return true;
181         if (!(o instanceof BipEncoding)) return false;
182 
183         BipEncoding e = (BipEncoding) o;
184         return e.getType() == getType()
185                 && e.getProprietaryEncodingId() == getProprietaryEncodingId();
186     }
187 
188     @Override
toString()189     public String toString() {
190         if (mType == USR_XXX) return "USR-" + mProprietaryEncodingId;
191         String encoding = (String) sIdsToEncodingNames.get(mType);
192         return encoding;
193     }
194 }
195