diff --git a/OpenPGP-Keychain/build.gradle b/OpenPGP-Keychain/build.gradle index 28e8c37b1..1454b80e7 100644 --- a/OpenPGP-Keychain/build.gradle +++ b/OpenPGP-Keychain/build.gradle @@ -13,6 +13,7 @@ dependencies { compile project(':libraries:spongycastle:pg') compile project(':libraries:spongycastle:pkix') compile project(':libraries:spongycastle:prov') + compile project(':libraries:Android-AppMsg:library') } android { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 1864e0d9d..a5027ac1c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -47,9 +47,9 @@ import android.support.v7.app.ActionBar; import android.view.View; import android.view.View.OnClickListener; import android.widget.ArrayAdapter; -import android.widget.Toast; import com.beardedhen.androidbootstrap.BootstrapButton; +import com.devspark.appmsg.AppMsg; public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNavigationListener { public static final String ACTION_IMPORT_KEY = Constants.INTENT_PREFIX + "IMPORT_KEY"; @@ -282,8 +282,8 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa Log.d(Constants.TAG, "fingerprint: " + fingerprint); if (fingerprint.length() < 16) { - Toast.makeText(this, R.string.import_qr_code_too_short_fingerprint, - Toast.LENGTH_LONG).show(); + AppMsg.makeText(this, R.string.import_qr_code_too_short_fingerprint, + AppMsg.STYLE_ALERT).show(); return; } @@ -392,7 +392,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa } else { toastMessage = getString(R.string.no_keys_added_or_updated); } - Toast.makeText(ImportKeysActivity.this, toastMessage, Toast.LENGTH_SHORT) + AppMsg.makeText(ImportKeysActivity.this, toastMessage, AppMsg.STYLE_INFO) .show(); if (bad > 0) { AlertDialog.Builder alert = new AlertDialog.Builder( @@ -474,7 +474,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa // start service with intent startService(intent); } else { - Toast.makeText(this, R.string.error_nothing_import, Toast.LENGTH_LONG).show(); + AppMsg.makeText(this, R.string.error_nothing_import, AppMsg.STYLE_ALERT).show(); } } diff --git a/OpenPGP-Keychain/src/main/res/raw/help_about.html b/OpenPGP-Keychain/src/main/res/raw/help_about.html index 85130965c..882049c49 100644 --- a/OpenPGP-Keychain/src/main/res/raw/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw/help_about.html @@ -34,8 +34,9 @@ And don't add newlines before or after p tags because of transifex -->
  • ZXing (Apache License v2)
  • SpongyCastle (MIT X11 License)
  • HtmlTextView (Apache License v2)
  • +
  • Android AppMsg Library (Apache License v2)
  • Icons from RRZE Icon Set (Creative Commons Attribution Share-Alike licence 3.0)
  • Icons from Tango Icon Set (Public Domain)
  • - \ No newline at end of file + diff --git a/README.md b/README.md index 29a8d35ff..3a8c81b74 100644 --- a/README.md +++ b/README.md @@ -240,6 +240,9 @@ Some parts (older parts and some libraries are Apache License v2, MIT X11 Licens https://github.com/Bearded-Hen/Android-Bootstrap MIT License +* Android AppMsg + https://github.com/johnkil/Android-AppMsg + Apache License v2 ### Images * icon.svg diff --git a/libraries/Android-AppMsg/.gitignore b/libraries/Android-AppMsg/.gitignore new file mode 100644 index 000000000..bdbc634c1 --- /dev/null +++ b/libraries/Android-AppMsg/.gitignore @@ -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 diff --git a/libraries/Android-AppMsg/LICENSE.txt b/libraries/Android-AppMsg/LICENSE.txt new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/libraries/Android-AppMsg/LICENSE.txt @@ -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. diff --git a/libraries/Android-AppMsg/README.md b/libraries/Android-AppMsg/README.md new file mode 100644 index 000000000..7f8006998 --- /dev/null +++ b/libraries/Android-AppMsg/README.md @@ -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. Let’s 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. + +Copyright (C) by Cyril Mottier + +Sample +------ + +A sample application is available on Google Play: + + + Get it on Google Play + + +![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 - + +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 diff --git a/libraries/Android-AppMsg/library/AndroidManifest.xml b/libraries/Android-AppMsg/library/AndroidManifest.xml new file mode 100644 index 000000000..c03f66559 --- /dev/null +++ b/libraries/Android-AppMsg/library/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/libraries/Android-AppMsg/library/build.gradle b/libraries/Android-AppMsg/library/build.gradle new file mode 100644 index 000000000..934cf1cb1 --- /dev/null +++ b/libraries/Android-AppMsg/library/build.gradle @@ -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' +} \ No newline at end of file diff --git a/libraries/Android-AppMsg/library/pom.xml b/libraries/Android-AppMsg/library/pom.xml new file mode 100644 index 000000000..149557370 --- /dev/null +++ b/libraries/Android-AppMsg/library/pom.xml @@ -0,0 +1,71 @@ + + + + 4.0.0 + + com.devspark + appmsg + 1.0.1 + apklib + + + + com.google.android + android + 4.1.1.4 + provided + + + + + src + + + org.apache.maven.plugins + maven-pmd-plugin + 2.7.1 + + true + utf-8 + 100 + 1.6 + + **/*Bean.java + **/generated/*.java + + + target/generated-sources/stubs + + + + + + com.jayway.maven.plugins.android.generation2 + android-maven-plugin + 3.6.0 + + ${project.basedir}/AndroidManifest.xml + ${project.basedir}/assets + ${project.basedir}/res + ${project.basedir}/src/main/native + + 16 + + true + + true + + + + maven-compiler-plugin + 2.3.2 + + 1.6 + 1.6 + + + + + diff --git a/libraries/Android-AppMsg/library/project.properties b/libraries/Android-AppMsg/library/project.properties new file mode 100644 index 000000000..91d2b0246 --- /dev/null +++ b/libraries/Android-AppMsg/library/project.properties @@ -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 diff --git a/libraries/Android-AppMsg/library/res/layout/app_msg.xml b/libraries/Android-AppMsg/library/res/layout/app_msg.xml new file mode 100644 index 000000000..235fe47b9 --- /dev/null +++ b/libraries/Android-AppMsg/library/res/layout/app_msg.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/libraries/Android-AppMsg/library/res/values/colors.xml b/libraries/Android-AppMsg/library/res/values/colors.xml new file mode 100644 index 000000000..2bfd9ecb4 --- /dev/null +++ b/libraries/Android-AppMsg/library/res/values/colors.xml @@ -0,0 +1,8 @@ + + + + #CC0000 + #FF8800 + #669900 + + \ No newline at end of file diff --git a/libraries/Android-AppMsg/library/src/com/devspark/appmsg/AppMsg.java b/libraries/Android-AppMsg/library/src/com/devspark/appmsg/AppMsg.java new file mode 100644 index 000000000..e86273762 --- /dev/null +++ b/libraries/Android-AppMsg/library/src/com/devspark/appmsg/AppMsg.java @@ -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; + + /** + *

    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.

    + * + *

    Note: You are responsible + * for calling {@link #cancel()} on such {@link com.devspark.appmsg.AppMsg}.

    + * + * @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 true if the {@link AppMsg} is being displayed, else false. + */ + 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 this, 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 this, 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; + } + + /** + *

    Set priority for this message

    + *

    Note: This only affects the order in which the messages get shown, + * not the stacking order of the views.

    + * + *

    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.

    + * + * @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; + } + + } + +} 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 { + + 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 sManagers; + private static ReleaseCallbacks sReleaseCallbacks; + + private final Queue msgQueue; + private final Queue stickyQueue; + + private MsgManager() { + msgQueue = new PriorityQueue(1, this); + stickyQueue = new LinkedList(); + } + + /** + * @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(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 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 showing = new HashSet(); + obtainShowing(msgQueue, showing); + obtainShowing(stickyQueue, showing); + for (AppMsg msg : showing) { + clearMsg(msg); + } + } + + static void obtainShowing(Collection from, Collection 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 mLastApp; + public void register(Application app) { + if (mLastApp != null && mLastApp.get() == app) { + return; // Already registered with this app + } else { + mLastApp = new WeakReference(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 diff --git a/settings.gradle b/settings.gradle index 344278063..f123762aa 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,3 +9,4 @@ include ':libraries:spongycastle:core' include ':libraries:spongycastle:pg' include ':libraries:spongycastle:pkix' include ':libraries:spongycastle:prov' +include ':libraries:Android-AppMsg:library'