Google changed his GCM (Google Cloud Messaging) to FCM (Firebase Cloud Messaging). What should I do to integrate it with Appcelerator app?
I found a lot of modules in marketplace but I think they only work with GCM.
Has anyone tried to combine it?
I have this currently working with FCM and a module called Ti.Goosh. It works really well and I can send push notifications to web browsers and Android devices with it. https://github.com/caffeinalab/ti.goosh
create an html with some Firebase functions, in my case we need to find some vehicles on a Geo radio
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Firebase</title>
<!-- Firebase -->
<script src="firebase.js"></script>
<!-- GeoFire -->
<script src="geofire.min.js"></script>
</head>
<body style="margin:0;padding:0;border:0;">
<script type="text/javascript">
var markers = new Array();
var markersArray = [];
var driverInvitations;
var diverInvited;
Ti.App.addEventListener('firebase:init', function(e) {
Ti.API.info('Geofire init');
Ti.API.info(e);
Ti.API.info('latitude :'+e.latitude);
Ti.API.info('longitude :'+e.longitude);
var latitude = parseFloat(e.latitude);
var longitude = parseFloat(e.longitude);
// Set the center
var center = [latitude, longitude];
// Query radius
var radiusInKm = 0;
// Get a reference to the Firebase public transit open data set
var transitFirebaseRef = new Firebase("https://xxx.firebaseio.com/");
// Create a new GeoFire instance, pulling data from the public transit data
var geoFire = new GeoFire(transitFirebaseRef.child("_geofire"));
/*************/
/* GEOQUERY */
/*************/
// Keep track of all of the vehicles currently within the query
var vehiclesInQuery = {};
// Create a new GeoQuery instance
var geoQuery = geoFire.query({
center : center,
radius : radiusInKm
});
geoQuery.on("ready", function() {
Ti.API.info("GeoQuery has loaded and fired all other events for initial data");
Ti.App.fireEvent('fbController:selectDriver');
});
/* Adds new vehicle markers to the map when they enter the query */
geoQuery.on("key_entered", function(vehicleId, vehicleLocation) {
Ti.API.info('Geofire enter driver, id ' + vehicleId + ', on location ' + vehicleLocation);
var latitude = vehicleLocation[0];
var longitude = vehicleLocation[1];
Ti.App.fireEvent('fbController:addMarker', {
id : vehicleId,
latitude : latitude,
longitude : longitude
});
});
/* Moves vehicles markers on the map when their location within the query changes */
geoQuery.on("key_moved", function(vehicleId, vehicleLocation) {
Ti.API.info('Geofire move driver, id ' + vehicleId + ', moved to : ' + vehicleLocation);
var latitude = vehicleLocation[0];
var longitude = vehicleLocation[1];
Ti.App.fireEvent('fbController:moveMarker', {
id : vehicleId,
latitude : latitude,
longitude : longitude
});
});
/* Removes vehicle markers from the map when they exit the query */
geoQuery.on("key_exited", function(vehicleId) {
Ti.API.info('Geofire exited driver, id ' + vehicleId);
Ti.App.fireEvent('fbController:removeMarker', {
id : vehicleId
});
});
});
</script>
</body>
</html>
then on the View window we add the html to the container view
var webview = Ti.UI.createWebView({
url: Ti.Filesystem.resourcesDirectory + '/webview/firebase/index.html',
width: 0,
height: 0,
top: 0,
left: 0,
visible: false
});
webview.addEventListener('load', function() {
Ti.App.fireEvent('firebase:init', {
latitude : latitude,
longitude : longitude
});
});
$.container.add(webview);
create some events to communicate with the Firebase Webview
var Map = require('ti.map');
var mapview;
var annotations = [];
Ti.App.addEventListener('fbController:addMarker', function(e) {
Ti.API.info('Firebase add marker: ' + e.id);
annotations[e.id] = Map.createAnnotation({
latitude: e.latitude,
longitude: e.longitude,
image: '/images/icon-car.png'
});
if (Ti.App.Properties.getBool('locationEnabled') == true) {
mapview.addAnnotation(annotations[e.id]);
} else {
}
});
Ti.App.addEventListener('fbController:moveMarker', function(e) {
Ti.API.info('Firebase move marker: ' + e.id);
annotations[e.id].setLatitude(e.latitude);
annotations[e.id].setLongitude(e.longitude);
});
Ti.App.addEventListener('fbController:removeMarker', function(e) {
Ti.API.info('Firebase remove marker: ' + e.id);
if (Ti.App.Properties.getBool('locationEnabled') == true) {
mapview.removeAnnotation(annotations[e.id]);
delete annotations[e.id];
}
});
Anything you need can ask me, I am using this on an App and is working good, try to improving everyday but with this setup we did the job :), hope this helps, greetings
I do it as this way in my Android app:
Install module ln.vanvianen.android.gcm
Create instance in alloy.js: var FCM = require('actions/gcm');// pub/sub
In app/lib/actions/gcm.js create instance of module and logic to interact with fcm.
As I use promises on gcm.js, link bluebird library in alloy.js: var Promise = require('bluebird/bluebird');
Run my app: appc run -p android -T device
To test run CURL command or use firebase notification console: https://console.firebase.google.com/project/your-project-name/notification
Firebase Notification Console example
Works for me with: sdk-version 5.5.0.GA
Related
On the same page, i have differents maps loaded with Here maps API for each map i have loaded a specific kml file.
When i try to click, it works only on the last kml loaded and not others one, so how to make working event on each map ? This my code, it's taken from the example but a little bit modified :
function renderSchoenefeldAirport(map, ui, renderControls, kmlfile) {
// Create a reader object, that will load data from a KML file
var reader = new H.data.kml.Reader(kmlfile);
// Request document parsing. Parsing is an asynchronous operation.
reader.parse();
reader.addEventListener('statechange', function () {
// Wait till the KML document is fully loaded and parsed
if (this.getState() === H.data.AbstractReader.State.READY) {
var parsedObjects = reader.getParsedObjects();
// Create a group from our objects to easily zoom to them
var container = new H.map.Group({objects: parsedObjects});
// So let's zoom to them by default
map.setViewBounds(parsedObjects[0].getBounds());
// Let's make kml ballon visible by tap on its owner
// Notice how we are using event delegation for it
container.addEventListener('tap', function (evt) {
var target = evt.target, position;
// We need to calculated a position for our baloon
if (target instanceof H.map.Polygon || target instanceof H.map.Polyline) {
position = target.getBounds().getCenter();
} else if (target instanceof H.map.Marker) {
position = target.getPosition();
}
if (position) {
// Let's use out custom (non-api) function for displaying a baloon
showKMLBallon(position, target.getData(), ui);
}
});
// Make objects visible by adding them to the map
map.addObject(container);
}
});
}
/**
* Boilerplate map initialization code starts below:
*/
// Step 1: initialize communication with the platform
var platform = new H.service.Platform({
'app_id': 'myappid',
'app_code': 'myappcode',
useHTTPS: true
});
var pixelRatio = window.devicePixelRatio || 1;
var defaultLayers = platform.createDefaultLayers({
tileSize: pixelRatio === 1 ? 256 : 512,
ppi: pixelRatio === 1 ? undefined : 320
});
// Step 2: initialize a map
// Please note, that default layer is set to satellite mode
var map = new H.Map(document.getElementById('mapcontainer1'), defaultLayers.satellite.map, {
zoom: 1,
pixelRatio: pixelRatio
});
var map_secondary = new H.Map(document.getElementById('mapcontainer2'), defaultLayers.satellite.map, {
zoom: 1,
pixelRatio: pixelRatio
});
// Step 3: make the map interactive
// MapEvents enables the event system
// Behavior implements default interactions for pan/zoom (also on mobile touch environments)
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
var behavior_secondary = new H.mapevents.Behavior(new H.mapevents.MapEvents(map_secondary));
// Template function for our controls
function renderControls(buttons) {
var containerNode = document.createElement('div');
containerNode.setAttribute('style', 'position:absolute;top:0;left:0;background-color:#fff; padding:10px;');
containerNode.className = "btn-group";
Object.keys(buttons).forEach(function (label) {
var input = document.createElement('input');
input.value = label;
input.type = 'button';
input.onclick = buttons[label];
input.className="btn btn-sm btn-default"
containerNode.appendChild(input);
});
map.getElement().appendChild(containerNode);
}
function showKMLBallon(position, data, ui) {
var content = data.balloonStyle.text;
if (content) {
// Styling of the balloon text.
// The only supported wilde cards are $[text] and $[description].
content = content
.replace('$[name]', data.name || '')
.replace('$[description]', data.description || '');
// Note how we are caching our infoBubble instance
// We create InfoBubble object only once and then reuse it
var bubble = showKMLBallon.infoBubble;
if (!bubble) {
bubble = new H.ui.InfoBubble(position, {content: content});
ui.addBubble(bubble);
bubble.getContentElement().style.marginRight = "-24px";
// Cache our instance for future use
showKMLBallon.infoBubble = bubble;
} else {
bubble.setPosition(position);
bubble.setContent(content);
bubble.open();
}
}
}
// Step 4: create the default UI component, for displaying bubbles
var ui = H.ui.UI.createDefault(map, defaultLayers);
var ui_secondary = H.ui.UI.createDefault(map_secondary, defaultLayers);
// Step 5: main logic goes here
renderSchoenefeldAirport(map, ui, renderControls, 'path/to/file1.kml');
renderSchoenefeldAirport(map_secondary, ui_secondary, renderControls, 'path/to/file2.kml');
Thanks by advance
In the provided snippet there is a line 106: var bubble = showKMLBallon.infoBubble; where the info bubble is "cached", the problem is that when the user clicks on one of the maps the infobubble is created and cached, and when somebody clicks on the second map the the info bubble from the first is used.
In the simplest case this line should be:
var bubble = ui.infoBubble;
so the bubble for each map instance in cached. In general, depending on the desired outcome, the proper caching strategy should be devised.
Hope this helps.
I am using the geofire library with angularjs. I noticed something odd happening when writing out a basic geolocation based query. Upon a state change, the page displays blank and the query doesn't appear to execute. However, if I ctrl + f5, the data displays and query executes.
I wondering why this is happening. I've included my controller code below. Any help is appreciated, I've been trying numerous things (tried updating user's geolocation to force an event to get triggered, $timeout, etc).
'use strict';
angular
.module('m02-discover')
.controller('DiscoverController', [
'$scope', '$state', 'fbRef', 'fbGeo', '$geofire', 'Discover', '$timeout',
function($scope, $state, fbRef, fbGeo, $geofire, Discover, $timeout) {
var ref = new Firebase(fbRef);
var geoRef = new Firebase(fbRef + 'geofire/');
var $geo = $geofire(geoRef);
$scope.searchResults = [];
var query = $geo.$query({
center: [37.68465, -122.1420265],
radius: 10
});
// Setup Angular Broadcast event for when an object enters our query
var geoQueryCallback = query.on('key_entered', 'SEARCH:KEY_ENTERED');
// Listen for Angular Broadcast
$scope.$on('SEARCH:KEY_ENTERED', function (event, key, location, distance) {
// Do something interesting with object
ref.child('users').child(key).once("value", function(snapshot) {
var user = snapshot.val();
$scope.$apply(function(){
$scope.searchResults.push(user);
});
});
// Cancel the query if the distance is > 50 km
if(distance > 50) {
geoQueryCallback.cancel();
}
});
$scope.favoriteUser = function(favoritedID, favoritedTwitterID){
Discover.favoriteUser(favoritedID, favoritedTwitterID);
}
}
]);
I am using the Google Analytics Embed API. Below is the code example that I'm working with from Google's Development page. Is there a way to set the defaults for the Selector? Account | Property | View
<!doctype html>
<html lang="en">
<head>
<title>Google Charts</title>
<script>
(function(w,d,s,g,js,fs){
g=w.gapi||(w.gapi={});g.analytics={q:[],ready:function(f){this.q.push(f);}};
js=d.createElement(s);fs=d.getElementsByTagName(s)[0];
js.src='https://apis.google.com/js/platform.js';
fs.parentNode.insertBefore(js,fs);js.onload=function(){g.load('analytics');};
}(window,document,'script'));
</script>
<script>
gapi.analytics.ready(function() {
var ACCESS_TOKEN = 'xxxxx'; // obtained from your service account
gapi.analytics.auth.authorize({
serverAuth: {
access_token: ACCESS_TOKEN
}
});
/**
* Create a new ViewSelector instance to be rendered inside of an
* element with the id "view-selector-container".
*/
var viewSelector = new gapi.analytics.ViewSelector({
container: 'view-selector-container'
});
// Render the view selector to the page.
viewSelector.execute();
/**
* Create a new DataChart instance with the given query parameters
* and Google chart options. It will be rendered inside an element
* with the id "chart-container".
*/
var dataChart = new gapi.analytics.googleCharts.DataChart({
query: {
metrics: 'ga:users',
dimensions: 'ga:date',
'start-date': '30daysAgo',
'end-date': 'yesterday'
},
chart: {
container: 'chart-container',
type: 'LINE',
options: {
width: '100%'
}
}
});
/**
* Render the dataChart on the page whenever a new view is selected.
*/
viewSelector.on('change', function(ids) {
dataChart.set({query: {ids: ids}}).execute();
});
});
</script>
</head>
<body>
<div id="embed-api-auth-container"></div>
<div id="chart-container"></div>
<div id="view-selector-container"></div>
</body>
</html>
You first want to find the ids value of the account you want as your default. You do this by simply console logging 'ids' and then choosing the selector in your view-selector-container. This will past a number in your browsers console.
You then need to set the value of 'ids' to this number. You change this in two places, firstly you add it into your query for dataChart. So for example if your ids number was 12345678 then you would write it as shown below ( ids: 'ga:12345678' ):
var dataChart = new gapi.analytics.googleCharts.DataChart({
query: {
ids: 'ga:12345678',
metrics: 'ga:users',
dimensions: 'ga:date',
'start-date': '30daysAgo',
'end-date': 'yesterday'
},
chart: {
container: 'chart-container',
type: 'LINE',
options: {
width: '100%'
}
}
});
You then also need to change the value of ids where you execute dataChart
viewSelector.on('change', function(ids) {
dataChart.set({query: {ids: ids}}).execute();
});
so inside the query the second 'ids' is changed as shown below:
viewSelector.on('change', function(ids) {
dataChart.set({query: {ids: 'ga:12345678'}}).execute();
});
I had the same thought, having the viewSelector on the page actually posed a bit of a security breach. What I ended up doing was creating constants for the singular view that I wanted to see (GID) and the client ID of my console account.
/********************** Constants ****************************/
var GID = {query: {ids:'ga:xxxxxxxxx'}};
var CLIENT_ID = 'xxxxxxxxxx-xxxxxxxxxxxx.apps.googleusercontent.com';
/*************************************************************/
I kept the authorize code
gapi.analytics.auth.authorize({
container: 'auth-button',
clientid: CLIENT_ID,
});
I removed the viewSelector Code
var viewSelector = new gapi.analytics.ViewSelector({
container: 'view-selector'
});
When executing the Datachart objects. I used this code.
sessions.set(GID).execute();
Everything else remains the same.
I was wondering if when coding, there was a way to make it so the users can make it so there are multiple waypoints. Right now, I'm getting an automatic waypoint 4 miles away from the starting waypoint that the users can edit.
This is the code I have:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>
<script type="text/javascript">
var map = null;
var directionsManager;
var directionsErrorEventObj;
var directionsUpdatedEventObj;
function getMap() {
map = new Microsoft.Maps.Map(document.getElementById('myMap'), { credentials: 'myKey' });
}
function createDirectionsManager() {
var displayMessage;
if (!directionsManager) {
directionsManager = new Microsoft.Maps.Directions.DirectionsManager(map);
displayMessage = 'Directions Module loaded\n';
displayMessage += 'Directions Manager loaded';
}
alert(displayMessage);
directionsManager.resetDirections();
directionsErrorEventObj = Microsoft.Maps.Events.addHandler(directionsManager, 'directionsError', function (arg) { alert(arg.message) });
directionsUpdatedEventObj = Microsoft.Maps.Events.addHandler(directionsManager, 'directionsUpdated', function () { alert('Directions updated') });
}
function createDrivingRoute() {
if (!directionsManager) { createDirectionsManager(); }
directionsManager.resetDirections();
// Set Route Mode to driving
{
directionsManager.setRequestOptions({ routeMode: Microsoft.Maps.Directions.RouteMode.driving });
var startWaypoint = new Microsoft.Maps.Directions.Waypoint({ address: '7500 University Dr., Bismarck, ND' });
directionsManager.addWaypoint(startWaypoint);
var destinationWaypoint = new Microsoft.Maps.Directions.Waypoint({ address: document.getElementById('hometown').value });
directionsManager.addWaypoint(destinationWaypoint);
// Set the element in which the itinerary will be rendered
directionsManager.setRenderOptions({ itineraryContainer: document.getElementById('directionsItinerary') });
alert('Calculating directions...');
directionsManager.calculateDirections();
}
// Insert a waypoint
directionsManager.addWaypoint(new Microsoft.Maps.Directions.Waypoint({ address: 'Bismarck, ND'}), 1);
// Set the element in which the itinerary will be rendered
directionsManager.setRenderOptions({ itineraryContainer: document.getElementById('directionsItinerary') });
alert('Calculating directions...');
directionsManager.calculateDirections();
}
function createDirections() {
if (!directionsManager) {
Microsoft.Maps.loadModule('Microsoft.Maps.Directions', { callback: createDrivingRoute });
}
else {
createDrivingRoute();
}
}
</script>
Is there a way I can make it so the user decides when to add a waypoint - so that the waypoint wouldn't be automatic and the user can decide to add multiple waypoints?
Thanks in advance.
Yes. You have to create a UI that handles this rather than automatically choosing random waypoints. Then you just have to use the addWaypoint method to add the waypoint to the directions manager.
I am trying to dynamically set the cluster title, rollover text, of clustered icons. I want the cluster count/total to be used in the rollover text.
Through console.log I am able to see that the title has been changed to that set for var txt. It also works with alert( txt ). The default title for a cluster is "" and does not seem to be getting updated and stays at the default value.
Currently I am setting the title in google.maps.event.addListener( markerClusterer, 'mouseover', function( cluster ) {}).
I'm thinking that my code continues to execute and that might be the reason I don't see the change but I haven't been able to narrow it down.
var latlng = new google.maps.LatLng( lat, lng );
var qs = location.search;
var options = {
zoom: 17,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP,
mapTypeControlOptions: {
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
}
};
map = new google.maps.Map( mapId[0], options );
google.maps.event.addListener( map, 'idle', function() {
var bounds = map.getBounds();
downloadXML( ABSPATH + 'xml/maps/markers.php' + qs + '&bounds=' + bounds, function( data ) {
var xml = parseXml( data );
var markers = xml.documentElement.getElementsByTagName( "marker" );
var markerArray = [];
for ( var i = 0; i < markers.length; i++ ) {
var attributes = getMarkerAttributes( markers[i] );
var marker = createMarker( attributes );
// Add marker to marker array
markerArray.push(marker);
}
// Define the marker clusterer
var clusterOptions = {
zoomOnClick: false,
gridSize: 1
}
var markerClusterer = new MarkerClusterer( map, markerArray, clusterOptions );
// Listen for a cluster to be clicked
google.maps.event.addListener( markerClusterer, 'clusterclick', function( cluster ) {
combineInfoWindows( cluster );
});
// Listen for a cluster to be hovered and set title
google.maps.event.addListener( markerClusterer, 'mouseover', function( cluster ) {
var txt = 'There are ' + cluster.getSize() + ' properties at this location.';
//alert( txt );
console.log( cluster );
markerClusterer.setTitle( txt );
});
}); // downloadXML
}); // google.maps.event.addListener( map, 'idle', ... )
Any help would be greatly appreciated. Thanks!
EDIT: 1
I have a solution based on the suggested solution by Rick.
I have modified the onAdd method.
/**
* Adds the icon to the DOM.
*/
ClusterIcon.prototype.onAdd = function () {
var cClusterIcon = this;
// MY CHANGES - START
this.cluster_.markerClusterer_.title_ = 'There are ' + this.cluster_.getSize() + ' properties at this location.';
// MY CHANGES - END
this.div_ = document.createElement("div");
if (this.visible_) {
this.show();
}
...
};
EDIT: 2 - FINAL SOLUTION
Moved changes to show method versus previous onAdd method as Rick had suggested. Change is made in a file outside of the original source file for MarkerClustererPlus.
/**
* Positions and shows the icon.
*/
ClusterIcon.prototype.show = function () {
if (this.div_) {
var pos = this.getPosFromLatLng_(this.center_);
this.div_.style.cssText = this.createCss(pos);
if (this.cluster_.printable_) {
// (Would like to use "width: inherit;" below, but doesn't work with MSIE)
this.div_.innerHTML = "<img src='" + this.url_ + "'><div style='position: absolute; top: 0px; left: 0px; width: " + this.width_ + "px;'>" + this.sums_.text + "</div>";
} else {
this.div_.innerHTML = this.sums_.text;
}
//this.div_.title = this.cluster_.getMarkerClusterer().getTitle();
// MY SOLUTION BELOW
this.div_.title = 'There are ' + this.cluster_.getSize() + ' properties at this location.';
this.div_.style.display = "";
}
this.visible_ = true;
};
Are you using this for clustering markers? Did you extend it to make your own setTitle function? If not, You'll have to make your own label. It's not actually a marker per se.
Edit: Didn't know this existed.
The cluster icons just pull the title from the MCOptions. I don't see where ClusterIcon or Cluster has a setTitle function, so I'd think the best bet would be overriding the ClusterIcon show prototype yourself and setting it there.
> ClusterIcon.prototype.show =
> function () { if (this.div_) {
> var pos = this.getPosFromLatLng_(this.center_);
> this.div_.style.cssText = this.createCss(pos);
> if (this.cluster_.printable_) {
> // (Would like to use "width: inherit;" below, but doesn't work with MSIE)
> this.div_.innerHTML = "<img src='" + this.url_ + "'><div style='position: absolute; top: 0px; left: 0px; width: " + this.width_
> + "px;'>" + this.sums_.text + "</div>";
> } else {
> this.div_.innerHTML = this.sums_.text;
> }
> this.div_.title = **** Your stuff here ***
> this.div_.style.display = ""; } this.visible_ = true; };
Your problem is that you are trying to assign the mouseover event listener (where you set the title) to a MarkerClusterer, but to define a mouseover listener, you have to pass a Cluster.
There is a MarkerClusterer.getClusters() function that will return an Array of the Cluster instances. You want to loop over that Array and pass an instance of Cluster to your mouseover event listeners. If you check the reference doc and scroll down to the MarkerClusterer Events section of the doc, the row for mouseover defines the Argument to be:
c:Cluster
Which is in contrast to events like clusteringbegin and clusteringend, which define the Argument to be:
mc:MarkerClusterer
Having said all that, I'm not sure there is an easy way to set the title for each Cluster. That class does not have a setTitle function. The setTitle on the MarkerClusterer just applies the title to all of the Cluster instances. And I've double checked in JavaScript; there is no setTitle function on the Cluster class. Your best option right now seems to be to dynamically create the content you want to display within the mouseover handler for each Cluster. You could create an InfoBox and then close it on a Cluster mouseoet event. Not the simplest solution ever, but it will get you where you want to be.
I know this is an old question, but it ranked high on Google for my search. Anyway, here is what I did thanks to tips from this page:
google.maps.event.addListener(markerCluster, 'mouseover', function (c) {
if(c.clusterIcon_.div_){
c.clusterIcon_.div_.title = c.getSize() + ' businesses in this area';
}
});
I can't guarantee it will be compatible with future versions of MarkerClusterer Plus since I'm using "private" properties clusterIcon_ and div_.