SpanUtil.java

/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.media3.common.text;

import android.text.Spannable;
import android.text.style.ForegroundColorSpan;
import androidx.media3.common.util.UnstableApi;

/**
 * Utility methods for Android <a href="https://developer.android.com/guide/topics/text/spans">span
 * styling</a>.
 */
@UnstableApi
public final class SpanUtil {

  /**
   * Adds {@code span} to {@code spannable} between {@code start} and {@code end}, removing any
   * existing spans of the same type and with the same indices and flags.
   *
   * <p>This is useful for types of spans that don't make sense to duplicate and where the
   * evaluation order might have an unexpected impact on the final text, e.g. {@link
   * ForegroundColorSpan}.
   *
   * @param spannable The {@link Spannable} to add {@code span} to.
   * @param span The span object to be added.
   * @param start The start index to add the new span at.
   * @param end The end index to add the new span at.
   * @param spanFlags The flags to pass to {@link Spannable#setSpan(Object, int, int, int)}.
   */
  public static void addOrReplaceSpan(
      Spannable spannable, Object span, int start, int end, int spanFlags) {
    Object[] existingSpans = spannable.getSpans(start, end, span.getClass());
    for (Object existingSpan : existingSpans) {
      if (spannable.getSpanStart(existingSpan) == start
          && spannable.getSpanEnd(existingSpan) == end
          && spannable.getSpanFlags(existingSpan) == spanFlags) {
        spannable.removeSpan(existingSpan);
      }
    }
    spannable.setSpan(span, start, end, spanFlags);
  }

  private SpanUtil() {}
}