{"record":{"id":"bc9277706cdc835f","repo":"material-components/material-components-android","slug":"couldn-t-set-rippledrawable-radius","errorCode":null,"errorMessage":"Couldn't set RippleDrawable radius","messagePattern":"Couldn't set RippleDrawable radius","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"lib/java/com/google/android/material/drawable/DrawableUtils.java","lineNumber":162,"sourceCode":"    } catch (XmlPullParserException | IOException e) {\n      NotFoundException exception =\n          new NotFoundException(\"Can't load badge resource ID #0x\" + Integer.toHexString(id));\n      exception.initCause(e);\n      throw exception;\n    }\n  }\n\n  public static void setRippleDrawableRadius(@Nullable RippleDrawable drawable, int radius) {\n    if (VERSION.SDK_INT >= VERSION_CODES.M) {\n      drawable.setRadius(radius);\n    } else {\n      try {\n        @SuppressLint(\"PrivateApi\")\n        Method setMaxRadiusMethod =\n            RippleDrawable.class.getDeclaredMethod(\"setMaxRadius\", int.class);\n        setMaxRadiusMethod.invoke(drawable, radius);\n      } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {\n        throw new IllegalStateException(\"Couldn't set RippleDrawable radius\", e);\n      }\n    }\n  }\n\n  /**\n   * Wraps and mutates the passed in drawable so that it may be used for tinting if a tintList is\n   * present. Also applies the tintMode if present.\n   */\n  @Nullable\n  public static Drawable createTintableDrawableIfNeeded(\n      @Nullable Drawable drawable, @Nullable ColorStateList tintList, @Nullable Mode tintMode) {\n    return createTintableMutatedDrawableIfNeeded(\n        drawable, tintList, tintMode, /* forceMutate= */ false);\n  }\n\n  /**\n   * Wraps and mutates the passed in drawable so that it may be used for tinting if a tintList is\n   * present. Also applies the tintMode if present. If there's not a tintList and the API level is <","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/material-components/material-components-android/blob/ac7e18efeefb331850c561faf9ab8bf81d27ba68/lib/java/com/google/android/material/drawable/DrawableUtils.java#L144-L180","documentation":"On API levels below 23, RippleDrawable.setRadius() does not exist, so DrawableUtils.setRippleDrawableRadius falls back to reflection, invoking the hidden setMaxRadius(int) method. If that hidden method is missing (OEM framework changes) or inaccessible — most commonly because ProGuard/R8 obfuscated or stripped it, or the method was renamed in a vendor fork — the reflective call fails and the IllegalStateException wraps the underlying NoSuchMethodException / IllegalAccessException / InvocationTargetException.","triggerScenarios":"Running on an API < 23 device with a minified release build where RippleDrawable.setMaxRadius is removed by ProGuard/R8; OEM-modified framework classes without the hidden method; calling setRippleDrawableRadius directly (e.g. when sizing FAB ripples) on pre-M devices.","commonSituations":"Works in debug, crashes only in minified release on old devices; shipping an app with minSdk < 23 that customizes ripple radius via Material components (e.g. FloatingActionButton ripple sizing).","solutions":["Add ProGuard keep rules so reflection survives minification: -keepclassmembers class android.graphics.drawable.RippleDrawable { void setMaxRadius(int); }","Raise minSdk to 23+ so the public setRadius() path is used and reflection is avoided entirely.","Guard the call: only invoke setRippleDrawableRadius when Build.VERSION.SDK_INT >= 23, skipping custom radius on older devices."],"exampleFix":"// before\nDrawableUtils.setRippleDrawableRadius(ripple, radiusPx); // crashes when minified on API<23\n\n// after\nif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n  DrawableUtils.setRippleDrawableRadius(ripple, radiusPx);\n}\n// proguard-rules.pro\n// -keepclassmembers class android.graphics.drawable.RippleDrawable { void setMaxRadius(int); }","handlingStrategy":"try-catch","validationCode":"if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n  DrawableUtils.setRippleDrawableRadius(drawable, radius);\n} // else: skip custom radius on pre-M","typeGuard":null,"tryCatchPattern":"try {\n  DrawableUtils.setRippleDrawableRadius(drawable, radius);\n} catch (IllegalStateException e) {\n  // reflection failed on pre-M; fall back to default ripple radius, do not crash UI\n  Log.w(TAG, \"Custom ripple radius unsupported on this device\", e);\n}","preventionTips":["Keep -keepclassmembers rules for RippleDrawable.setMaxRadius in proguard-rules.pro.","Test minified release builds on (or emulated) API < 23 devices.","Consider raising minSdkVersion to 23 to remove the reflection path entirely."],"tags":["material-components","ripple","reflection","proguard","api-level"],"backgroundTag":null,"analyzedSha":"ac7e18efeefb331850c561faf9ab8bf81d27ba68","analyzedAt":"2026-08-14T15:35:35.735Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}