Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Remove unnecessary camera callbacks loops #12973

Merged
merged 2 commits into from
Sep 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
package com.mapbox.mapboxsdk.maps;

import android.os.Handler;
import android.os.Message;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;

import java.lang.annotation.Retention;
import java.lang.ref.WeakReference;
import java.util.concurrent.CopyOnWriteArrayList;

import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraIdleListener;
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveCanceledListener;
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveListener;
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener;
import static java.lang.annotation.RetentionPolicy.SOURCE;

/**
* Class responsible for dispatching camera change events to registered listeners.
*/
class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, MapboxMap.OnCameraMoveListener,
MapboxMap.OnCameraMoveCanceledListener, OnCameraIdleListener {

private final Handler handler = new Handler();
private final CameraChangeHandler handler = new CameraChangeHandler(this);

private boolean idle = true;
private int moveStartedReason;
Expand All @@ -31,83 +36,36 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M
private OnCameraMoveListener onCameraMoveListener;
private OnCameraIdleListener onCameraIdleListener;

private final Runnable onCameraMoveStartedRunnable = new Runnable() {
@Override
public void run() {
if (!idle) {
return;
}
idle = false;

// deprecated API
if (onCameraMoveStartedListener != null) {
onCameraMoveStartedListener.onCameraMoveStarted(moveStartedReason);
}

// new API
if (!onCameraMoveStarted.isEmpty()) {
for (OnCameraMoveStartedListener cameraMoveStartedListener : onCameraMoveStarted) {
cameraMoveStartedListener.onCameraMoveStarted(moveStartedReason);
}
}
}
};

private final Runnable onCameraMoveRunnable = new Runnable() {
@Override
public void run() {
// deprecated API
if (onCameraMoveListener != null && !idle) {
onCameraMoveListener.onCameraMove();
}

// new API
if (!onCameraMove.isEmpty() && !idle) {
for (OnCameraMoveListener cameraMoveListener : onCameraMove) {
cameraMoveListener.onCameraMove();
}
}
}
};
@Retention(SOURCE)
@IntDef( {MOVE_STARTED, MOVE, MOVE_CANCELED, IDLE})
@interface CameraChange {
}

private final Runnable onCameraMoveCancelRunnable = new Runnable() {
@Override
public void run() {
// deprecated API
if (onCameraMoveCanceledListener != null && !idle) {
onCameraMoveCanceledListener.onCameraMoveCanceled();
}
private static final int MOVE_STARTED = 0;
private static final int MOVE = 1;
private static final int MOVE_CANCELED = 2;
private static final int IDLE = 3;

// new API
if (!onCameraMoveCanceled.isEmpty() && !idle) {
for (OnCameraMoveCanceledListener cameraMoveCanceledListener : onCameraMoveCanceled) {
cameraMoveCanceledListener.onCameraMoveCanceled();
}
}
}
};
@Override
public void onCameraMoveStarted(final int reason) {
moveStartedReason = reason;
handler.scheduleMessage(MOVE_STARTED);
}

private final Runnable onCameraIdleRunnable = new Runnable() {
@Override
public void run() {
if (idle) {
return;
}
idle = true;
@Override
public void onCameraMove() {
handler.scheduleMessage(MOVE);
}

// deprecated API
if (onCameraIdleListener != null) {
onCameraIdleListener.onCameraIdle();
}
@Override
public void onCameraMoveCanceled() {
handler.scheduleMessage(MOVE_CANCELED);
}

// new API
if (!onCameraIdle.isEmpty()) {
for (OnCameraIdleListener cameraIdleListener : onCameraIdle) {
cameraIdleListener.onCameraIdle();
}
}
}
};
@Override
public void onCameraIdle() {
handler.scheduleMessage(IDLE);
}

@Deprecated
void setOnCameraMoveStartedListener(OnCameraMoveStartedListener onCameraMoveStartedListener) {
Expand All @@ -129,27 +87,6 @@ void setOnCameraIdleListener(OnCameraIdleListener onCameraIdleListener) {
this.onCameraIdleListener = onCameraIdleListener;
}

@Override
public void onCameraMoveStarted(final int reason) {
moveStartedReason = reason;
handler.post(onCameraMoveStartedRunnable);
}

@Override
public void onCameraMove() {
handler.post(onCameraMoveRunnable);
}

@Override
public void onCameraMoveCanceled() {
handler.post(onCameraMoveCancelRunnable);
}

@Override
public void onCameraIdle() {
handler.post(onCameraIdleRunnable);
}

void addOnCameraIdleListener(@NonNull OnCameraIdleListener listener) {
onCameraIdle.add(listener);
}
Expand Down Expand Up @@ -189,4 +126,122 @@ void removeOnCameraMoveListener(OnCameraMoveListener listener) {
onCameraMove.remove(listener);
}
}

private void executeOnCameraMoveStarted() {
if (!idle) {
return;
}
idle = false;

// deprecated API
if (onCameraMoveStartedListener != null) {
onCameraMoveStartedListener.onCameraMoveStarted(moveStartedReason);
}

// new API
if (!onCameraMoveStarted.isEmpty()) {
for (OnCameraMoveStartedListener cameraMoveStartedListener : onCameraMoveStarted) {
cameraMoveStartedListener.onCameraMoveStarted(moveStartedReason);
}
}
}

private void executeOnCameraMove() {
// deprecated API
if (onCameraMoveListener != null && !idle) {
onCameraMoveListener.onCameraMove();
}

// new API
if (!onCameraMove.isEmpty() && !idle) {
for (OnCameraMoveListener cameraMoveListener : onCameraMove) {
cameraMoveListener.onCameraMove();
}
}
}

private void executeOnCameraMoveCancelled() {
// deprecated API
if (onCameraMoveCanceledListener != null && !idle) {
onCameraMoveCanceledListener.onCameraMoveCanceled();
}

// new API
if (!onCameraMoveCanceled.isEmpty() && !idle) {
for (OnCameraMoveCanceledListener cameraMoveCanceledListener : onCameraMoveCanceled) {
cameraMoveCanceledListener.onCameraMoveCanceled();
}
}
}

private void executeOnCameraIdle() {
if (idle) {
return;
}
idle = true;

// deprecated API
if (onCameraIdleListener != null) {
onCameraIdleListener.onCameraIdle();
}

// new API
if (!onCameraIdle.isEmpty()) {
for (OnCameraIdleListener cameraIdleListener : onCameraIdle) {
cameraIdleListener.onCameraIdle();
}
}
}

private static class CameraChangeHandler extends Handler {

private WeakReference<CameraChangeDispatcher> dispatcherWeakReference;

CameraChangeHandler(CameraChangeDispatcher dispatcher) {
super();
this.dispatcherWeakReference = new WeakReference<>(dispatcher);
}

@Override
public void handleMessage(Message msg) {
CameraChangeDispatcher dispatcher = dispatcherWeakReference.get();
if (dispatcher != null) {
switch (msg.what) {
case MOVE_STARTED:
dispatcher.executeOnCameraMoveStarted();
break;
case MOVE:
dispatcher.executeOnCameraMove();
break;
case MOVE_CANCELED:
dispatcher.executeOnCameraMoveCancelled();
break;
case IDLE:
dispatcher.executeOnCameraIdle();
break;
}
}
}

void scheduleMessage(@CameraChange int change) {
CameraChangeDispatcher dispatcher = dispatcherWeakReference.get();
if (dispatcher != null) {
// if there is a movement that is cancelled/stopped and restarted in the same code block
// we can safely assume that the movement will continue, no need for dispatching unnecessary callbacks sequence
if (change == MOVE_STARTED) {
boolean shouldReturn = !dispatcher.idle && (hasMessages(IDLE) || hasMessages(MOVE_CANCELED));
removeMessages(IDLE);
removeMessages(MOVE_CANCELED);

if (shouldReturn) {
return;
}
}

Message message = new Message();
message.what = change;
this.sendMessage(message);
}
}
}
}
Loading