1/*
2 * Copyright (C) 2018 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
17package androidx.core.content.res
18
19import android.content.res.ColorStateList
20import android.content.res.TypedArray
21import android.graphics.Typeface
22import android.graphics.drawable.Drawable
23import androidx.annotation.AnyRes
24import androidx.annotation.ColorInt
25import androidx.annotation.Dimension
26import androidx.annotation.RequiresApi
27import androidx.annotation.StyleableRes
28
29private fun TypedArray.checkAttribute(@StyleableRes index: Int) {
30    if (!hasValue(index)) {
31        throw IllegalArgumentException("Attribute not defined in set.")
32    }
33}
34
35/**
36 * Retrieve the boolean value for the attribute at [index] or throws [IllegalArgumentException]
37 * if not defined.
38 *
39 * @see TypedArray.hasValue
40 * @see TypedArray.getBoolean
41 */
42fun TypedArray.getBooleanOrThrow(@StyleableRes index: Int): Boolean {
43    checkAttribute(index)
44    return getBoolean(index, false)
45}
46
47/**
48 * Retrieve the color value for the attribute at [index] or throws [IllegalArgumentException]
49 * if not defined.
50 *
51 * @see TypedArray.hasValue
52 * @see TypedArray.getColor
53 */
54@ColorInt
55fun TypedArray.getColorOrThrow(@StyleableRes index: Int): Int {
56    checkAttribute(index)
57    return getColor(index, 0)
58}
59
60/**
61 * Retrieve the color state list value for the attribute at [index] or throws
62 * [IllegalArgumentException] if not defined.
63 *
64 * @see TypedArray.hasValue
65 * @see TypedArray.getColorStateList
66 */
67fun TypedArray.getColorStateListOrThrow(@StyleableRes index: Int): ColorStateList {
68    checkAttribute(index)
69    return checkNotNull(getColorStateList(index)) {
70        "Attribute value was not a color or color state list."
71    }
72}
73
74/**
75 * Retrieve the dimension value for the attribute at [index] or throws [IllegalArgumentException]
76 * if not defined.
77 *
78 * @see TypedArray.hasValue
79 * @see TypedArray.getDimension
80 */
81fun TypedArray.getDimensionOrThrow(@StyleableRes index: Int): Float {
82    checkAttribute(index)
83    return getDimension(index, 0f)
84}
85
86/**
87 * Retrieve the dimension pixel offset value for the attribute at [index] or throws
88 * [IllegalArgumentException] if not defined.
89 *
90 * @see TypedArray.hasValue
91 * @see TypedArray.getDimensionPixelOffset
92 */
93@Dimension
94fun TypedArray.getDimensionPixelOffsetOrThrow(@StyleableRes index: Int): Int {
95    checkAttribute(index)
96    return getDimensionPixelOffset(index, 0)
97}
98
99/**
100 * Retrieve the dimension pixel size value for the attribute at [index] or throws
101 * [IllegalArgumentException] if not defined.
102 *
103 * @see TypedArray.hasValue
104 * @see TypedArray.getDimensionPixelSize
105 */
106@Dimension
107fun TypedArray.getDimensionPixelSizeOrThrow(@StyleableRes index: Int): Int {
108    checkAttribute(index)
109    return getDimensionPixelSize(index, 0)
110}
111
112/**
113 * Retrieve the drawable value for the attribute at [index] or throws [IllegalArgumentException]
114 * if not defined.
115 *
116 * @see TypedArray.hasValue
117 * @see TypedArray.getDrawable
118 */
119fun TypedArray.getDrawableOrThrow(@StyleableRes index: Int): Drawable {
120    checkAttribute(index)
121    return getDrawable(index)
122}
123
124/**
125 * Retrieve the float value for the attribute at [index] or throws [IllegalArgumentException]
126 * if not defined.
127 *
128 * @see TypedArray.hasValue
129 * @see TypedArray.getFloat
130 */
131fun TypedArray.getFloatOrThrow(@StyleableRes index: Int): Float {
132    checkAttribute(index)
133    return getFloat(index, 0f)
134}
135
136/**
137 * Retrieve the font value for the attribute at [index] or throws [IllegalArgumentException]
138 * if not defined.
139 *
140 * @see TypedArray.hasValue
141 * @see TypedArray.getFont
142 */
143@RequiresApi(26)
144fun TypedArray.getFontOrThrow(@StyleableRes index: Int): Typeface {
145    checkAttribute(index)
146    return getFont(index)
147}
148
149/**
150 * Retrieve the integer value for the attribute at [index] or throws [IllegalArgumentException]
151 * if not defined.
152 *
153 * @see TypedArray.hasValue
154 * @see TypedArray.getInt
155 */
156fun TypedArray.getIntOrThrow(@StyleableRes index: Int): Int {
157    checkAttribute(index)
158    return getInt(index, 0)
159}
160
161/**
162 * Retrieve the integer value for the attribute at [index] or throws [IllegalArgumentException]
163 * if not defined.
164 *
165 * @see TypedArray.hasValue
166 * @see TypedArray.getInteger
167 */
168fun TypedArray.getIntegerOrThrow(@StyleableRes index: Int): Int {
169    checkAttribute(index)
170    return getInteger(index, 0)
171}
172
173/**
174 * Retrieves the resource identifier for the attribute at [index] or throws
175 * [IllegalArgumentException] if not defined.
176 *
177 * @see TypedArray.hasValue
178 * @see TypedArray.getResourceId
179 */
180@AnyRes
181fun TypedArray.getResourceIdOrThrow(@StyleableRes index: Int): Int {
182    checkAttribute(index)
183    return getResourceId(index, 0)
184}
185
186/**
187 * Retrieve the string value for the attribute at [index] or throws [IllegalArgumentException]
188 * if not defined.
189 *
190 * @see TypedArray.hasValue
191 * @see TypedArray.getString
192 */
193fun TypedArray.getStringOrThrow(@StyleableRes index: Int): String {
194    checkAttribute(index)
195    return checkNotNull(getString(index)) {
196        "Attribute value could not be coerced to String."
197    }
198}
199
200/**
201 * Retrieve the text value for the attribute at [index] or throws [IllegalArgumentException]
202 * if not defined.
203 *
204 * @see TypedArray.hasValue
205 * @see TypedArray.getText
206 */
207fun TypedArray.getTextOrThrow(@StyleableRes index: Int): CharSequence {
208    checkAttribute(index)
209    return checkNotNull(getText(index)) {
210        "Attribute value could not be coerced to CharSequence."
211    }
212}
213
214/**
215 * Retrieve the text array value for the attribute at [index] or throws
216 * [IllegalArgumentException] if not defined.
217 *
218 * @see TypedArray.hasValue
219 * @see TypedArray.getTextArray
220 */
221fun TypedArray.getTextArrayOrThrow(@StyleableRes index: Int): Array<CharSequence> {
222    checkAttribute(index)
223    return getTextArray(index)
224}
225
226/**
227 * Executes the given [block] function on this TypedArray and then recycles it.
228 *
229 * @see kotlin.io.use
230 */
231inline fun <R> TypedArray.use(block: (TypedArray) -> R): R {
232    return block(this).also {
233        recycle()
234    }
235}
236