-
-
Notifications
You must be signed in to change notification settings - Fork 427
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
364 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
...ndroid-core/src/main/java/io/sentry/android/core/internal/util/FirstDrawDoneListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package io.sentry.android.core.internal.util; | ||
|
||
import android.annotation.SuppressLint; | ||
import android.os.Build; | ||
import android.os.Handler; | ||
import android.os.Looper; | ||
import android.view.View; | ||
import android.view.ViewTreeObserver; | ||
import androidx.annotation.RequiresApi; | ||
import io.sentry.android.core.BuildInfoProvider; | ||
import java.util.concurrent.atomic.AtomicReference; | ||
import org.jetbrains.annotations.NotNull; | ||
|
||
/** | ||
* OnDrawListener that unregisters itself and invokes callback when the next draw is done. This API | ||
* 16+ implementation is an approximation of the initial-display-time defined by Android Vitals. | ||
*/ | ||
@SuppressLint("ObsoleteSdkInt") | ||
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) | ||
public class FirstDrawDoneListener implements ViewTreeObserver.OnDrawListener { | ||
private final @NotNull Handler mainThreadHandler = new Handler(Looper.getMainLooper()); | ||
private final @NotNull AtomicReference<View> viewReference; | ||
private final @NotNull Runnable callback; | ||
|
||
/** Registers a post-draw callback for the next draw of a view. */ | ||
public static void registerForNextDraw( | ||
final @NotNull View view, | ||
final @NotNull Runnable drawDoneCallback, | ||
final @NotNull BuildInfoProvider buildInfoProvider) { | ||
final FirstDrawDoneListener listener = new FirstDrawDoneListener(view, drawDoneCallback); | ||
// Handle bug prior to API 26 where OnDrawListener from the floating ViewTreeObserver is not | ||
// merged into the real ViewTreeObserver. | ||
// https://android.googlesource.com/platform/frameworks/base/+/9f8ec54244a5e0343b9748db3329733f259604f3 | ||
if (buildInfoProvider.getSdkInfoVersion() < 26 | ||
&& !isAliveAndAttached(view, buildInfoProvider)) { | ||
view.addOnAttachStateChangeListener( | ||
new View.OnAttachStateChangeListener() { | ||
@Override | ||
public void onViewAttachedToWindow(View view) { | ||
view.getViewTreeObserver().addOnDrawListener(listener); | ||
view.removeOnAttachStateChangeListener(this); | ||
} | ||
|
||
@Override | ||
public void onViewDetachedFromWindow(View view) { | ||
view.removeOnAttachStateChangeListener(this); | ||
} | ||
}); | ||
} else { | ||
view.getViewTreeObserver().addOnDrawListener(listener); | ||
} | ||
} | ||
|
||
private FirstDrawDoneListener(final @NotNull View view, final @NotNull Runnable callback) { | ||
this.viewReference = new AtomicReference<>(view); | ||
this.callback = callback; | ||
} | ||
|
||
@Override | ||
public void onDraw() { | ||
// Set viewReference to null so any onDraw past the first is a no-op | ||
final View view = viewReference.getAndSet(null); | ||
if (view == null) { | ||
return; | ||
} | ||
// OnDrawListeners cannot be removed within onDraw, so we remove it with a | ||
// GlobalLayoutListener | ||
view.getViewTreeObserver() | ||
.addOnGlobalLayoutListener(() -> view.getViewTreeObserver().removeOnDrawListener(this)); | ||
mainThreadHandler.postAtFrontOfQueue(callback); | ||
} | ||
|
||
/** | ||
* Helper to avoid <a | ||
* href="https://android.googlesource.com/platform/frameworks/base/+/9f8ec54244a5e0343b9748db3329733f259604f3">bug | ||
* prior to API 26</a>, where the floating ViewTreeObserver's OnDrawListeners are not merged into | ||
* the real ViewTreeObserver during attach. | ||
* | ||
* @return true if the View is already attached and the ViewTreeObserver is not a floating | ||
* placeholder. | ||
*/ | ||
private static boolean isAliveAndAttached( | ||
final @NotNull View view, final @NotNull BuildInfoProvider buildInfoProvider) { | ||
return view.getViewTreeObserver().isAlive() && isAttachedToWindow(view, buildInfoProvider); | ||
} | ||
|
||
@SuppressLint("NewApi") | ||
private static boolean isAttachedToWindow( | ||
final @NotNull View view, final @NotNull BuildInfoProvider buildInfoProvider) { | ||
if (buildInfoProvider.getSdkInfoVersion() >= 19) { | ||
return view.isAttachedToWindow(); | ||
} | ||
return view.getWindowToken() != null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.