Compare commits

...
41 Commits
Author SHA1 Message Date
EddyVerbruggen dc6c537125 #86 Is there anyway to get a significantly larger font on IOS and Android? 2016-04-28 19:52:33 +02:00
EddyVerbruggen 9ed9f018a7 #71 ios toast get error while message is integer 2016-03-17 21:30:17 +01:00
EddyVerbruggen fe1aed17e3 #59 Add specific duration of the toast 2016-03-17 21:23:19 +01:00
Eddy Verbruggen 4fb9932c9b Merge pull request #78 from NoahYarian/patch-1
README.md - replaced , with : for valid JSON in success callback example
2016-02-29 22:32:58 +01:00
Noah Yarian 4c65555aca README.md - replaced , with : for valid JSON in success callback example 2016-02-29 15:08:59 -06:00
Eddy Verbruggen aa1a771f7b Update README.md 2016-02-19 09:43:44 +01:00
Eddy Verbruggen 35ace3dd9d Update README.md 2016-02-19 09:43:09 +01:00
Eddy Verbruggen c0daed7ce4 Update README.md 2016-02-19 09:42:57 +01:00
EddyVerbruggen 8dbcc43e68 #75 [Toast-PhoneGap-Plugin] styling.opacity is meanless 2016-02-18 21:18:00 +01:00
Eddy Verbruggen 4f6463e3f2 Update issue_template.md 2016-02-17 21:40:01 +01:00
Eddy Verbruggen 41a568c549 Update issue_template.md 2016-02-17 21:35:34 +01:00
EddyVerbruggen 811e0a82ed issue template 2016-02-17 21:27:24 +01:00
Eddy Verbruggen a2d253bdde Update plugin.xml 2016-02-17 19:50:19 +01:00
Eddy Verbruggen ffe056db75 Merge pull request #73 from Telerik-Verified-Plugins/master
Style the text color
2016-02-17 19:50:05 +01:00
EddyVerbruggen 5e5251bbd2 #2 Allow the user set a textColor - fixed a compiler warning, added doc, and bumped the version 2016-02-17 19:47:32 +01:00
Eddy Verbruggen 37c14a6d2d Merge pull request #2 from ergowerk/master
Allow the user set a textColor
2016-02-17 19:27:24 +01:00
Sino 369ab363b9 fixed naming, id and import 2016-02-16 22:53:43 +01:00
Sino 6ba778dd22 option to set the text color of the toast 2016-02-16 17:00:52 +01:00
Sino e8ade312d9 possibillity to change text color as a styling option 2016-02-16 16:48:24 +01:00
EddyVerbruggen f6479c8846 Merge remote-tracking branch 'upstream/master'
# Conflicts:
#	plugin.xml
2016-01-27 23:07:58 +01:00
EddyVerbruggen 658ee711db Merge remote-tracking branch 'upstream/master'
# Conflicts:
#	plugin.xml
2016-01-27 23:07:49 +01:00
EddyVerbruggen 5f4a0f5636 #4 styling 2016-01-27 20:49:03 +01:00
EddyVerbruggen c7a329a7ac Merge remote-tracking branch 'upstream/master'
# Conflicts:
#	plugin.xml
2016-01-15 15:51:18 +01:00
EddyVerbruggen 577b1704d9 Merge remote-tracking branch 'upstream/master'
# Conflicts:
#	plugin.xml
2016-01-15 15:51:10 +01:00
EddyVerbruggen ac0746b9b9 #65 Success callback not triggered when toast tapped in Android 2016-01-15 13:12:03 +01:00
EddyVerbruggen 7a08e6afd6 sync from upstream 2015-12-08 12:35:44 +01:00
EddyVerbruggen 23d35404b5 sync from upstream 2015-12-08 12:35:37 +01:00
EddyVerbruggen a5cdda3ea2 #20 Click event, now with an optional data object 2015-12-08 12:32:08 +01:00
EddyVerbruggen c5b6060973 upstream update 2015-12-08 12:18:08 +01:00
EddyVerbruggen cabeea18ed upstream update 2015-12-08 12:18:00 +01:00
EddyVerbruggen 335f731da4 #20 Click event 2015-12-08 10:18:20 +01:00
EddyVerbruggen ecb47ab110 #60 Is it possible to change the android theme? Compatibility with older Android versions. 2015-12-01 12:31:41 +01:00
EddyVerbruggen f8887dc45e #60 Is it possible to change the android theme? Compatibility with older Android versions.
#61 Android exception NoSuchMethodError setTint.
2015-12-01 08:59:43 +01:00
EddyVerbruggen 273334664a #60 Is it possible to change the android theme? Version bump. 2015-11-30 09:33:28 +01:00
EddyVerbruggen 6ee16e9e7d #60 Is it possible to change the android theme? 2015-11-29 21:00:46 +01:00
EddyVerbruggen 6e13de8982 Stable ID for AB 2015-10-01 10:34:25 +02:00
EddyVerbruggen 03320113b4 #42 toast not showing on top of InAppBrowser on iOS
#55 Again/Still? Toast won't show in front of InAppBrowser on iOS
2015-09-29 22:36:48 +02:00
Eddy Verbruggen 5634e1d84c typo 2015-09-26 08:05:18 +02:00
EddyVerbruggen 2add9bef7b Cordova id change & version bump 2015-08-27 16:34:49 +02:00
EddyVerbruggen c252764372 #41 npm prep (name was taken) 2015-08-04 22:17:28 +02:00
EddyVerbruggen ba7c60f324 #41 npm prep 2015-08-04 22:09:57 +02:00
13 changed files with 494 additions and 135 deletions
+19
View File
@@ -0,0 +1,19 @@
[The MIT License (MIT)](http://www.opensource.org/licenses/mit-license.html)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+85 -39
View File
@@ -2,6 +2,9 @@
for Android, iOS and WP8, by [Eddy Verbruggen](http://www.x-services.nl/phonegap-toast-plugin/796)
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=eddyverbruggen%40gmail%2ecom&lc=US&item_name=cordova%2dplugin%2dtoast&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted)
If you like this plugin and want to say thanks please send a PR or donation. Both are equally appreciated!
<table width="100%">
<tr>
@@ -19,9 +22,9 @@ for Android, iOS and WP8, by [Eddy Verbruggen](http://www.x-services.nl/phonegap
3. [Manually](#manually)
3. [PhoneGap Build](#phonegap-build)
4. [Usage](#4-usage)
4. [Styling](#styling)
5. [Credits](#5-credits)
6. [Changelog](#6-changelog)
7. [License](#7-license)
## 1. Description
@@ -48,10 +51,18 @@ iOS
![ScreenShot](screenshots/screenshot-ios-toast.png)
A few styling options
![ScreenShot](screenshots/styling-green.png)
![ScreenShot](screenshots/styling-red.png)
Android
![ScreenShot](screenshots/screenshot-android-toast.png)
Windows Phone 8
![ScreenShot](screenshots/screenshot-wp8.jpg)
@@ -63,18 +74,19 @@ Toast is compatible with [Cordova Plugman](https://github.com/apache/cordova-plu
Using the Cordova CLI and the [Cordova Plugin Registry](http://plugins.cordova.io)
```
$ cordova plugin add nl.x-services.plugins.toast
$ cordova plugin add cordova-plugin-x-toast
$ cordova prepare
```
Or using the phonegap CLI
```
$ phonegap local plugin add nl.x-services.plugins.toast
$ phonegap local plugin add cordova-plugin-x-toast
```
Toast.js is brought in automatically. There is no need to change or add anything in your html.
### Manually
You'd better use the CLI, but here goes:
1\. Add the following xml to your `config.xml` in the root directory of your `www` folder:
```xml
@@ -118,11 +130,7 @@ Toast works with PhoneGap build too, but only with PhoneGap 3.0 and up.
Just add the following xml to your `config.xml` to always use the latest version of this plugin:
```xml
<gap:plugin name="nl.x-services.plugins.toast" source="plugins.cordova.io" />
```
or to use a specific version:
```xml
<gap:plugin name="nl.x-services.plugins.toast" source="plugins.cordova.io" version="2.0.6" />
<gap:plugin name="cordova-plugin-x-toast" source="npm" />
```
Toast.js is brought in automatically. There is no need to change or add anything in your html.
@@ -132,7 +140,7 @@ Toast.js is brought in automatically. There is no need to change or add anything
### Showing a Toast
You have two choices to make when showing a Toast: where to show it and for how long.
* show(message, duration, position)
* duration: 'short', 'long'
* duration: 'short', 'long', '3000', 900 (the latter are milliseconds)
* position: 'top', 'center', 'bottom'
You can also use any of these convenience methods:
@@ -154,11 +162,11 @@ You can copy-paste these lines of code for a quick test:
Since 2.1.0 you can add pixels to move the toast up or down.
Note that `showWithOptions` can be used instead of the functions above, but it's not useful unless you want to pass `addPixelsY`.
```js
function showTop() {
function showBottom() {
window.plugins.toast.showWithOptions(
{
message: "hey there",
duration: "short",
duration: "short", // which is 2000 ms. "long" is 4000. Or specify the nr of ms yourself.
position: "bottom",
addPixelsY: -40 // added a negative value to move it up a bit (default 0)
},
@@ -177,7 +185,62 @@ function hide() {
}
```
### WP8 quircks
### Receiving a callback when a Toast is tapped
On iOS and Android the success handler of your `show` function will be notified (again) when the toast was tapped.
So the first time the success handler fires is when the toast is shown, and in case the user taps the toast it will be
called again. You can distinguish between those events of course:
```js
window.plugins.toast.showWithOptions(
{
message: "hey there",
duration: 1500, // ms
position: "bottom",
addPixelsY: -40, // (optional) added a negative value to move it up a bit (default 0)
data: {'foo':'bar'} // (optional) pass in a JSON object here (it will be sent back in the success callback below)
},
// implement the success callback
function(result) {
if (result && result.event) {
console.log("The toast was tapped");
console.log("Event: " + result.event); // will be defined, with a value of "touch" when it was tapped by the user
console.log("Message: " + result.message); // will be equal to the message you passed in
console.log("data.foo: " + result.data.foo); // .. retrieve passed in data here
} else {
console.log("The toast has been shown");
}
}
);
```
### Styling
Since version 2.4.0 you can pass an optional `styling` object to the plugin.
The defaults make sure the Toast looks the same as when you would not pass in the `styling` object at all.
Note that on WP this object is currently ignored.
```js
window.plugins.toast.showWithOptions({
message: "hey there",
duration: "short", // 2000 ms
position: "bottom",
styling: {
opacity: 0.75, // 0.0 (transparent) to 1.0 (opaque). Default 0.8
backgroundColor: '#FF0000', // make sure you use #RRGGBB. Default #333333
textColor: '#FFFF00', // Ditto. Default #FFFFFF
textSize: 20.5, // Default is approx. 13.
cornerRadius: 16, // minimum is 0 (square). iOS default 20, Android default 100
horizontalPadding: 20, // iOS default 16, Android default 50
verticalPadding: 16 // iOS default 12, Android default 30
}
});
```
Tip: if you need to pass different values for iOS and Android you can use fi. the device plugin
to determine the platform and pass `opacity: isAndroid() ? 0.7 : 0.9`.
### WP8 quirks
The WP8 implementation needs a little more work, but it's perfectly useable when you keep this in mind:
* You can't show two Toasts simultaneously.
* Wait until the first Toast is hidden before the second is shown, otherwise the second one will be hidden quickly.
@@ -191,30 +254,13 @@ The Android code was entirely created by me.
For iOS most credits go to this excellent [Toast for iOS project by Charles Scalesse] (https://github.com/scalessec/Toast).
## 6. CHANGELOG
2.0.1: iOS messages are hidden when another one is shown. [Thanks Richie Min!](https://github.com/EddyVerbruggen/Toast-PhoneGap-Plugin/pull/13)
2.0: WP8 support
1.0: initial version supporting Android and iOS
## 7. License
[The MIT License (MIT)](http://www.opensource.org/licenses/mit-license.html)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
- 2.5.1: You can now specify the `textSize` used in the font for iOS and Android.
- 2.5.0: By popular demand: Specify the duration of the Toast on iOS and Android. Pass in `short` (2000ms), `long` (4000ms), or any nr of milliseconds: `900`.
- 2.4.2: You can now also set the Toast `opacity` for iOS.
- 2.4.1: As an addition to 2.4.0, [Sino](https://github.com/SinoBoeckmann) added the option to change the text color!
- 2.4.0: You can now style the Toast with a number of properties. See
- 2.3.2: The click event introduced with 2.3.0 did not work with Android 5+.
- 2.3.0: The plugin will now report back to JS if Toasts were tapped by the user.
- 2.0.1: iOS messages are hidden when another one is shown. [Thanks Richie Min!](https://github.com/EddyVerbruggen/Toast-PhoneGap-Plugin/pull/13)
- 2.0: WP8 support
- 1.0: initial version supporting Android and iOS
+42
View File
@@ -0,0 +1,42 @@
### Expected behaviour
Tell us what should happen.
### Actual behaviour
Tell us what happens instead. Provide a log message if relevant.
### I'm seeing this behaviour on
_Remove this hint: these checkboxes can be checked like this: [x]_
- [ ] iOS device
- [ ] iOS sim
- [ ] Android device
- [ ] Android sim
#### Hardware models
Example: Samsung Galaxy S6, iPhone 6s
#### OS versions
Example: Android 4.4.2, iOS 9.2
### I'm not a dummy, so I've checked these
- [ ] It happens on a fresh Cordova CLI project as well.
- [ ] I'm waiting for `deviceready` to fire.
- [ ] My JavaScript has no errors (`window.onerror` catches nothing).
### So how can we reproduce this?
_Remove this hint: Pick one of these - use the Preview feature of this editor to get a sense which option we like best_
#### Awesome :star::star::star::star::star:
Provide a (link to a) minimal demo app showing the faulty behaviour.
#### Sweet :star::star::star::star:
Provide a concise code sample which can be dropped into a freshly created Cordova CLI app.
#### Good :star::star::star:
Provide your own app and instructions how to reproduce the issue.
#### Meh :star::star:
Provide a code sample with a bunch of magic parameters which I need to interpolate by guessing to reconstruct the actual runtime code.
#### Worst :poop:
Say the source code can't be disclosed and refuse to provide any of the above. Expect this issue to be closed by a bunch of angry aliens :alien::alien::alien::alien::alien: that will hunt you down and :fire: your :computer:. You've been warned. :fire_engine:
+41
View File
@@ -0,0 +1,41 @@
{
"name": "cordova-plugin-x-toast",
"version": "2.5.1",
"description": "This plugin allows you to show a Toast. A Toast is a little non intrusive buttonless popup which automatically disappears.",
"cordova": {
"id": "cordova-plugin-x-toast",
"platforms": [
"ios",
"android",
"wp8",
"blackberry10"
]
},
"repository": {
"type": "git",
"url": "git+https://github.com/EddyVerbruggen/Toast-PhoneGap-Plugin.git"
},
"keywords": [
"Toast",
"Notification",
"Message",
"Alert",
"ecosystem:cordova",
"cordova-ios",
"cordova-android",
"cordova-wp8",
"cordova-blackberry10"
],
"engines": [
{
"name": "cordova",
"version": ">=3.0.0"
}
],
"author": "Eddy Verbruggen <eddyverbruggen@gmail.com> (https://github.com/EddyVerbruggen)",
"license": "MIT",
"bugs": {
"url": "https://github.com/EddyVerbruggen/Toast-PhoneGap-Plugin/issues"
},
"homepage": "https://github.com/EddyVerbruggen/Toast-PhoneGap-Plugin#readme"
}
+2 -2
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="nl.x-services.plugins.toast"
version="2.1.1">
id="cordova-plugin-x-toast"
version="2.5.1">
<name>Toast</name>
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

+161 -26
View File
@@ -1,75 +1,180 @@
package nl.xservices.plugins;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.os.CountDownTimer;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/*
// TODO nice way for the Toast plugin to offer a longer delay than the default short and long options
// TODO also look at https://github.com/JohnPersano/Supertoasts
new CountDownTimer(6000, 1000) {
public void onTick(long millisUntilFinished) {toast.show();}
public void onFinish() {toast.show();}
}.start();
Also, check https://github.com/JohnPersano/SuperToasts
*/
public class Toast extends CordovaPlugin {
private static final String ACTION_SHOW_EVENT = "show";
private static final String ACTION_HIDE_EVENT = "hide";
private static final int GRAVITY_TOP = Gravity.TOP|Gravity.CENTER_HORIZONTAL;
private static final int GRAVITY_CENTER = Gravity.CENTER_VERTICAL|Gravity.CENTER_HORIZONTAL;
private static final int GRAVITY_BOTTOM = Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL;
private static final int BASE_TOP_BOTTOM_OFFSET = 20;
private android.widget.Toast mostRecentToast;
private ViewGroup viewGroup;
// note that webView.isPaused() is not Xwalk compatible, so tracking it poor-man style
private boolean isPaused;
private static CountDownTimer _timer;
@Override
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
if (ACTION_HIDE_EVENT.equals(action)) {
if (mostRecentToast != null) {
mostRecentToast.cancel();
}
hide();
callbackContext.success();
return true;
} else if (ACTION_SHOW_EVENT.equals(action)) {
if (this.isPaused) {
return true;
}
final JSONObject options = args.getJSONObject(0);
final String message = options.getString("message");
final String duration = options.getString("duration");
final String position = options.getString("position");
final int addPixelsY = options.has("addPixelsY") ? options.getInt("addPixelsY") : 0;
final JSONObject data = options.has("data") ? options.getJSONObject("data") : null;
final JSONObject styling = options.optJSONObject("styling");
cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
android.widget.Toast toast = android.widget.Toast.makeText(
cordova.getActivity().getApplicationContext(),
int hideAfterMs;
if ("short".equalsIgnoreCase(duration)) {
hideAfterMs = 2000;
} else if ("long".equalsIgnoreCase(duration)) {
hideAfterMs = 4000;
} else {
// assuming a number of ms
hideAfterMs = Integer.parseInt(duration);
}
final android.widget.Toast toast = android.widget.Toast.makeText(
Build.VERSION.SDK_INT >= 21 ? cordova.getActivity().getWindow().getContext() : cordova.getActivity().getApplicationContext(),
message,
"short".equals(duration) ? android.widget.Toast.LENGTH_SHORT : android.widget.Toast.LENGTH_LONG);
android.widget.Toast.LENGTH_LONG // actually controlled by a timer further down
);
if ("top".equals(position)) {
toast.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL, 0, 20 + addPixelsY);
toast.setGravity(GRAVITY_TOP, 0, BASE_TOP_BOTTOM_OFFSET + addPixelsY);
} else if ("bottom".equals(position)) {
toast.setGravity(Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL, 0, 20 - addPixelsY);
toast.setGravity(GRAVITY_BOTTOM, 0, BASE_TOP_BOTTOM_OFFSET - addPixelsY);
} else if ("center".equals(position)) {
toast.setGravity(Gravity.CENTER_VERTICAL|Gravity.CENTER_HORIZONTAL, 0, addPixelsY);
toast.setGravity(GRAVITY_CENTER, 0, addPixelsY);
} else {
callbackContext.error("invalid position. valid options are 'top', 'center' and 'bottom'");
return;
}
toast.show();
// if one of the custom layout options have been passed in, draw our own shape
if (styling != null && Build.VERSION.SDK_INT >= 16) {
// the defaults mimic the default toast as close as possible
final String backgroundColor = styling.optString("backgroundColor", "#333333");
final String textColor = styling.optString("textColor", "#ffffff");
final Double textSize = styling.optDouble("textSize", -1);
final double opacity = styling.optDouble("opacity", 0.8);
final int cornerRadius = styling.optInt("cornerRadius", 100);
final int horizontalPadding = styling.optInt("horizontalPadding", 50);
final int verticalPadding = styling.optInt("verticalPadding", 30);
GradientDrawable shape = new GradientDrawable();
shape.setCornerRadius(cornerRadius);
shape.setAlpha((int)(opacity * 255)); // 0-255, where 0 is an invisible background
shape.setColor(Color.parseColor(backgroundColor));
toast.getView().setBackground(shape);
final TextView toastTextView;
toastTextView = (TextView) toast.getView().findViewById(android.R.id.message);
toastTextView.setTextColor(Color.parseColor(textColor));
if (textSize > -1) {
toastTextView.setTextSize(textSize.floatValue());
}
toast.getView().setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
// this gives the toast a very subtle shadow on newer devices
if (Build.VERSION.SDK_INT >= 21) {
toast.getView().setElevation(6);
}
}
// On newer Android devices you can no longer rely on the 'toast.getView().setOnTouchListener',
// so created something funky that compares the Toast position to the tap coordinates.
getViewGroup().setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() != MotionEvent.ACTION_DOWN) {
return false;
}
if (mostRecentToast == null || !mostRecentToast.getView().isShown()) {
getViewGroup().setOnTouchListener(null);
return false;
}
float w = mostRecentToast.getView().getWidth();
float startX = (view.getWidth() / 2) - (w / 2);
float endX = (view.getWidth() / 2) + (w / 2);
float startY;
float endY;
float g = mostRecentToast.getGravity();
float y = mostRecentToast.getYOffset();
float h = mostRecentToast.getView().getHeight();
if (g == GRAVITY_BOTTOM) {
startY = view.getHeight() - y - h;
endY = view.getHeight() - y;
} else if (g == GRAVITY_CENTER) {
startY = (view.getHeight() / 2) + y - (h / 2);
endY = (view.getHeight() / 2) + y + (h / 2);
} else {
// top
startY = y;
endY = y + h;
}
float tapX = motionEvent.getX();
float tapY = motionEvent.getY();
final boolean tapped = tapX >= startX && tapX <= endX &&
tapY >= startY && tapY <= endY;
return tapped && returnTapEvent(message, data, callbackContext);
}
});
// trigger show every 2500 ms for as long as the requested duration
_timer = new CountDownTimer(hideAfterMs, 2500) {
public void onTick(long millisUntilFinished) {toast.show();}
public void onFinish() {toast.cancel();}
}.start();
mostRecentToast = toast;
callbackContext.success();
toast.show();
PluginResult pr = new PluginResult(PluginResult.Status.OK);
pr.setKeepCallback(true);
callbackContext.sendPluginResult(pr);
}
});
@@ -80,11 +185,41 @@ public class Toast extends CordovaPlugin {
}
}
@Override
public void onPause(boolean multitasking) {
private void hide() {
if (mostRecentToast != null) {
mostRecentToast.cancel();
getViewGroup().setOnTouchListener(null);
}
if (_timer != null) {
_timer.cancel();
}
}
private boolean returnTapEvent(String message, JSONObject data, CallbackContext callbackContext) {
final JSONObject json = new JSONObject();
try {
json.put("event", "touch");
json.put("message", message);
json.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
callbackContext.success(json);
hide();
return true;
}
// lazy init and caching
private ViewGroup getViewGroup() {
if (viewGroup == null) {
viewGroup = (ViewGroup) ((ViewGroup) cordova.getActivity().findViewById(android.R.id.content)).getChildAt(0);
}
return viewGroup;
}
@Override
public void onPause(boolean multitasking) {
hide();
this.isPaused = true;
}
@@ -92,4 +227,4 @@ public class Toast extends CordovaPlugin {
public void onResume(boolean multitasking) {
this.isPaused = false;
}
}
}
+2 -1
View File
@@ -1,11 +1,12 @@
#import <Foundation/Foundation.h>
#import <Cordova/CDV.h>
@interface UIView (Toast)
// each makeToast method creates a view and displays it as toast
- (void)makeToast:(NSString *)message;
- (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position;
- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position addPixelsY:(int)addPixelsY;
- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position addPixelsY:(int)addPixelsY data:(NSDictionary*)data styling:(NSDictionary*)styling commandDelegate:(id <CDVCommandDelegate>)commandDelegate callbackId:(NSString *)callbackId;
- (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position image:(UIImage *)image;
- (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position title:(NSString *)title;
- (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position title:(NSString *)title image:(UIImage *)image;
+107 -40
View File
@@ -48,6 +48,13 @@ static const NSString * CSToastActivityViewKey = @"CSToastActivityViewKey";
static UIView *prevToast = NULL;
// doesn't matter these are static
static id commandDelegate;
static id callbackId;
static id msg;
static id data;
static id styling;
@interface UIView (ToastPrivate)
- (void)hideToast:(UIView *)toast;
@@ -56,12 +63,12 @@ static UIView *prevToast = NULL;
- (CGPoint)centerPointForPosition:(id)position withToast:(UIView *)toast withAddedPixelsY:(int) addPixelsY;
- (UIView *)viewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image;
- (CGSize)sizeForString:(NSString *)string font:(UIFont *)font constrainedToSize:(CGSize)constrainedSize lineBreakMode:(NSLineBreakMode)lineBreakMode;
@end
@implementation UIView (Toast)
#pragma mark - Toast Methods
- (void)makeToast:(NSString *)message {
@@ -73,24 +80,37 @@ static UIView *prevToast = NULL;
[self showToast:toast duration:duration position:position];
}
- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position addPixelsY:(int)addPixelsY {
UIView *toast = [self viewForMessage:message title:nil image:nil];
[self showToast:toast duration:duration position:position addedPixelsY:addPixelsY];
- (void)makeToast:(NSString *)message
duration:(NSTimeInterval)duration
position:(id)position addPixelsY:(int)addPixelsY
data:(NSDictionary*)_data
styling:(NSDictionary*)_styling
commandDelegate:(id <CDVCommandDelegate>)_commandDelegate
callbackId:(NSString *)_callbackId {
commandDelegate = _commandDelegate;
callbackId = _callbackId;
msg = message;
data = _data;
styling = _styling;
UIView *toast = [self viewForMessage:message title:nil image:nil];
[self showToast:toast duration:duration position:position addedPixelsY:addPixelsY];
}
- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position title:(NSString *)title {
UIView *toast = [self viewForMessage:message title:title image:nil];
[self showToast:toast duration:duration position:position];
[self showToast:toast duration:duration position:position];
}
- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position image:(UIImage *)image {
UIView *toast = [self viewForMessage:message title:nil image:image];
[self showToast:toast duration:duration position:position];
[self showToast:toast duration:duration position:position];
}
- (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position title:(NSString *)title image:(UIImage *)image {
UIView *toast = [self viewForMessage:message title:title image:image];
[self showToast:toast duration:duration position:position];
[self showToast:toast duration:duration position:position];
}
- (void)showToast:(UIView *)toast {
@@ -101,38 +121,47 @@ static UIView *prevToast = NULL;
[self showToast:toast duration:CSToastDefaultDuration position:CSToastDefaultPosition addedPixelsY:0];
}
- (void)showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)point addedPixelsY:(int) addPixelsY {
- (void)showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)point addedPixelsY:(int) addPixelsY {
[self hideToast];
prevToast = toast;
toast.center = [self centerPointForPosition:point withToast:toast withAddedPixelsY:addPixelsY];
toast.alpha = 0.0;
// note that we changed this to be always true
if (CSToastHidesOnTap) {
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:toast action:@selector(handleToastTapped:)];
[toast addGestureRecognizer:recognizer];
toast.userInteractionEnabled = YES;
toast.exclusiveTouch = YES;
}
if ([[[UIDevice currentDevice] systemVersion] compare:@"8.0" options:NSNumericSearch] != NSOrderedAscending) {
// on iOS8 when InAppBrowser is active, the Toast is below it,
[self.superview.superview addSubview:toast];
} else {
// ..on iOS7 however with this fix on landscape the Toast isn't rotated automatically
[self.superview addSubview:toast];
}
// make sure that if InAppBrowser is active, we're still showing Toasts on top of it
UIViewController *vc = [self getTopMostViewController];
UIView *v = [vc view];
[v addSubview:toast];
NSNumber * opacity = styling[@"opacity"];
CGFloat theOpacity = opacity == nil ? CSToastOpacity : [opacity floatValue];
[UIView animateWithDuration:CSToastFadeDuration
delay:0.0
options:(UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction)
animations:^{
toast.alpha = CSToastOpacity;
toast.alpha = theOpacity;
} completion:^(BOOL finished) {
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:duration target:self selector:@selector(toastTimerDidFinish:) userInfo:toast repeats:NO];
// associate the timer with the toast view
objc_setAssociatedObject (toast, &CSToastTimerKey, timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}];
}
- (UIViewController*) getTopMostViewController {
UIViewController *presentingViewController = [[[UIApplication sharedApplication] delegate] window].rootViewController;
while (presentingViewController.presentedViewController != nil) {
presentingViewController = presentingViewController.presentedViewController;
}
return presentingViewController;
}
- (void)hideToast {
@@ -163,6 +192,15 @@ static UIView *prevToast = NULL;
[timer invalidate];
[self hideToast:recognizer.view];
// also send an event back to JS
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:msg, @"message", @"touch", @"event", nil];
if (data != nil) {
[dict setObject:data forKey:@"data"];
}
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dict];
[commandDelegate sendPluginResult:pluginResult callbackId:callbackId];
}
#pragma mark - Toast Activity Methods
@@ -175,31 +213,31 @@ static UIView *prevToast = NULL;
// sanity
UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey);
if (existingActivityView != nil) return;
UIView *activityView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CSToastActivityWidth, CSToastActivityHeight)];
activityView.center = [self centerPointForPosition:position withToast:activityView withAddedPixelsY:0];
activityView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:CSToastOpacity];
activityView.alpha = 0.0;
activityView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin);
activityView.layer.cornerRadius = CSToastCornerRadius;
if (CSToastDisplayShadow) {
activityView.layer.shadowColor = [UIColor blackColor].CGColor;
activityView.layer.shadowOpacity = CSToastShadowOpacity;
activityView.layer.shadowRadius = CSToastShadowRadius;
activityView.layer.shadowOffset = CSToastShadowOffset;
}
UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
activityIndicatorView.center = CGPointMake(activityView.bounds.size.width / 2, activityView.bounds.size.height / 2);
[activityView addSubview:activityIndicatorView];
[activityIndicatorView startAnimating];
// associate the activity view with self
objc_setAssociatedObject (self, &CSToastActivityViewKey, activityView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self addSubview:activityView];
[UIView animateWithDuration:CSToastFadeDuration
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
@@ -238,7 +276,7 @@ static UIView *prevToast = NULL;
} else if ([point isKindOfClass:[NSValue class]]) {
return [point CGPointValue];
}
NSLog(@"Warning: Invalid position for toast.");
return [self centerPointForPosition:CSToastDefaultPosition withToast:toast withAddedPixelsY:addPixelsY];
}
@@ -270,7 +308,9 @@ static UIView *prevToast = NULL;
// create the parent view
UIView *wrapperView = [[UIView alloc] init];
wrapperView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin);
wrapperView.layer.cornerRadius = CSToastCornerRadius;
NSNumber * cornerRadius = styling[@"cornerRadius"];
wrapperView.layer.cornerRadius = cornerRadius == nil ? CSToastCornerRadius : [cornerRadius floatValue];
if (CSToastDisplayShadow) {
wrapperView.layer.shadowColor = [UIColor blackColor].CGColor;
@@ -279,12 +319,24 @@ static UIView *prevToast = NULL;
wrapperView.layer.shadowOffset = CSToastShadowOffset;
}
wrapperView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:CSToastOpacity];
NSString * backgroundColor = styling[@"backgroundColor"];
UIColor *theColor = backgroundColor == nil ? [UIColor blackColor] : [self colorFromHexString:backgroundColor];
NSNumber * horizontalPadding = styling[@"horizontalPadding"];
CGFloat theHorizontalPadding = horizontalPadding == nil ? CSToastHorizontalPadding : [horizontalPadding floatValue];
NSNumber * verticalPadding = styling[@"verticalPadding"];
CGFloat theVerticalPadding = verticalPadding == nil ? CSToastVerticalPadding : [verticalPadding floatValue];
NSNumber * textSize = styling[@"textSize"];
CGFloat theTextSize = textSize == nil ? CSToastFontSize : [textSize floatValue];
wrapperView.backgroundColor = theColor;
if(image != nil) {
imageView = [[UIImageView alloc] initWithImage:image];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.frame = CGRectMake(CSToastHorizontalPadding, CSToastVerticalPadding, CSToastImageViewWidth, CSToastImageViewHeight);
imageView.frame = CGRectMake(theHorizontalPadding, theVerticalPadding, CSToastImageViewWidth, CSToastImageViewHeight);
}
CGFloat imageWidth, imageHeight, imageLeft;
@@ -293,18 +345,21 @@ static UIView *prevToast = NULL;
if(imageView != nil) {
imageWidth = imageView.bounds.size.width;
imageHeight = imageView.bounds.size.height;
imageLeft = CSToastHorizontalPadding;
imageLeft = theHorizontalPadding;
} else {
imageWidth = imageHeight = imageLeft = 0.0;
}
if (title != nil) {
NSString * titleLabelTextColor = styling[@"textColor"];
UIColor *theTitleLabelTextColor = titleLabelTextColor == nil ? [UIColor whiteColor] : [self colorFromHexString:titleLabelTextColor];
titleLabel = [[UILabel alloc] init];
titleLabel.numberOfLines = CSToastMaxTitleLines;
titleLabel.font = [UIFont boldSystemFontOfSize:CSToastFontSize];
titleLabel.font = [UIFont boldSystemFontOfSize:theTextSize];
titleLabel.textAlignment = NSTextAlignmentLeft;
titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
titleLabel.textColor = [UIColor whiteColor];
titleLabel.textColor = theTitleLabelTextColor;
titleLabel.backgroundColor = [UIColor clearColor];
titleLabel.alpha = 1.0;
titleLabel.text = title;
@@ -316,11 +371,14 @@ static UIView *prevToast = NULL;
}
if (message != nil) {
NSString * messageLabelTextColor = styling[@"textColor"];
UIColor *theMessageLabelTextColor = messageLabelTextColor == nil ? [UIColor whiteColor] : [self colorFromHexString:messageLabelTextColor];
messageLabel = [[UILabel alloc] init];
messageLabel.numberOfLines = CSToastMaxMessageLines;
messageLabel.font = [UIFont systemFontOfSize:CSToastFontSize];
messageLabel.font = [UIFont systemFontOfSize:theTextSize];
messageLabel.lineBreakMode = NSLineBreakByWordWrapping;
messageLabel.textColor = [UIColor whiteColor];
messageLabel.textColor = theMessageLabelTextColor;
messageLabel.backgroundColor = [UIColor clearColor];
messageLabel.alpha = 1.0;
messageLabel.text = message;
@@ -337,8 +395,8 @@ static UIView *prevToast = NULL;
if(titleLabel != nil) {
titleWidth = titleLabel.bounds.size.width;
titleHeight = titleLabel.bounds.size.height;
titleTop = CSToastVerticalPadding;
titleLeft = imageLeft + imageWidth + CSToastHorizontalPadding;
titleTop = theVerticalPadding;
titleLeft = imageLeft + imageWidth + theHorizontalPadding;
} else {
titleWidth = titleHeight = titleTop = titleLeft = 0.0;
}
@@ -349,8 +407,8 @@ static UIView *prevToast = NULL;
if(messageLabel != nil) {
messageWidth = messageLabel.bounds.size.width;
messageHeight = messageLabel.bounds.size.height;
messageLeft = imageLeft + imageWidth + CSToastHorizontalPadding;
messageTop = titleTop + titleHeight + CSToastVerticalPadding;
messageLeft = imageLeft + imageWidth + theHorizontalPadding;
messageTop = titleTop + titleHeight + theVerticalPadding;
} else {
messageWidth = messageHeight = messageLeft = messageTop = 0.0;
}
@@ -359,8 +417,8 @@ static UIView *prevToast = NULL;
CGFloat longerLeft = MAX(titleLeft, messageLeft);
// wrapper width uses the longerWidth or the image width, whatever is larger. same logic applies to the wrapper height
CGFloat wrapperWidth = MAX((imageWidth + (CSToastHorizontalPadding * 2)), (longerLeft + longerWidth + CSToastHorizontalPadding));
CGFloat wrapperHeight = MAX((messageTop + messageHeight + CSToastVerticalPadding), (imageHeight + (CSToastVerticalPadding * 2)));
CGFloat wrapperWidth = MAX((imageWidth + (theHorizontalPadding * 2)), (longerLeft + longerWidth + theHorizontalPadding));
CGFloat wrapperHeight = MAX((messageTop + messageHeight + theVerticalPadding), (imageHeight + (theVerticalPadding * 2)));
wrapperView.frame = CGRectMake(0.0, 0.0, wrapperWidth, wrapperHeight);
@@ -381,4 +439,13 @@ static UIView *prevToast = NULL;
return wrapperView;
}
// Assumes input like "#00FF00" (#RRGGBB)
- (UIColor*) colorFromHexString:(NSString*) hexString {
unsigned rgbValue = 0;
NSScanner *scanner = [NSScanner scannerWithString:hexString];
[scanner setScanLocation:1]; // bypass '#' character
[scanner scanHexInt:&rgbValue];
return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16) / 255.0 green:((rgbValue & 0xFF00) >> 8) / 255.0 blue:(rgbValue & 0xFF) / 255.0 alpha:1.0];
}
@end
+27 -21
View File
@@ -5,40 +5,46 @@
@implementation Toast
- (void)show:(CDVInvokedUrlCommand*)command {
NSDictionary* options = [command.arguments objectAtIndex:0];
NSString *message = [options objectForKey:@"message"];
NSString *duration = [options objectForKey:@"duration"];
NSString *position = [options objectForKey:@"position"];
NSNumber *addPixelsY = [options objectForKey:@"addPixelsY"];
NSDictionary* options = [command argumentAtIndex:0];
NSString *message = options[@"message"];
NSString *duration = options[@"duration"];
NSString *position = options[@"position"];
NSDictionary *data = options[@"data"];
NSNumber *addPixelsY = options[@"addPixelsY"];
NSDictionary *styling = options[@"styling"];
if (![position isEqual: @"top"] && ![position isEqual: @"center"] && ![position isEqual: @"bottom"]) {
CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"invalid position. valid options are 'top', 'center' and 'bottom'"];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}
NSInteger durationInt;
if ([duration isEqual: @"short"]) {
durationInt = 2;
} else if ([duration isEqual: @"long"]) {
durationInt = 5;
NSTimeInterval durationMS;
if ([duration.lowercaseString isEqualToString: @"short"]) {
durationMS = 2000;
} else if ([duration.lowercaseString isEqualToString: @"long"]) {
durationMS = 4000;
} else {
CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"invalid duration. valid options are 'short' and 'long'"];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
durationMS = [duration intValue];
}
[self.webView makeToast:message duration:durationInt position:position addPixelsY:addPixelsY == nil ? 0 : [addPixelsY intValue]];
[self.webView makeToast:message
duration:durationMS / 1000
position:position
addPixelsY:addPixelsY == nil ? 0 : [addPixelsY intValue]
data:data
styling:styling
commandDelegate:self.commandDelegate
callbackId:command.callbackId];
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
pluginResult.keepCallback = [NSNumber numberWithBool:YES];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void)hide:(CDVInvokedUrlCommand*)command {
[self.webView hideToast];
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
+2 -2
View File
@@ -126,10 +126,10 @@ namespace WPCordovaClassLib.Cordova.Commands
return;
}
int hideDelay = 2800;
int hideDelay = 2500;
if ("long".Equals(duration))
{
hideDelay = 5500;
hideDelay = 5000;
}
else if (!"short".Equals(duration))
{
+6 -4
View File
@@ -11,12 +11,12 @@ Toast.prototype.optionsBuilder = function () {
return {
withMessage: function(m) {
message = m;
message = m.toString();
return this;
},
withDuration: function(d) {
duration = d;
duration = d.toString();
return this;
},
@@ -36,13 +36,15 @@ Toast.prototype.optionsBuilder = function () {
duration: duration,
position: position,
addPixelsY: addPixelsY
}
};
}
}
};
};
Toast.prototype.showWithOptions = function (options, successCallback, errorCallback) {
options.duration = (options.duration === undefined ? 'long' : options.duration.toString());
options.message = options.message.toString();
cordova.exec(successCallback, errorCallback, "Toast", "show", [options]);
};