/*
* Copyright 2015 Hippo Seven
*
* 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 com.hippo.widget;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import java.lang.ref.WeakReference;
public class SnackbarManager {
private static final int MSG_TIMEOUT = 0;
private static final int SHORT_DURATION_MS = 1500;
private static final int LONG_DURATION_MS = 2750;
private static SnackbarManager sSnackbarManager;
private final Object mLock;
private final Handler mHandler;
private SnackbarRecord mCurrentSnackbar;
private SnackbarRecord mNextSnackbar;
static SnackbarManager getInstance() {
if (sSnackbarManager == null) {
sSnackbarManager = new SnackbarManager();
}
return sSnackbarManager;
}
private SnackbarManager() {
mLock = new Object();
mHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
switch (message.what) {
case MSG_TIMEOUT:
handleTimeout((SnackbarManager.SnackbarRecord)message.obj);
return true;
}
return false;
}
});
}
public void show(int duration, Callback callback) {
synchronized (mLock) {
if (isCurrentSnackbar(callback)) {
mCurrentSnackbar.duration = duration;
mHandler.removeCallbacksAndMessages(mCurrentSnackbar);
scheduleTimeoutLocked(mCurrentSnackbar);
return;
}
if (isNextSnackbar(callback)) {
mNextSnackbar.duration = duration;
} else {
mNextSnackbar = new SnackbarRecord(duration, callback);
}
if ((mCurrentSnackbar != null) && (cancelSnackbarLocked(mCurrentSnackbar, Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE))) {
return;
}
mCurrentSnackbar = null;
showNextSnackbarLocked();
}
}
public void dismiss(Callback callback, int event) {
synchronized (mLock) {
if (isCurrentSnackbar(callback)) {
cancelSnackbarLocked(mCurrentSnackbar, event);
} else if (isNextSnackbar(callback)) {
cancelSnackbarLocked(mNextSnackbar, event);
}
}
}
public void onDismissed(Callback callback) {
synchronized (mLock) {
if (isCurrentSnackbar(callback)) {
mCurrentSnackbar = null;
if (mNextSnackbar != null) {
showNextSnackbarLocked();
}
}
}
}
public void onShown(Callback callback) {
synchronized (mLock) {
if (isCurrentSnackbar(callback)) {
scheduleTimeoutLocked(mCurrentSnackbar);
}
}
}
public void cancelTimeout(Callback callback) {
synchronized (mLock) {
if (isCurrentSnackbar(callback)) {
mHandler.removeCallbacksAndMessages(mCurrentSnackbar);
}
}
}
public void restoreTimeout(Callback callback) {
synchronized (mLock) {
if (isCurrentSnackbar(callback)) {
scheduleTimeoutLocked(mCurrentSnackbar);
}
}
}
private static class SnackbarRecord {
private final WeakReference<Callback> callback;
private int duration;
SnackbarRecord(int duration, SnackbarManager.Callback callback) {
this.callback = new WeakReference<>(callback);
this.duration = duration;
}
boolean isSnackbar(SnackbarManager.Callback callback) {
return (callback != null) && (this.callback.get() == callback);
}
}
private void showNextSnackbarLocked() {
if (mNextSnackbar != null) {
mCurrentSnackbar = mNextSnackbar;
mNextSnackbar = null;
Callback callback = mCurrentSnackbar.callback.get();
if (callback != null) {
callback.show();
} else {
mCurrentSnackbar = null;
}
}
}
private boolean cancelSnackbarLocked(SnackbarRecord record, int event) {
Callback callback = record.callback.get();
if (callback != null) {
callback.dismiss(event);
return true;
}
return false;
}
private boolean isCurrentSnackbar(Callback callback) {
return (mCurrentSnackbar != null) && (mCurrentSnackbar.isSnackbar(callback));
}
private boolean isNextSnackbar(Callback callback) {
return (mNextSnackbar != null) && (mNextSnackbar.isSnackbar(callback));
}
private void scheduleTimeoutLocked(SnackbarRecord r) {
if (r.duration == Snackbar.LENGTH_INDEFINITE) {
return;
}
int durationMs;
if (r.duration > Snackbar.LENGTH_LONG) {
durationMs = r.duration;
} else if (r.duration == Snackbar.LENGTH_SHORT) {
durationMs = SHORT_DURATION_MS;
} else {
durationMs = LONG_DURATION_MS;
}
mHandler.removeCallbacksAndMessages(r);
mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_TIMEOUT, r), durationMs);
}
private void handleTimeout(SnackbarRecord record) {
synchronized (mLock) {
if ((mCurrentSnackbar == record) || (mNextSnackbar == record)) {
cancelSnackbarLocked(record, Snackbar.Callback.DISMISS_EVENT_TIMEOUT);
}
}
}
interface Callback {
void show();
void dismiss(int paramInt);
}
}