@@ -12,6 +12,7 @@ class ApiKeyManager(context: Context) {
1212 private val PREFS_NAME = " api_key_prefs"
1313 private val API_KEYS = " api_keys"
1414 private val CURRENT_KEY_INDEX = " current_key_index"
15+ private val FAILED_KEYS = " failed_keys"
1516
1617 private val prefs: SharedPreferences = context.getSharedPreferences(PREFS_NAME , Context .MODE_PRIVATE )
1718
@@ -73,6 +74,9 @@ class ApiKeyManager(context: Context) {
7374 setCurrentKeyIndex(0 )
7475 }
7576
77+ // Clear this key from failed keys if it was previously marked as failed
78+ removeFailedKey(apiKey)
79+
7680 Log .d(TAG , " Added new API key, total keys: ${keys.size} " )
7781 return true
7882 }
@@ -95,6 +99,9 @@ class ApiKeyManager(context: Context) {
9599 setCurrentKeyIndex(0 )
96100 }
97101
102+ // Also remove from failed keys if present
103+ removeFailedKey(apiKey)
104+
98105 Log .d(TAG , " Removed API key, remaining keys: ${keys.size} " )
99106 } else {
100107 Log .d(TAG , " API key not found for removal" )
@@ -128,6 +135,122 @@ class ApiKeyManager(context: Context) {
128135 return prefs.getInt(CURRENT_KEY_INDEX , 0 )
129136 }
130137
138+ /* *
139+ * Mark an API key as failed (e.g., due to 503 error)
140+ * @param apiKey The API key to mark as failed
141+ */
142+ fun markKeyAsFailed (apiKey : String ) {
143+ val failedKeys = getFailedKeys().toMutableList()
144+ if (! failedKeys.contains(apiKey)) {
145+ failedKeys.add(apiKey)
146+ saveFailedKeys(failedKeys)
147+ Log .d(TAG , " Marked API key as failed: ${apiKey.take(5 )} ..." )
148+ }
149+ }
150+
151+ /* *
152+ * Remove an API key from the failed keys list
153+ * @param apiKey The API key to remove from failed keys
154+ */
155+ fun removeFailedKey (apiKey : String ) {
156+ val failedKeys = getFailedKeys().toMutableList()
157+ if (failedKeys.remove(apiKey)) {
158+ saveFailedKeys(failedKeys)
159+ Log .d(TAG , " Removed API key from failed keys: ${apiKey.take(5 )} ..." )
160+ }
161+ }
162+
163+ /* *
164+ * Get all failed API keys
165+ * @return List of failed API keys
166+ */
167+ fun getFailedKeys (): List <String > {
168+ val keysString = prefs.getString(FAILED_KEYS , " " ) ? : " "
169+ return if (keysString.isEmpty()) {
170+ emptyList()
171+ } else {
172+ keysString.split(" ," )
173+ }
174+ }
175+
176+ /* *
177+ * Check if an API key is marked as failed
178+ * @param apiKey The API key to check
179+ * @return True if the key is marked as failed, false otherwise
180+ */
181+ fun isKeyFailed (apiKey : String ): Boolean {
182+ return getFailedKeys().contains(apiKey)
183+ }
184+
185+ /* *
186+ * Reset all failed keys
187+ */
188+ fun resetFailedKeys () {
189+ prefs.edit().remove(FAILED_KEYS ).apply ()
190+ Log .d(TAG , " Reset all failed keys" )
191+ }
192+
193+ /* *
194+ * Check if all API keys are marked as failed
195+ * @return True if all keys are failed, false otherwise
196+ */
197+ fun areAllKeysFailed (): Boolean {
198+ val keys = getApiKeys()
199+ val failedKeys = getFailedKeys()
200+ return keys.isNotEmpty() && failedKeys.size >= keys.size
201+ }
202+
203+ /* *
204+ * Get the count of available API keys
205+ * @return The number of API keys
206+ */
207+ fun getKeyCount (): Int {
208+ return getApiKeys().size
209+ }
210+
211+ /* *
212+ * Switch to the next available API key that is not marked as failed
213+ * @return The new API key or null if no valid keys are available
214+ */
215+ fun switchToNextAvailableKey (): String? {
216+ val keys = getApiKeys()
217+ if (keys.isEmpty()) {
218+ Log .d(TAG , " No API keys available to switch to" )
219+ return null
220+ }
221+
222+ val failedKeys = getFailedKeys()
223+ val currentIndex = getCurrentKeyIndex()
224+
225+ // If all keys are failed, reset failed keys and start from the beginning
226+ if (failedKeys.size >= keys.size) {
227+ Log .d(TAG , " All keys are marked as failed, resetting failed keys" )
228+ resetFailedKeys()
229+ setCurrentKeyIndex(0 )
230+ return keys[0 ]
231+ }
232+
233+ // Find the next key that is not failed
234+ var nextIndex = (currentIndex + 1 ) % keys.size
235+ var attempts = 0
236+
237+ while (attempts < keys.size) {
238+ if (! failedKeys.contains(keys[nextIndex])) {
239+ setCurrentKeyIndex(nextIndex)
240+ Log .d(TAG , " Switched to next available key at index $nextIndex " )
241+ return keys[nextIndex]
242+ }
243+ nextIndex = (nextIndex + 1 ) % keys.size
244+ attempts++
245+ }
246+
247+ // If we get here, all keys are failed (shouldn't happen due to earlier check)
248+ Log .d(TAG , " Could not find a non-failed key, resetting failed keys" )
249+ resetFailedKeys()
250+ setCurrentKeyIndex(0 )
251+ return keys[0 ]
252+ }
253+
131254 /* *
132255 * Save the list of API keys to SharedPreferences
133256 * @param keys The list of API keys to save
@@ -137,11 +260,20 @@ class ApiKeyManager(context: Context) {
137260 prefs.edit().putString(API_KEYS , keysString).apply ()
138261 }
139262
263+ /* *
264+ * Save the list of failed API keys to SharedPreferences
265+ * @param keys The list of failed API keys to save
266+ */
267+ private fun saveFailedKeys (keys : List <String >) {
268+ val keysString = keys.joinToString(" ," )
269+ prefs.edit().putString(FAILED_KEYS , keysString).apply ()
270+ }
271+
140272 /* *
141273 * Clear all stored API keys
142274 */
143275 fun clearAllKeys () {
144- prefs.edit().remove(API_KEYS ).remove(CURRENT_KEY_INDEX ).apply ()
276+ prefs.edit().remove(API_KEYS ).remove(CURRENT_KEY_INDEX ).remove( FAILED_KEYS ). apply ()
145277 Log .d(TAG , " Cleared all API keys" )
146278 }
147279
0 commit comments