mirror of
https://github.com/moparisthebest/open-keychain
synced 2025-02-08 02:50:12 -05:00
340 lines
11 KiB
Java
340 lines
11 KiB
Java
/*
|
|
* Copyright 2012 Evgeny Shishkin
|
|
*
|
|
* 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.devspark.appmsg;
|
|
|
|
import android.annotation.TargetApi;
|
|
import android.app.Activity;
|
|
import android.app.Application;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.os.Message;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.view.animation.Animation;
|
|
import android.view.animation.AnimationUtils;
|
|
|
|
import java.lang.ref.WeakReference;
|
|
import java.util.Collection;
|
|
import java.util.Comparator;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedList;
|
|
import java.util.PriorityQueue;
|
|
import java.util.Queue;
|
|
import java.util.WeakHashMap;
|
|
|
|
import static android.app.Application.ActivityLifecycleCallbacks;
|
|
import static android.os.Build.VERSION.SDK_INT;
|
|
import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
|
|
import static com.devspark.appmsg.AppMsg.LENGTH_STICKY;
|
|
|
|
/**
|
|
* @author Evgeny Shishkin
|
|
*/
|
|
class MsgManager extends Handler implements Comparator<AppMsg> {
|
|
|
|
private static final int MESSAGE_DISPLAY = 0xc2007;
|
|
private static final int MESSAGE_ADD_VIEW = 0xc20074dd;
|
|
private static final int MESSAGE_REMOVE = 0xc2007de1;
|
|
|
|
private static WeakHashMap<Activity, MsgManager> sManagers;
|
|
private static ReleaseCallbacks sReleaseCallbacks;
|
|
|
|
private final Queue<AppMsg> msgQueue;
|
|
private final Queue<AppMsg> stickyQueue;
|
|
|
|
private MsgManager() {
|
|
msgQueue = new PriorityQueue<AppMsg>(1, this);
|
|
stickyQueue = new LinkedList<AppMsg>();
|
|
}
|
|
|
|
/**
|
|
* @return A {@link MsgManager} instance to be used for given {@link android.app.Activity}.
|
|
*/
|
|
static synchronized MsgManager obtain(Activity activity) {
|
|
if (sManagers == null) {
|
|
sManagers = new WeakHashMap<Activity, MsgManager>(1);
|
|
}
|
|
MsgManager manager = sManagers.get(activity);
|
|
if (manager == null) {
|
|
manager = new MsgManager();
|
|
ensureReleaseOnDestroy(activity);
|
|
sManagers.put(activity, manager);
|
|
}
|
|
|
|
return manager;
|
|
}
|
|
|
|
static void ensureReleaseOnDestroy(Activity activity) {
|
|
if (SDK_INT < ICE_CREAM_SANDWICH) {
|
|
return;
|
|
}
|
|
if (sReleaseCallbacks == null) {
|
|
sReleaseCallbacks = new ReleaseCallbacksIcs();
|
|
}
|
|
sReleaseCallbacks.register(activity.getApplication());
|
|
}
|
|
|
|
|
|
static synchronized void release(Activity activity) {
|
|
if (sManagers != null) {
|
|
final MsgManager manager = sManagers.remove(activity);
|
|
if (manager != null) {
|
|
manager.clearAllMsg();
|
|
}
|
|
}
|
|
}
|
|
|
|
static synchronized void clearAll() {
|
|
if (sManagers != null) {
|
|
final Iterator<MsgManager> iterator = sManagers.values().iterator();
|
|
while (iterator.hasNext()) {
|
|
final MsgManager manager = iterator.next();
|
|
if (manager != null) {
|
|
manager.clearAllMsg();
|
|
}
|
|
iterator.remove();
|
|
}
|
|
sManagers.clear();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Inserts a {@link AppMsg} to be displayed.
|
|
*
|
|
* @param appMsg
|
|
*/
|
|
void add(AppMsg appMsg) {
|
|
msgQueue.add(appMsg);
|
|
if (appMsg.mInAnimation == null) {
|
|
appMsg.mInAnimation = AnimationUtils.loadAnimation(appMsg.getActivity(),
|
|
android.R.anim.fade_in);
|
|
}
|
|
if (appMsg.mOutAnimation == null) {
|
|
appMsg.mOutAnimation = AnimationUtils.loadAnimation(appMsg.getActivity(),
|
|
android.R.anim.fade_out);
|
|
}
|
|
displayMsg();
|
|
}
|
|
|
|
/**
|
|
* Removes all {@link AppMsg} from the queue.
|
|
*/
|
|
void clearMsg(AppMsg appMsg) {
|
|
if(msgQueue.contains(appMsg) || stickyQueue.contains(appMsg)){
|
|
// Avoid the message from being removed twice.
|
|
removeMessages(MESSAGE_DISPLAY, appMsg);
|
|
removeMessages(MESSAGE_ADD_VIEW, appMsg);
|
|
removeMessages(MESSAGE_REMOVE, appMsg);
|
|
msgQueue.remove(appMsg);
|
|
stickyQueue.remove(appMsg);
|
|
removeMsg(appMsg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes all {@link AppMsg} from the queue.
|
|
*/
|
|
void clearAllMsg() {
|
|
removeMessages(MESSAGE_DISPLAY);
|
|
removeMessages(MESSAGE_ADD_VIEW);
|
|
removeMessages(MESSAGE_REMOVE);
|
|
clearShowing();
|
|
msgQueue.clear();
|
|
stickyQueue.clear();
|
|
}
|
|
|
|
void clearShowing() {
|
|
final Collection<AppMsg> showing = new HashSet<AppMsg>();
|
|
obtainShowing(msgQueue, showing);
|
|
obtainShowing(stickyQueue, showing);
|
|
for (AppMsg msg : showing) {
|
|
clearMsg(msg);
|
|
}
|
|
}
|
|
|
|
static void obtainShowing(Collection<AppMsg> from, Collection<AppMsg> appendTo) {
|
|
for (AppMsg msg : from) {
|
|
if (msg.isShowing()) {
|
|
appendTo.add(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Displays the next {@link AppMsg} within the queue.
|
|
*/
|
|
private void displayMsg() {
|
|
if (msgQueue.isEmpty()) {
|
|
return;
|
|
}
|
|
// First peek whether the AppMsg is being displayed.
|
|
final AppMsg appMsg = msgQueue.peek();
|
|
final Message msg;
|
|
if (!appMsg.isShowing()) {
|
|
// Display the AppMsg
|
|
msg = obtainMessage(MESSAGE_ADD_VIEW);
|
|
msg.obj = appMsg;
|
|
sendMessage(msg);
|
|
} else if (appMsg.getDuration() != LENGTH_STICKY) {
|
|
msg = obtainMessage(MESSAGE_DISPLAY);
|
|
sendMessageDelayed(msg, appMsg.getDuration()
|
|
+ appMsg.mInAnimation.getDuration() + appMsg.mOutAnimation.getDuration());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes the {@link AppMsg}'s view after it's display duration.
|
|
*
|
|
* @param appMsg The {@link AppMsg} added to a {@link ViewGroup} and should be removed.s
|
|
*/
|
|
private void removeMsg(final AppMsg appMsg) {
|
|
clearMsg(appMsg);
|
|
final View view = appMsg.getView();
|
|
ViewGroup parent = ((ViewGroup) view.getParent());
|
|
if (parent != null) {
|
|
appMsg.mOutAnimation.setAnimationListener(new OutAnimationListener(appMsg));
|
|
view.clearAnimation();
|
|
view.startAnimation(appMsg.mOutAnimation);
|
|
}
|
|
|
|
Message msg = obtainMessage(MESSAGE_DISPLAY);
|
|
sendMessage(msg);
|
|
}
|
|
|
|
private void addMsgToView(AppMsg appMsg) {
|
|
View view = appMsg.getView();
|
|
if (view.getParent() == null) { // Not added yet
|
|
final ViewGroup targetParent = appMsg.getParent();
|
|
final ViewGroup.LayoutParams params = appMsg.getLayoutParams();
|
|
if (targetParent != null) {
|
|
targetParent.addView(view, params);
|
|
} else {
|
|
appMsg.getActivity().addContentView(view, params);
|
|
}
|
|
}
|
|
view.clearAnimation();
|
|
view.startAnimation(appMsg.mInAnimation);
|
|
if (view.getVisibility() != View.VISIBLE) {
|
|
view.setVisibility(View.VISIBLE);
|
|
}
|
|
|
|
final int duration = appMsg.getDuration();
|
|
if (duration != LENGTH_STICKY) {
|
|
final Message msg = obtainMessage(MESSAGE_REMOVE);
|
|
msg.obj = appMsg;
|
|
sendMessageDelayed(msg, duration);
|
|
} else { // We are sticky, we don't get removed just yet
|
|
stickyQueue.add(msgQueue.poll());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void handleMessage(Message msg) {
|
|
final AppMsg appMsg;
|
|
switch (msg.what) {
|
|
case MESSAGE_DISPLAY:
|
|
displayMsg();
|
|
break;
|
|
case MESSAGE_ADD_VIEW:
|
|
appMsg = (AppMsg) msg.obj;
|
|
addMsgToView(appMsg);
|
|
break;
|
|
case MESSAGE_REMOVE:
|
|
appMsg = (AppMsg) msg.obj;
|
|
removeMsg(appMsg);
|
|
break;
|
|
default:
|
|
super.handleMessage(msg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int compare(AppMsg lhs, AppMsg rhs) {
|
|
return inverseCompareInt(lhs.mPriority, rhs.mPriority);
|
|
}
|
|
|
|
static int inverseCompareInt(int lhs, int rhs) {
|
|
return lhs < rhs ? 1 : (lhs == rhs ? 0 : -1);
|
|
}
|
|
|
|
private static class OutAnimationListener implements Animation.AnimationListener {
|
|
|
|
private final AppMsg appMsg;
|
|
|
|
private OutAnimationListener(AppMsg appMsg) {
|
|
this.appMsg = appMsg;
|
|
}
|
|
|
|
@Override
|
|
public void onAnimationStart(Animation animation) {
|
|
|
|
}
|
|
|
|
@Override
|
|
public void onAnimationEnd(Animation animation) {
|
|
final View view = appMsg.getView();
|
|
if (appMsg.isFloating()) {
|
|
final ViewGroup parent = ((ViewGroup) view.getParent());
|
|
if (parent != null) {
|
|
parent.post(new Runnable() { // One does not simply removeView
|
|
@Override
|
|
public void run() {
|
|
parent.removeView(view);
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
view.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onAnimationRepeat(Animation animation) {
|
|
|
|
}
|
|
}
|
|
|
|
interface ReleaseCallbacks {
|
|
void register(Application application);
|
|
}
|
|
|
|
@TargetApi(ICE_CREAM_SANDWICH)
|
|
static class ReleaseCallbacksIcs implements ActivityLifecycleCallbacks, ReleaseCallbacks {
|
|
private WeakReference<Application> mLastApp;
|
|
public void register(Application app) {
|
|
if (mLastApp != null && mLastApp.get() == app) {
|
|
return; // Already registered with this app
|
|
} else {
|
|
mLastApp = new WeakReference<Application>(app);
|
|
}
|
|
app.registerActivityLifecycleCallbacks(this);
|
|
}
|
|
|
|
@Override
|
|
public void onActivityDestroyed(Activity activity) {
|
|
release(activity);
|
|
}
|
|
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
|
|
@Override public void onActivityStarted(Activity activity) {}
|
|
@Override public void onActivityResumed(Activity activity) {}
|
|
@Override public void onActivityPaused(Activity activity) {}
|
|
@Override public void onActivityStopped(Activity activity) {}
|
|
@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
|
|
}
|
|
} |