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