Added AppMsg library to the librarys-folder

This commit is contained in:
Daniel Haß 2014-02-20 22:53:47 +01:00
parent 4839172f5b
commit 15757a2a32
11 changed files with 1430 additions and 0 deletions

36
libraries/Android-AppMsg/.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
#Android generated
bin
gen
#Eclipse
.project
.classpath
.settings
#IntelliJ IDEA
.idea
*.iml
*.ipr
*.iws
out
#Maven
target
release.properties
pom.xml.*
#Ant
build.xml
local.properties
proguard.cfg
proguard-project.txt
#Gradle
.gradle
build
#OSX
.DS_Store
#Personal Files
signing.properties

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@ -0,0 +1,117 @@
Android AppMsg (Crouton) Library
================================
Implementation of in-layout notifications. Based on [Toast](http://developer.android.com/reference/android/widget/Toast.html) notifications and article [The making of Prixing #4: in-layout notifications](http://android.cyrilmottier.com/?p=773) by [Cyril Mottier](http://www.cyrilmottier.com/).
Description
-----------
Toast is far from being perfect and I am not entirely satisfied with it.
Toast can be un-accurate in some cases. Indeed, Toast has one major drawback: it completely breaks contexts.
This issue can be reproduced effortless. Lets say a user is currently in an app firing a Toast and wants to switch to another application using the dedicated “multitask” button.
The Toast will remain on screen even if the brought-to-front application has nothing do to with the previously shown app as described on the following figure:
![Example Image][1]
As you can easily notice, the problem with Toasts is they are persistent.
Once a Toast has been fired, it is displayed on top of any screen and remains visible for the duration specified at its creation.
In order to bypass the Toast persistence problem and ensure information is displayed in the correct context, we decided to create a new notification system:
Activity-bound notifications. This is what it looks like in the current version of Prixing:
![Example Image][2]
Crouton overcomes the main issue of having a Toast being shown while the menu is open.
It sticks to the current screen sliding with it and leaving the menu completely free of any information that would have not been related to it.
<b>Copyright (C) by Cyril Mottier</b>
Sample
------
A sample application is available on Google Play:
<a href="http://play.google.com/store/apps/details?id=com.devspark.appmsg.sample">
<img alt="Get it on Google Play"
src="http://www.android.com/images/brand/get_it_on_play_logo_small.png" />
</a>
![Example Image][3]
The source code is available in this repository.
Compatibility
-------------
This library is compatible from API 4 (Android 1.6).
Installation
------------
The sample project requires:
* The library project
* [ActionBarSherlock](https://github.com/JakeWharton/ActionBarSherlock)
Usage
-----
Android AppMsg is presented as an [Android library project](http://developer.android.com/guide/developing/projects/projects-eclipse.html).
You can include this project by [referencing it as a library project](http://developer.android.com/guide/developing/projects/projects-eclipse.html#ReferencingLibraryProject) in Eclipse or ant.
To display the item you need the following code:
* Show AppMsg:
``` java
AppMsg.makeText(/*Activity*/, /*CharSequence*/, /*AppMsg.Style*/).show();
```
Gradle
------
Android-AppMsg Library is now pushed to Maven Central as a AAR, so you just need to add the following dependency to your build.gradle.
``` xml
dependencies {
compile 'com.github.johnkil.android-appmsg:appmsg:1.2.0'
}
```
Example Gradle project using Android-AppMsg:
* [Android-AppMsg-Gradle-Sample](https://github.com/johnkil/Android-AppMsg-Gradle-Sample)
Contribution
------------
Please fork [dev](https://github.com/johnkil/Android-AppMsg/tree/dev) repository and contribute back using [pull requests](https://github.com/johnkil/Android-AppMsg/pulls).
Contributors are recommended to follow the Android [Code Style Guidelines](http://source.android.com/source/code-style.html).
Any contributions, large or small, major features, bug fixes, additional language translations, unit/integration tests are welcomed and appreciated but will be thoroughly reviewed and discussed.
Developed By
------------
* Evgeny Shishkin - <johnkil78@gmail.com>
License
-------
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.
[1]: http://cyrilmottier.com/media/2012/07/the-making-of-prixing-4-activity-tied-notifications/toast_user_flow_fail.png
[2]: http://cyrilmottier.com/media/2012/07/the-making-of-prixing-4-activity-tied-notifications/in_layout_notification.png
[3]: http://i46.tinypic.com/21kywit.png

View File

@ -0,0 +1,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.devspark.appmsg"
android:versionCode="5"
android:versionName="1.2.0">
<uses-sdk
android:minSdkVersion="4"
android:targetSdkVersion="19"/>
<application/>
</manifest>

View File

@ -0,0 +1,22 @@
apply plugin: 'android-library'
android {
compileSdkVersion 19
buildToolsVersion '19.0.1'
defaultConfig {
minSdkVersion 4
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
res.srcDirs = ['res']
}
}
}
if (project.hasProperty('nexusUsername')) {
// Used to push in maven
apply from: '../maven_push.gradle'
}

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.devspark</groupId>
<artifactId>appmsg</artifactId>
<version>1.0.1</version>
<packaging>apklib</packaging>
<dependencies>
<dependency>
<groupId>com.google.android</groupId>
<artifactId>android</artifactId>
<version>4.1.1.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>2.7.1</version>
<configuration>
<linkXref>true</linkXref>
<sourceEncoding>utf-8</sourceEncoding>
<minimumTokens>100</minimumTokens>
<targetJdk>1.6</targetJdk>
<excludes>
<exclude>**/*Bean.java</exclude>
<exclude>**/generated/*.java</exclude>
</excludes>
<excludeRoots>
<excludeRoot>target/generated-sources/stubs</excludeRoot>
</excludeRoots>
</configuration>
</plugin>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>android-maven-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<androidManifestFile>${project.basedir}/AndroidManifest.xml</androidManifestFile>
<assetsDirectory>${project.basedir}/assets</assetsDirectory>
<resourceDirectory>${project.basedir}/res</resourceDirectory>
<nativeLibrariesDirectory>${project.basedir}/src/main/native</nativeLibrariesDirectory>
<sdk>
<platform>16</platform>
</sdk>
<undeployBeforeDeploy>true</undeployBeforeDeploy>
</configuration>
<extensions>true</extensions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,15 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-19
android.library=true

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:minHeight="48dp"
android:orientation="vertical" >
<TextView
android:id="@android:id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="8dp"
android:textColor="?android:textColorPrimaryInverse"
android:textIsSelectable="false"
android:textSize="14sp"
android:textStyle="bold" />
</LinearLayout>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="alert">#CC0000</color>
<color name="confirm">#FF8800</color>
<color name="info">#669900</color>
</resources>

View File

@ -0,0 +1,587 @@
/*
* 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.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.TextView;
/**
* In-layout notifications. Based on {@link android.widget.Toast} notifications
* and article by Cyril Mottier (http://android.cyrilmottier.com/?p=773).
*
* @author e.shishkin
*/
public class AppMsg {
/**
* Show the view or text notification for a short period of time. This time
* could be user-definable. This is the default.
*
* @see #setDuration
*/
public static final int LENGTH_SHORT = 3000;
/**
* Show the view or text notification for a long period of time. This time
* could be user-definable.
*
* @see #setDuration
*/
public static final int LENGTH_LONG = 5000;
/**
* <p>Show the view or text notification for an undefined amount of time
* -Usually until an invocation of {@link #cancel()}, {@link #cancelAll(android.app.Activity)},
* {@link #cancelAll()} or {@link android.app.Activity#onDestroy()}-,
* stacking on top of any other {@link com.devspark.appmsg.AppMsg} with this duration.</p>
*
* <p><b>Note</b>: You are responsible
* for calling {@link #cancel()} on such {@link com.devspark.appmsg.AppMsg}.</p>
*
* @see #setDuration
*/
public static final int LENGTH_STICKY = -1;
/**
* Lowest priority, messages with this priority will be showed after all messages with priority
* {@link #PRIORITY_HIGH} and {@link #PRIORITY_NORMAL} have been shown.
*
* @see #setPriority(int)
*/
public static final int PRIORITY_LOW = Integer.MIN_VALUE;
/**
* Normal priority, messages with this priority will be showed after all messages with priority
* {@link #PRIORITY_HIGH} but before {@link #PRIORITY_LOW} have been shown.
*
* @see #setPriority(int)
*/
public static final int PRIORITY_NORMAL = 0;
/**
* Highest priority, messages with this priority will be showed before any other message.
*
* @see #setPriority(int)
*/
public static final int PRIORITY_HIGH = Integer.MAX_VALUE;
/**
* Show the text notification for a long period of time with a negative style.
*/
public static final Style STYLE_ALERT = new Style(LENGTH_LONG, R.color.alert);
/**
* Show the text notification for a short period of time with a positive style.
*/
public static final Style STYLE_CONFIRM = new Style(LENGTH_SHORT, R.color.confirm);
/**
* Show the text notification for a short period of time with a neutral style.
*/
public static final Style STYLE_INFO = new Style(LENGTH_SHORT, R.color.info);
private final Activity mActivity;
private int mDuration = LENGTH_SHORT;
private View mView;
private ViewGroup mParent;
private LayoutParams mLayoutParams;
private boolean mFloating;
Animation mInAnimation, mOutAnimation;
int mPriority = PRIORITY_NORMAL;
/**
* Construct an empty AppMsg object. You must call {@link #setView} before
* you can call {@link #show}.
*
* @param activity {@link android.app.Activity} to use.
*/
public AppMsg(Activity activity) {
mActivity = activity;
}
/**
* Make a {@link AppMsg} that just contains a text view.
*
* @param context The context to use. Usually your
* {@link android.app.Activity} object.
* @param text The text to show. Can be formatted text.
* @param style The style with a background and a duration.
*/
public static AppMsg makeText(Activity context, CharSequence text, Style style) {
return makeText(context, text, style, R.layout.app_msg);
}
/**
* @author mengguoqiang 扩展支持设置字体大小
* Make a {@link AppMsg} that just contains a text view.
*
* @param context The context to use. Usually your
* {@link android.app.Activity} object.
* @param text The text to show. Can be formatted text.
* @param style The style with a background and a duration.
*/
public static AppMsg makeText(Activity context, CharSequence text, Style style, float textSize) {
return makeText(context, text, style, R.layout.app_msg, textSize);
}
/**
* Make a {@link AppMsg} with a custom layout. The layout must have a {@link TextView} com id {@link android.R.id.message}
*
* @param context The context to use. Usually your
* {@link android.app.Activity} object.
* @param text The text to show. Can be formatted text.
* @param style The style with a background and a duration.
*/
public static AppMsg makeText(Activity context, CharSequence text, Style style, int layoutId) {
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(layoutId, null);
return makeText(context, text, style, v, true);
}
/**
* @author mengguoqiang 扩展支持字体大小
* Make a {@link AppMsg} with a custom layout. The layout must have a {@link TextView} com id {@link android.R.id.message}
*
* @param context The context to use. Usually your
* {@link android.app.Activity} object.
* @param text The text to show. Can be formatted text.
* @param style The style with a background and a duration.
*/
public static AppMsg makeText(Activity context, CharSequence text, Style style, int layoutId, float textSize) {
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(layoutId, null);
return makeText(context, text, style, v, true, textSize);
}
/**
* Make a non-floating {@link AppMsg} with a custom view presented inside the layout.
* It can be used to create non-floating notifications if floating is false.
*
* @param context The context to use. Usually your
* {@link android.app.Activity} object.
* @param customView
* View to be used.
* @param text The text to show. Can be formatted text.
* @param style The style with a background and a duration.
*/
public static AppMsg makeText(Activity context, CharSequence text, Style style, View customView) {
return makeText(context, text, style, customView, false);
}
/**
* Make a {@link AppMsg} with a custom view. It can be used to create non-floating notifications if floating is false.
*
* @param context The context to use. Usually your
* {@link android.app.Activity} object.
* @param view
* View to be used.
* @param text The text to show. Can be formatted text.
* @param style The style with a background and a duration.
* @param floating true if it'll float.
*/
private static AppMsg makeText(Activity context, CharSequence text, Style style, View view, boolean floating) {
return makeText(context, text, style, view, floating, 0);
}
/**
*
* @author mengguoqiang 扩展支持设置字体大小
* Make a {@link AppMsg} with a custom view. It can be used to create non-floating notifications if floating is false.
*
* @param context The context to use. Usually your
* {@link android.app.Activity} object.
* @param view
* View to be used.
* @param text The text to show. Can be formatted text.
* @param style The style with a background and a duration.
* @param floating true if it'll float.
*/
private static AppMsg makeText(Activity context, CharSequence text, Style style, View view, boolean floating, float textSize) {
AppMsg result = new AppMsg(context);
view.setBackgroundResource(style.background);
TextView tv = (TextView) view.findViewById(android.R.id.message);
if(textSize > 0) tv.setTextSize(textSize);
tv.setText(text);
result.mView = view;
result.mDuration = style.duration;
result.mFloating = floating;
return result;
}
/**
* Make a {@link AppMsg} with a custom view. It can be used to create non-floating notifications if floating is false.
*
* @param context The context to use. Usually your
* {@link android.app.Activity} object.
* @param resId The resource id of the string resource to use. Can be
* formatted text.
* @param style The style with a background and a duration.
* @param floating true if it'll float.
*/
public static AppMsg makeText(Activity context, int resId, Style style, View customView, boolean floating) {
return makeText(context, context.getResources().getText(resId), style, customView, floating);
}
/**
* Make a {@link AppMsg} that just contains a text view with the text from a
* resource.
*
* @param context The context to use. Usually your
* {@link android.app.Activity} object.
* @param resId The resource id of the string resource to use. Can be
* formatted text.
* @param style The style with a background and a duration.
* @throws Resources.NotFoundException if the resource can't be found.
*/
public static AppMsg makeText(Activity context, int resId, Style style)
throws Resources.NotFoundException {
return makeText(context, context.getResources().getText(resId), style);
}
/**
* Make a {@link AppMsg} with a custom layout using the text from a
* resource. The layout must have a {@link TextView} com id {@link android.R.id.message}
*
* @param context The context to use. Usually your
* {@link android.app.Activity} object.
* @param resId The resource id of the string resource to use. Can be
* formatted text.
* @param style The style with a background and a duration.
* @throws Resources.NotFoundException if the resource can't be found.
*/
public static AppMsg makeText(Activity context, int resId, Style style, int layoutId)
throws Resources.NotFoundException {
return makeText(context, context.getResources().getText(resId), style, layoutId);
}
/**
* Show the view for the specified duration.
*/
public void show() {
MsgManager manager = MsgManager.obtain(mActivity);
manager.add(this);
}
/**
* @return <code>true</code> if the {@link AppMsg} is being displayed, else <code>false</code>.
*/
public boolean isShowing() {
if (mFloating) {
return mView != null && mView.getParent() != null;
} else {
return mView.getVisibility() == View.VISIBLE;
}
}
/**
* Close the view if it's showing, or don't show it if it isn't showing yet.
* You do not normally have to call this. Normally view will disappear on its own
* after the appropriate duration.
*/
public void cancel() {
MsgManager.obtain(mActivity).clearMsg(this);
}
/**
* Cancels all queued {@link AppMsg}s, in all Activities. If there is a {@link AppMsg}
* displayed currently, it will be the last one displayed.
*/
public static void cancelAll() {
MsgManager.clearAll();
}
/**
* Cancels all queued {@link AppMsg}s, in given {@link android.app.Activity}.
* If there is a {@link AppMsg} displayed currently, it will be the last one displayed.
* @param activity
*/
public static void cancelAll(Activity activity) {
MsgManager.release(activity);
}
/**
* Return the activity.
*/
public Activity getActivity() {
return mActivity;
}
/**
* Set the view to show.
*
* @see #getView
*/
public void setView(View view) {
mView = view;
}
/**
* Return the view.
*
* @see #setView
*/
public View getView() {
return mView;
}
/**
* Set how long to show the view for.
*
* @see #LENGTH_SHORT
* @see #LENGTH_LONG
*/
public void setDuration(int duration) {
mDuration = duration;
}
/**
* Return the duration.
*
* @see #setDuration
*/
public int getDuration() {
return mDuration;
}
/**
* Update the text in a AppMsg that was previously created using one of the makeText() methods.
*
* @param resId The new text for the AppMsg.
*/
public void setText(int resId) {
setText(mActivity.getText(resId));
}
/**
* Update the text in a AppMsg that was previously created using one of the makeText() methods.
*
* @param s The new text for the AppMsg.
*/
public void setText(CharSequence s) {
if (mView == null) {
throw new RuntimeException("This AppMsg was not created with AppMsg.makeText()");
}
TextView tv = (TextView) mView.findViewById(android.R.id.message);
if (tv == null) {
throw new RuntimeException("This AppMsg was not created with AppMsg.makeText()");
}
tv.setText(s);
}
/**
* Gets the crouton's layout parameters, constructing a default if necessary.
*
* @return the layout parameters
*/
public LayoutParams getLayoutParams() {
if (mLayoutParams == null) {
mLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
}
return mLayoutParams;
}
/**
* Sets the layout parameters which will be used to display the crouton.
*
* @param layoutParams The layout parameters to use.
* @return <code>this</code>, for chaining.
*/
public AppMsg setLayoutParams(LayoutParams layoutParams) {
mLayoutParams = layoutParams;
return this;
}
/**
* Constructs and sets the layout parameters to have some gravity.
*
* @param gravity the gravity of the Crouton
* @return <code>this</code>, for chaining.
* @see android.view.Gravity
*/
public AppMsg setLayoutGravity(int gravity) {
mLayoutParams = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, gravity);
return this;
}
/**
* Return the value of floating.
*
* @see #setFloating(boolean)
*/
public boolean isFloating() {
return mFloating;
}
/**
* Sets the value of floating.
*
* @param mFloating
*/
public void setFloating(boolean mFloating) {
this.mFloating = mFloating;
}
/**
* Sets the Animations to be used when displaying/removing the Crouton.
* @param inAnimation the Animation resource ID to be used when displaying.
* @param outAnimation the Animation resource ID to be used when removing.
*/
public AppMsg setAnimation(int inAnimation, int outAnimation) {
return setAnimation(AnimationUtils.loadAnimation(mActivity, inAnimation),
AnimationUtils.loadAnimation(mActivity, outAnimation));
}
/**
* Sets the Animations to be used when displaying/removing the Crouton.
* @param inAnimation the Animation to be used when displaying.
* @param outAnimation the Animation to be used when removing.
*/
public AppMsg setAnimation(Animation inAnimation, Animation outAnimation) {
mInAnimation = inAnimation;
mOutAnimation = outAnimation;
return this;
}
/**
* @return
* Current priority
*
* @see #PRIORITY_HIGH
* @see #PRIORITY_NORMAL
* @see #PRIORITY_LOW
*/
public int getPriority() {
return mPriority;
}
/**
* <p>Set priority for this message</p>
* <p><b>Note</b>: This only affects the order in which the messages get shown,
* not the stacking order of the views.</p>
*
* <p>Example: In the queue there are 3 messages [A, B, C],
* all of them with priority {@link #PRIORITY_NORMAL}, currently message A is being shown
* so we add a new message D with priority {@link #PRIORITY_HIGH}, after A goes away, given that
* D has a higher priority than B an the reset, D will be shown, then once that D is gone,
* B will be shown, and then finally C.</p>
*
* @param priority
* A value indicating priority, although you can use any integer value, usage of already
* defined is highly encouraged.
*
* @see #PRIORITY_HIGH
* @see #PRIORITY_NORMAL
* @see #PRIORITY_LOW
*/
public void setPriority(int priority) {
mPriority = priority;
}
/**
* @return
* Provided parent to add {@link #getView()} to using {@link #getLayoutParams()}.
*/
public ViewGroup getParent() {
return mParent;
}
/**
* Provide a different parent than Activity decor view
* @param parent
* Provided parent to add {@link #getView()} to using {@link #getLayoutParams()}.
*
*/
public void setParent(ViewGroup parent) {
mParent = parent;
}
/**
* Provide a different parent than Activity decor view
*
* @param parentId
* Provided parent id to add {@link #getView()} to using {@link #getLayoutParams()}.
*
*/
public void setParent(int parentId) {
setParent((ViewGroup) mActivity.findViewById(parentId));
}
/**
* The style for a {@link AppMsg}.
*
* @author e.shishkin
*/
public static class Style {
private final int duration;
private final int background;
/**
* Construct an {@link AppMsg.Style} object.
*
* @param duration How long to display the message. Either
* {@link #LENGTH_SHORT} or {@link #LENGTH_LONG}
* @param resId resource for AppMsg background
*/
public Style(int duration, int resId) {
this.duration = duration;
this.background = resId;
}
/**
* Return the duration in milliseconds.
*/
public int getDuration() {
return duration;
}
/**
* Return the resource id of background.
*/
public int getBackground() {
return background;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof AppMsg.Style)) {
return false;
}
Style style = (Style) o;
return style.duration == duration
&& style.background == background;
}
}
}

View File

@ -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) {}
}
}