aboutsummaryrefslogtreecommitdiffstats
path: root/libraries/Android-AppMsg/library/src/com/devspark/appmsg/MsgManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/Android-AppMsg/library/src/com/devspark/appmsg/MsgManager.java')
-rw-r--r--libraries/Android-AppMsg/library/src/com/devspark/appmsg/MsgManager.java340
1 files changed, 340 insertions, 0 deletions
diff --git a/libraries/Android-AppMsg/library/src/com/devspark/appmsg/MsgManager.java b/libraries/Android-AppMsg/library/src/com/devspark/appmsg/MsgManager.java
new file mode 100644
index 000000000..962648566
--- /dev/null
+++ b/libraries/Android-AppMsg/library/src/com/devspark/appmsg/MsgManager.java
@@ -0,0 +1,340 @@
+/*
+ * 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) {}
+ }
+} \ No newline at end of file