| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| package io.flutter.view; | |
| import android.annotation.TargetApi; | |
| import android.hardware.display.DisplayManager; | |
| import android.view.Choreographer; | |
| import android.view.Display; | |
| import androidx.annotation.NonNull; | |
| import androidx.annotation.VisibleForTesting; | |
| import io.flutter.embedding.engine.FlutterJNI; | |
| // TODO(mattcarroll): add javadoc. | |
| public class VsyncWaiter { | |
| @TargetApi(17) | |
| class DisplayListener implements DisplayManager.DisplayListener { | |
| DisplayListener(DisplayManager displayManager) { | |
| this.displayManager = displayManager; | |
| } | |
| private DisplayManager displayManager; | |
| void register() { | |
| displayManager.registerDisplayListener(this, null); | |
| } | |
| @Override | |
| public void onDisplayAdded(int displayId) {} | |
| @Override | |
| public void onDisplayRemoved(int displayId) {} | |
| @Override | |
| public void onDisplayChanged(int displayId) { | |
| if (displayId == Display.DEFAULT_DISPLAY) { | |
| final Display primaryDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY); | |
| float fps = primaryDisplay.getRefreshRate(); | |
| VsyncWaiter.this.refreshPeriodNanos = (long) (1000000000.0 / fps); | |
| VsyncWaiter.this.flutterJNI.setRefreshRateFPS(fps); | |
| } | |
| } | |
| } | |
| private static VsyncWaiter instance; | |
| private static DisplayListener listener; | |
| private long refreshPeriodNanos = -1; | |
| private FlutterJNI flutterJNI; | |
| private FrameCallback frameCallback = new FrameCallback(0); | |
| @NonNull | |
| public static VsyncWaiter getInstance(float fps, @NonNull FlutterJNI flutterJNI) { | |
| if (instance == null) { | |
| instance = new VsyncWaiter(flutterJNI); | |
| } | |
| flutterJNI.setRefreshRateFPS(fps); | |
| instance.refreshPeriodNanos = (long) (1000000000.0 / fps); | |
| return instance; | |
| } | |
| @TargetApi(17) | |
| @NonNull | |
| public static VsyncWaiter getInstance( | |
| @NonNull DisplayManager displayManager, @NonNull FlutterJNI flutterJNI) { | |
| if (instance == null) { | |
| instance = new VsyncWaiter(flutterJNI); | |
| } | |
| if (listener == null) { | |
| listener = instance.new DisplayListener(displayManager); | |
| listener.register(); | |
| } | |
| if (instance.refreshPeriodNanos == -1) { | |
| final Display primaryDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY); | |
| float fps = primaryDisplay.getRefreshRate(); | |
| instance.refreshPeriodNanos = (long) (1000000000.0 / fps); | |
| flutterJNI.setRefreshRateFPS(fps); | |
| } | |
| return instance; | |
| } | |
| // For tests, to reset the singleton between tests. | |
| @VisibleForTesting | |
| public static void reset() { | |
| instance = null; | |
| listener = null; | |
| } | |
| private class FrameCallback implements Choreographer.FrameCallback { | |
| private long cookie; | |
| FrameCallback(long cookie) { | |
| this.cookie = cookie; | |
| } | |
| @Override | |
| public void doFrame(long frameTimeNanos) { | |
| long delay = System.nanoTime() - frameTimeNanos; | |
| if (delay < 0) { | |
| delay = 0; | |
| } | |
| flutterJNI.onVsync(delay, refreshPeriodNanos, cookie); | |
| frameCallback = this; | |
| } | |
| } | |
| private final FlutterJNI.AsyncWaitForVsyncDelegate asyncWaitForVsyncDelegate = | |
| new FlutterJNI.AsyncWaitForVsyncDelegate() { | |
| private Choreographer.FrameCallback obtainFrameCallback(final long cookie) { | |
| if (frameCallback != null) { | |
| frameCallback.cookie = cookie; | |
| FrameCallback ret = frameCallback; | |
| frameCallback = null; | |
| return ret; | |
| } | |
| return new FrameCallback(cookie); | |
| } | |
| @Override | |
| public void asyncWaitForVsync(long cookie) { | |
| Choreographer.getInstance().postFrameCallback(obtainFrameCallback(cookie)); | |
| } | |
| }; | |
| private VsyncWaiter(@NonNull FlutterJNI flutterJNI) { | |
| this.flutterJNI = flutterJNI; | |
| } | |
| public void init() { | |
| flutterJNI.setAsyncWaitForVsyncDelegate(asyncWaitForVsyncDelegate); | |
| } | |
| } |