ClickableSpan.java
/*
* Copyright 2021 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.car.app.model;
import static java.util.Objects.requireNonNull;
import android.annotation.SuppressLint;
import android.os.Looper;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.car.app.annotations.CarProtocol;
import androidx.car.app.annotations.RequiresCarApi;
import java.util.Objects;
/**
* A span that makes a section of text clickable.
*
* <p>The text of this span will be highlighted by the host, so users understand that it is
* interactive. If this span overlaps the other spans (for example, {@link ForegroundCarColorSpan}),
* the host might choose to ignore those spans if they conflict on how clickable text is
* highlighted.
*
* <p>The host may ignore {@link ClickableSpan}s unless support for it is explicitly documented in
* the API that takes the string.
*
* <p>For example, to make a portion of a text clickable:
*
* <pre>{@code
* SpannableString string = new SpannableString("Text with a clickable span");
* string.setSpan(ClickableSpan.create(
* new OnClickListener
* ), 12, 22, Spanned.SPAN_INCLUSIVE_EXCLUSIVE));
* }</pre>
*/
@RequiresCarApi(2)
@CarProtocol
public final class ClickableSpan extends CarSpan {
@Keep
@Nullable
private final OnClickDelegate mOnClickDelegate;
/**
* Creates a {@link ClickableSpan} from a {@link OnClickListener}.
*
* <p>Note that the callback relates to UI events and will be executed on the main thread
* using {@link Looper#getMainLooper()}.
*
* @throws NullPointerException if {@code onClickListener} is {@code null}
*/
@NonNull
@SuppressLint("ExecutorRegistration")
public static ClickableSpan create(@NonNull OnClickListener onClickListener) {
return new ClickableSpan(requireNonNull(onClickListener));
}
/** Returns the {@link OnClickDelegate} associated with this span. */
@NonNull
public OnClickDelegate getOnClickDelegate() {
return requireNonNull(mOnClickDelegate);
}
@Override
@NonNull
public String toString() {
return "[clickable]";
}
@Override
public int hashCode() {
return Objects.hash(mOnClickDelegate == null);
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof ClickableSpan)) {
return false;
}
ClickableSpan otherSpan = (ClickableSpan) other;
// Don't compare callback, only ensure if it is present in one, it is also present in
// the other.
return Objects.equals(mOnClickDelegate == null, otherSpan.mOnClickDelegate == null);
}
private ClickableSpan(OnClickListener onClickListener) {
mOnClickDelegate = OnClickDelegateImpl.create(onClickListener);
}
/** Constructs an empty instance, used by serialization code. */
private ClickableSpan() {
mOnClickDelegate = null;
}
}