• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 package com.android.tv.twopanelsettings.slices
17 
18 import android.content.Context
19 import android.content.ContextWrapper
20 import android.os.Bundle
21 import android.util.AttributeSet
22 import android.util.Log
23 import android.view.ContextThemeWrapper
24 import androidx.preference.Preference
25 import java.lang.reflect.Constructor
26 import java.lang.reflect.Array
27 import java.lang.reflect.Field
28 import java.lang.reflect.Method
29 import java.util.Locale
30 
31 class NonSlicePreferenceBuilder private constructor(className: String) {
32     private val cls : Class<*> = Class.forName(className)
33     private val factory: Constructor<*>
34     private val setters: MutableMap<String, MutableList<Method>> = mutableMapOf()
35 
<lambda>null36     init {
37         if (!Preference::class.java.isAssignableFrom(cls)) {
38             throw IllegalArgumentException("Not a preference")
39         }
40 
41         factory = try {
42             cls.getConstructor(Context::class.java)
43         } catch (e: Exception) {
44             cls.getConstructor(Context::class.java, AttributeSet::class.java)
45         }
46     }
47 
48 
createnull49     fun create(context: Context, bundle: Bundle?) : Preference {
50         synchronized(this) {
51             return createInternal(context, bundle)
52         }
53     }
54 
55     @Suppress("DEPRECATION") // Types can not be determined statically.
createInternalnull56     private fun createInternal(context: Context, bundle: Bundle?): Preference {
57         val preference: Preference =
58             (if (factory.parameters.size == 1)
59                 factory.newInstance(context) else factory.newInstance(context, null)) as Preference
60         bundle ?: return preference
61 
62         properties@ for (property in bundle.keySet()) {
63             val value = bundle[property]!!
64             val setterList = setters[property] ?: mutableListOf()
65             for (setter in setterList) {
66                 try {
67                     setter.invoke(preference, value)
68                     continue@properties;
69                 } catch (_: Exception) {
70                 }
71             }
72 
73             val setterName = "set" + property.substring(0..<1).uppercase(Locale.US) +
74                     property.substring(1)
75             val setter = findSetter(setterName, value::class.java)
76             if (setter != null) {
77                 setter.invoke(preference, value)
78                 setterList += setter
79                 setters[property] = setterList
80             } else {
81                 Log.e(
82                     TAG,
83                     "Can't find $setterName in ${cls.name} of type ${value::class.java.name}"
84                 );
85             }
86         }
87 
88         return preference
89     }
90 
findSetternull91     private fun findSetter(name: String, type: Class<*>) : Method? {
92         try {
93             return cls.getMethod(name, type)
94         } catch (_: Exception) {}
95 
96         if (type.isArray) {
97             val componentType = type.componentType
98             val baseSuperClass = componentType.superclass
99             if (baseSuperClass != null) {
100                 findSetter(name, Array.newInstance(baseSuperClass, 0)::class.java)?.let { return it }
101             }
102 
103             for (baseInterface in componentType.interfaces) {
104                 findSetter(name, Array.newInstance(baseInterface, 0)::class.java)?.let { return it }
105             }
106 
107             return null
108         }
109 
110         try {
111             val primitiveField = type.getField("TYPE")
112             val primitiveType = primitiveField.get(null)
113             if (primitiveType is Class<*>) {
114                 findSetter(name, primitiveType)?.let { return it }
115             }
116         } catch(_: Exception) {}
117 
118         val superclass = type.superclass
119         if (superclass != null) {
120             findSetter(name, superclass)?.let { return it }
121         }
122 
123         for (iface in type.interfaces) {
124             findSetter(name, iface)?.let { return it }
125         }
126 
127         return null
128     }
129 
130     companion object {
131         private const val TAG = "NonSlicePreferenceBld"
132 
133         private val builders: MutableMap<String, NonSlicePreferenceBuilder> = mutableMapOf()
134 
forClassNamenull135         fun forClassName(className: String): NonSlicePreferenceBuilder {
136             synchronized(builders) {
137                 var builder = builders[className]
138                 if (builder == null) {
139                     builder = NonSlicePreferenceBuilder(className)
140                     builders[className] = builder
141                 }
142                 return builder
143             }
144         }
145     }
146 }
147