blob: c5021460da89b901a715a9d7e58924cd71126034 [file] [log] [blame]
// 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;
@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 final FlutterJNI.AsyncWaitForVsyncDelegate asyncWaitForVsyncDelegate =
new FlutterJNI.AsyncWaitForVsyncDelegate() {
@Override
public void asyncWaitForVsync(long cookie) {
Choreographer.getInstance()
.postFrameCallback(
new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
long delay = System.nanoTime() - frameTimeNanos;
if (delay < 0) {
delay = 0;
}
flutterJNI.onVsync(delay, refreshPeriodNanos, cookie);
}
});
}
};
private VsyncWaiter(@NonNull FlutterJNI flutterJNI) {
this.flutterJNI = flutterJNI;
}
public void init() {
flutterJNI.setAsyncWaitForVsyncDelegate(asyncWaitForVsyncDelegate);
}
}