Merge branch 'master' into release
This commit is contained in:
commit
18b684c95a
@ -6,6 +6,7 @@
|
||||
// - shards: move between portals (along links) each hour. more than one can be at a portal
|
||||
// - targets: specific portals - one per team
|
||||
// the artifact data includes details for the specific portals, so can be useful
|
||||
// 2014-02-06: intel site updates hint at new 'amar artifacts', likely following the same system as above
|
||||
|
||||
|
||||
window.artifact = function() {}
|
||||
@ -24,9 +25,9 @@ window.artifact.setup = function() {
|
||||
setTimeout (artifact.requestData, 1);
|
||||
|
||||
artifact._layer = new L.LayerGroup();
|
||||
addLayerGroup ('Artifacts (Jarvis shards)', artifact._layer, true);
|
||||
addLayerGroup ('Artifacts', artifact._layer, true);
|
||||
|
||||
$('#toolbox').append(' <a onclick="window.artifact.showArtifactList()" title="Show artifact portal list (jarvis shards and targets)">Artifacts</a>');
|
||||
$('#toolbox').append(' <a onclick="window.artifact.showArtifactList()" title="Show artifact portal list">Artifacts</a>');
|
||||
|
||||
}
|
||||
|
||||
@ -179,6 +180,7 @@ window.artifact.updateLayer = function() {
|
||||
var iconSize = 0;
|
||||
var opacity = 1.0;
|
||||
|
||||
// redundant as of 2014-02-05 - jarvis shards removed
|
||||
if (data.jarvis) {
|
||||
if (data.jarvis.target) {
|
||||
// target portal - show the target marker. use the count of fragments at the target to pick the right icon - it has segments that fill up
|
||||
@ -193,6 +195,22 @@ window.artifact.updateLayer = function() {
|
||||
opacity = 0.6; // these often hide portals - let's make them semi transparent
|
||||
}
|
||||
|
||||
}
|
||||
// 2014-02-06: a guess at whats needed for the new artifacts
|
||||
if (data.amar) {
|
||||
if (data.amar.target) {
|
||||
// target portal - show the target marker. use the count of fragments at the target to pick the right icon - it has segments that fill up
|
||||
|
||||
var count = data.amar.fragments ? data.amar.fragments.length : 0;
|
||||
|
||||
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/amar_shard_target_'+count+'.png';
|
||||
iconSize = 100/2; // 100 pixels - half that size works better
|
||||
} else if (data.amar.fragments) {
|
||||
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/amar_shard.png';
|
||||
iconSize = 60/2; // 60 pixels - half that size works better
|
||||
opacity = 0.6; // these often hide portals - let's make them semi transparent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (iconUrl) {
|
||||
@ -217,15 +235,20 @@ window.artifact.updateLayer = function() {
|
||||
window.artifact.showArtifactList = function() {
|
||||
|
||||
|
||||
var html = '<div><b>Artifact portals</b></div>';
|
||||
var html = '';
|
||||
|
||||
var types = { 'jarvis': 'Jarvis Shards' };
|
||||
var typeNames = { 'jarvis': 'Jarvis Shards', 'amar': 'Amar Artifacts' };
|
||||
|
||||
$.each(types, function(type, name) {
|
||||
if (Object.keys(artifact.artifactTypes).length == 0) {
|
||||
html += '<i>No artifacts at this time</i>';
|
||||
}
|
||||
|
||||
html += '<hr><div><b>'+types[type]+'</b></div>';
|
||||
$.each(artifact.artifactTypes, function(type,type2) {
|
||||
var name = typeNames[type] || ('New artifact type: '+type);
|
||||
|
||||
html += '<table class="artifact '+type+'">';
|
||||
html += '<hr><div><b>'+name+'</b></div>';
|
||||
|
||||
html += '<table class="artifact artifact-'+type+'">';
|
||||
html += '<tr><th>Portal</th><th>Details</th></tr>';
|
||||
|
||||
var tableRows = [];
|
||||
@ -260,6 +283,11 @@ window.artifact.showArtifactList = function() {
|
||||
}
|
||||
});
|
||||
|
||||
// check for no rows, and add a note to the table instead
|
||||
if (tableRows.length == 0) {
|
||||
html += '<tr><td colspan="2"><i>No portals at this time</i></td></tr>';
|
||||
}
|
||||
|
||||
// sort the rows
|
||||
tableRows.sort(function(a,b) {
|
||||
return a[0]-b[0];
|
||||
@ -268,6 +296,7 @@ window.artifact.showArtifactList = function() {
|
||||
// and add them to the table
|
||||
html += tableRows.map(function(a){return a[1];}).join('');
|
||||
|
||||
|
||||
html += '</table>';
|
||||
});
|
||||
|
||||
|
@ -2,15 +2,9 @@
|
||||
// cache for map data tiles.
|
||||
|
||||
window.DataCache = function() {
|
||||
// stock site nemesis.dashboard.DataManager.CACHE_EXPIRY_MS_ = 18E4 - so should be 2 mins cache time
|
||||
this.REQUEST_CACHE_FRESH_AGE = 120; // if younger than this, use data in the cache rather than fetching from the server
|
||||
// stock site nemesis.dashboard.DataManager.CACHE_EXPIRY_MS_ = 18E4 - 3 minutes
|
||||
this.REQUEST_CACHE_FRESH_AGE = 3*60; // if younger than this, use data in the cache rather than fetching from the server
|
||||
|
||||
// stale cache entries can be updated (that's what the optional 'timestampMs' field in getThinnedEntities is
|
||||
// for, retrieving deltas) so use a long max age to take advantage of this
|
||||
// however, there must be an overall limit on the maximum age of data from the servers, otherwise the deletedEntity
|
||||
// entries would grow indefinitely. an hour seems reasonable from experience with the data, so 55 mins max cache time
|
||||
// this.REQUEST_CACHE_MAX_AGE = 55*60; // maximum cache age. entries are deleted from the cache after this time
|
||||
//UPDATE: this timestampMs parameter doesn't work, so reduced max age to limit RAM usage
|
||||
this.REQUEST_CACHE_MAX_AGE = 15*60; // maximum cache age. entries are deleted from the cache after this time
|
||||
|
||||
if (L.Browser.mobile) {
|
||||
|
113
code/map_data_calc_tools.js
Normal file → Executable file
113
code/map_data_calc_tools.js
Normal file → Executable file
@ -10,32 +10,119 @@
|
||||
// http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
|
||||
|
||||
|
||||
window.levelToTilesPerEdge = function(level) {
|
||||
var LEVEL_TO_TILES_PER_EDGE = [65536, 65536, 16384, 16384, 4096, 1536, 1024, 256, 32];
|
||||
return LEVEL_TO_TILES_PER_EDGE[level];
|
||||
window.getMapZoomTileParameters = function(zoom) {
|
||||
// these arrays/constants are based on those in the stock intel site. it's essential we keep them in sync with their code
|
||||
// (it may be worth reading the values from their code rather than using our own copies? it's a case of either
|
||||
// breaking if they rename their variables if we do, or breaking if they change the values if we don't)
|
||||
var ZOOM_TO_TILES_PER_EDGE = [32, 32, 32, 32, 256, 256, 256, 1024, 1024, 1536, 4096, 4096, 16384, 16384, 16384];
|
||||
var MAX_TILES_PER_EDGE = 65536;
|
||||
var ZOOM_TO_LEVEL = [8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 4, 4, 3, 2, 2, 1, 1];
|
||||
|
||||
return {
|
||||
level: ZOOM_TO_LEVEL[zoom] || 0, // default to level 0 (all portals) if not in array
|
||||
tilesPerEdge: ZOOM_TO_TILES_PER_EDGE[zoom] || MAX_TILES_PER_EDGE,
|
||||
zoom: zoom // include the zoom level, for reference
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
window.lngToTile = function(lng, level) {
|
||||
return Math.floor((lng + 180) / 360 * levelToTilesPerEdge(level));
|
||||
window.getDataZoomForMapZoom = function(zoom) {
|
||||
// we can fetch data at a zoom level different to the map zoom.
|
||||
|
||||
//NOTE: the specifics of this are tightly coupled with the above ZOOM_TO_LEVEL and ZOOM_TO_TILES_PER_EDGE arrays
|
||||
|
||||
// firstly, some of IITCs zoom levels, depending on base map layer, can be higher than stock. limit zoom level
|
||||
if (zoom > 18) {
|
||||
zoom = 18;
|
||||
}
|
||||
|
||||
if (!window.CONFIG_ZOOM_DEFAULT_DETAIL_LEVEL) {
|
||||
// some reasonable optimisations of data retreival
|
||||
|
||||
switch(zoom) {
|
||||
case 2:
|
||||
case 3:
|
||||
// L8 portals - fall back to the furthest out view. less detail, faster retreival. cache advantages when zooming
|
||||
// (note: iitc + stock both limited so zoom 0 never possible)
|
||||
zoom = 1;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// default is L7 - but this is a crazy number of tiles. fall back to L8 (but higher detail than above)
|
||||
// (the back-end does, unfortunately, rarely (never?) returns large fields with L8-only portals
|
||||
zoom = 3;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
case 6:
|
||||
// default L7 - pull out to furthest L7 zoom
|
||||
zoom = 4;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
// default L6 - pull back to highest L6 zoom
|
||||
zoom = 7;
|
||||
break;
|
||||
|
||||
// L5 portals - only one zoom level
|
||||
|
||||
case 11:
|
||||
// default L4 - pull back to lower detail L4
|
||||
zoom = 10;
|
||||
break;
|
||||
|
||||
// L3 portals - only one zoom level
|
||||
|
||||
case 14:
|
||||
// L2 portals - pull back to furthest
|
||||
zoom = 13;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
// L1 portals - pull back to furthest zoom
|
||||
zoom = 15;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (zoom >= 18) {
|
||||
// all portals - pull back to furthest zoom
|
||||
zoom = 17;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (window.CONFIG_ZOOM_SHOW_MORE_PORTALS) {
|
||||
if (zoom >= 15) {
|
||||
//L1+ and closer zooms. the 'all portals' zoom uses the same tile size, so it's no harm to request things at that zoom level
|
||||
zoom = 17;
|
||||
}
|
||||
}
|
||||
|
||||
return zoom;
|
||||
}
|
||||
|
||||
window.latToTile = function(lat, level) {
|
||||
|
||||
window.lngToTile = function(lng, params) {
|
||||
return Math.floor((lng + 180) / 360 * params.tilesPerEdge);
|
||||
}
|
||||
|
||||
window.latToTile = function(lat, params) {
|
||||
return Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) +
|
||||
1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * levelToTilesPerEdge(level));
|
||||
1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * params.tilesPerEdge);
|
||||
}
|
||||
|
||||
window.tileToLng = function(x, level) {
|
||||
return x / levelToTilesPerEdge(level) * 360 - 180;
|
||||
window.tileToLng = function(x, params) {
|
||||
return x / params.tilesPerEdge * 360 - 180;
|
||||
}
|
||||
|
||||
window.tileToLat = function(y, level) {
|
||||
var n = Math.PI - 2 * Math.PI * y / levelToTilesPerEdge(level);
|
||||
window.tileToLat = function(y, params) {
|
||||
var n = Math.PI - 2 * Math.PI * y / params.tilesPerEdge;
|
||||
return 180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
|
||||
}
|
||||
|
||||
window.pointToTileId = function(level, x, y) {
|
||||
return level + "_" + x + "_" + y;
|
||||
window.pointToTileId = function(params, x, y) {
|
||||
return params.zoom + "_" + x + "_" + y;
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,9 +10,6 @@ window.Render = function() {
|
||||
this.CLUSTER_SIZE = L.Browser.mobile ? 10 : 4; // the map is divided into squares of this size in pixels for clustering purposes. mobile uses larger markers, so therefore larger clustering areas
|
||||
this.CLUSTER_PORTAL_LIMIT = 4; // no more than this many portals are drawn in each cluster square
|
||||
|
||||
// link length, in pixels, to be visible. use the portal cluster size, as shorter than this is likely hidden
|
||||
// under the portals
|
||||
this.LINK_VISIBLE_PIXEL_LENGTH = this.CLUSTER_SIZE;
|
||||
|
||||
this.entityVisibilityZoom = undefined;
|
||||
|
||||
@ -398,11 +395,7 @@ window.Render.prototype.createLinkEntity = function(ent,faked) {
|
||||
|
||||
window.links[ent[0]] = poly;
|
||||
|
||||
// only add the link to the layer if it's long enough to be seen
|
||||
|
||||
if (this.linkVisible(poly)) {
|
||||
linksFactionLayers[poly.options.team].addLayer(poly);
|
||||
}
|
||||
linksFactionLayers[poly.options.team].addLayer(poly);
|
||||
}
|
||||
|
||||
|
||||
@ -412,7 +405,6 @@ window.Render.prototype.updateEntityVisibility = function() {
|
||||
this.entityVisibilityZoom = map.getZoom();
|
||||
|
||||
this.resetPortalClusters();
|
||||
this.resetLinkVisibility();
|
||||
|
||||
if (this.portalMarkerScale === undefined || this.portalMarkerScale != portalMarkerScale()) {
|
||||
this.portalMarkerScale = portalMarkerScale();
|
||||
@ -440,14 +432,20 @@ window.Render.prototype.resetPortalClusters = function() {
|
||||
|
||||
if (!(cid in this.portalClusters)) this.portalClusters[cid] = [];
|
||||
|
||||
this.portalClusters[cid].push(p.options.guid);
|
||||
this.portalClusters[cid].push(pguid);
|
||||
}
|
||||
|
||||
// now, for each cluster, sort by some arbitrary data (the guid will do), and display the first CLUSTER_PORTAL_LIMIT
|
||||
// now, for each cluster, sort by some arbitrary data (the level+guid will do), and display the first CLUSTER_PORTAL_LIMIT
|
||||
for (var cid in this.portalClusters) {
|
||||
var c = this.portalClusters[cid];
|
||||
|
||||
c.sort();
|
||||
c.sort(function(a,b) {
|
||||
var ka = (8-portals[a].options.level)+a;
|
||||
var kb = (8-portals[b].options.level)+b;
|
||||
if (ka<kb) return -1;
|
||||
else if (ka>kb) return 1;
|
||||
else return 0;
|
||||
});
|
||||
|
||||
for (var i=0; i<c.length; i++) {
|
||||
var guid = c[i];
|
||||
@ -513,49 +511,3 @@ window.Render.prototype.getPortalClusterID = function(portal) {
|
||||
}
|
||||
|
||||
|
||||
// link length
|
||||
|
||||
window.Render.prototype.getLinkPixelLength = function(link) {
|
||||
var z = map.getZoom();
|
||||
|
||||
var latLngs = link.getLatLngs();
|
||||
if (latLngs.length != 2) {
|
||||
console.warn ('Link had '+latLngs.length+' points - expected 2!');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var point0 = map.project(latLngs[0]);
|
||||
var point1 = map.project(latLngs[1]);
|
||||
|
||||
var dx = point0.x - point1.x;
|
||||
var dy = point0.y - point1.y;
|
||||
|
||||
var lengthSquared = (dx*dx)+(dy*dy);
|
||||
|
||||
var length = Math.sqrt (lengthSquared);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
window.Render.prototype.linkVisible = function(link) {
|
||||
var length = this.getLinkPixelLength (link);
|
||||
|
||||
return length >= this.LINK_VISIBLE_PIXEL_LENGTH;
|
||||
}
|
||||
|
||||
|
||||
window.Render.prototype.resetLinkVisibility = function() {
|
||||
|
||||
for (var guid in window.links) {
|
||||
var link = window.links[guid];
|
||||
|
||||
var visible = this.linkVisible(link);
|
||||
|
||||
if (visible) {
|
||||
if (!linksFactionLayers[link.options.team].hasLayer(link)) linksFactionLayers[link.options.team].addLayer(link);
|
||||
} else {
|
||||
if (linksFactionLayers[link.options.team].hasLayer(link)) linksFactionLayers[link.options.team].removeLayer(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,13 +20,8 @@ window.MapDataRequest = function() {
|
||||
// using our own queue limit ensures that other requests (e.g. chat, portal details) don't get delayed
|
||||
this.MAX_REQUESTS = 5;
|
||||
|
||||
// no more than this many tiles in one request
|
||||
// as of 2013-11-11, or possibly the release before that, the stock site was changed to only request four tiles at a time
|
||||
// (which matches the number successfully returned for a *long* time!)
|
||||
this.MAX_TILES_PER_REQUEST = 4;
|
||||
|
||||
// try to maintain at least this may tiles in each request, by reducing the number of requests as needed
|
||||
this.MIN_TILES_PER_REQUEST = 4;
|
||||
// this many tiles in one request
|
||||
this.NUM_TILES_PER_REQUEST = 4;
|
||||
|
||||
// number of times to retry a tile after a 'bad' error (i.e. not a timeout)
|
||||
this.MAX_TILE_RETRIES = 2;
|
||||
@ -184,8 +179,12 @@ window.MapDataRequest.prototype.refresh = function() {
|
||||
|
||||
|
||||
var bounds = clampLatLngBounds(map.getBounds());
|
||||
var zoom = map.getZoom();
|
||||
var minPortalLevel = getMinPortalLevelForZoom(zoom);
|
||||
var mapZoom = map.getZoom();
|
||||
|
||||
var dataZoom = getDataZoomForMapZoom(mapZoom);
|
||||
|
||||
var tileParams = getMapZoomTileParameters(dataZoom);
|
||||
|
||||
|
||||
//DEBUG: resize the bounds so we only retrieve some data
|
||||
//bounds = bounds.pad(-0.4);
|
||||
@ -193,36 +192,34 @@ window.MapDataRequest.prototype.refresh = function() {
|
||||
//var debugrect = L.rectangle(bounds,{color: 'red', fill: false, weight: 4, opacity: 0.8}).addTo(map);
|
||||
//setTimeout (function(){ map.removeLayer(debugrect); }, 10*1000);
|
||||
|
||||
var x1 = lngToTile(bounds.getWest(), minPortalLevel);
|
||||
var x2 = lngToTile(bounds.getEast(), minPortalLevel);
|
||||
var y1 = latToTile(bounds.getNorth(), minPortalLevel);
|
||||
var y2 = latToTile(bounds.getSouth(), minPortalLevel);
|
||||
var x1 = lngToTile(bounds.getWest(), tileParams);
|
||||
var x2 = lngToTile(bounds.getEast(), tileParams);
|
||||
var y1 = latToTile(bounds.getNorth(), tileParams);
|
||||
var y2 = latToTile(bounds.getSouth(), tileParams);
|
||||
|
||||
// calculate the full bounds for the data - including the part of the tiles off the screen edge
|
||||
var dataBounds = L.latLngBounds([
|
||||
[tileToLat(y2+1,minPortalLevel), tileToLng(x1,minPortalLevel)],
|
||||
[tileToLat(y1,minPortalLevel), tileToLng(x2+1,minPortalLevel)]
|
||||
[tileToLat(y2+1,tileParams), tileToLng(x1,tileParams)],
|
||||
[tileToLat(y1,tileParams), tileToLng(x2+1,tileParams)]
|
||||
]);
|
||||
//var debugrect2 = L.rectangle(dataBounds,{color: 'magenta', fill: false, weight: 4, opacity: 0.8}).addTo(map);
|
||||
//setTimeout (function(){ map.removeLayer(debugrect2); }, 10*1000);
|
||||
|
||||
// store the parameters used for fetching the data. used to prevent unneeded refreshes after move/zoom
|
||||
this.fetchedDataParams = { bounds: dataBounds, mapZoom: map.getZoom(), minPortalLevel: minPortalLevel };
|
||||
this.fetchedDataParams = { bounds: dataBounds, mapZoom: mapZoom, dataZoom: dataZoom };
|
||||
|
||||
|
||||
window.runHooks ('mapDataRefreshStart', {bounds: bounds, zoom: zoom, minPortalLevel: minPortalLevel, tileBounds: dataBounds});
|
||||
window.runHooks ('mapDataRefreshStart', {bounds: bounds, mapZoom: mapZoom, dataZoom: dataZoom, minPortalLevel: tileParams.level, tileBounds: dataBounds});
|
||||
|
||||
this.render.startRenderPass();
|
||||
this.render.clearPortalsBelowLevel(minPortalLevel);
|
||||
|
||||
|
||||
this.render.clearPortalsBelowLevel(tileParams.level);
|
||||
this.render.clearEntitiesOutsideBounds(dataBounds);
|
||||
|
||||
this.render.updateEntityVisibility();
|
||||
|
||||
this.render.processGameEntities(artifact.getArtifactEntities());
|
||||
|
||||
console.log('requesting data tiles at zoom '+zoom+' (L'+minPortalLevel+'+ portals), map zoom is '+map.getZoom());
|
||||
console.log('requesting data tiles at zoom '+dataZoom+' (L'+tileParams.level+'+ portals, '+tileParams.tilesPerEdge+' tiles per global edge), map zoom is '+mapZoom);
|
||||
|
||||
|
||||
this.cachedTileCount = 0;
|
||||
@ -234,17 +231,17 @@ window.MapDataRequest.prototype.refresh = function() {
|
||||
var tilesToFetchDistance = {};
|
||||
|
||||
// map center point - for fetching center tiles first
|
||||
var mapCenterPoint = map.project(map.getCenter(), zoom);
|
||||
var mapCenterPoint = map.project(map.getCenter(), mapZoom);
|
||||
|
||||
// y goes from left to right
|
||||
for (var y = y1; y <= y2; y++) {
|
||||
// x goes from bottom to top(?)
|
||||
for (var x = x1; x <= x2; x++) {
|
||||
var tile_id = pointToTileId(minPortalLevel, x, y);
|
||||
var latNorth = tileToLat(y,minPortalLevel);
|
||||
var latSouth = tileToLat(y+1,minPortalLevel);
|
||||
var lngWest = tileToLng(x,minPortalLevel);
|
||||
var lngEast = tileToLng(x+1,minPortalLevel);
|
||||
var tile_id = pointToTileId(tileParams, x, y);
|
||||
var latNorth = tileToLat(y,tileParams);
|
||||
var latSouth = tileToLat(y+1,tileParams);
|
||||
var lngWest = tileToLng(x,tileParams);
|
||||
var lngEast = tileToLng(x+1,tileParams);
|
||||
|
||||
this.debugTiles.create(tile_id,[[latSouth,lngWest],[latNorth,lngEast]]);
|
||||
|
||||
@ -258,10 +255,10 @@ window.MapDataRequest.prototype.refresh = function() {
|
||||
// no fresh data
|
||||
|
||||
// render the cached stale data, if we have it. this ensures *something* appears quickly when possible
|
||||
var old_data = this.cache && this.cache.get(tile_id);
|
||||
if (old_data) {
|
||||
this.render.processTileData (old_data);
|
||||
}
|
||||
// var old_data = this.cache && this.cache.get(tile_id);
|
||||
// if (old_data) {
|
||||
// this.render.processTileData (old_data);
|
||||
// }
|
||||
|
||||
// tile needed. calculate the distance from the centre of the screen, to optimise the load order
|
||||
|
||||
@ -269,7 +266,7 @@ window.MapDataRequest.prototype.refresh = function() {
|
||||
var lngCenter = (lngEast+lngWest)/2;
|
||||
var tileLatLng = L.latLng(latCenter,lngCenter);
|
||||
|
||||
var tilePoint = map.project(tileLatLng, zoom);
|
||||
var tilePoint = map.project(tileLatLng, mapZoom);
|
||||
|
||||
var delta = mapCenterPoint.subtract(tilePoint);
|
||||
var distanceSquared = delta.x*delta.x + delta.y*delta.y;
|
||||
@ -363,26 +360,13 @@ window.MapDataRequest.prototype.processRequestQueue = function(isFirstPass) {
|
||||
var requestBuckets = this.MAX_REQUESTS - this.activeRequestCount;
|
||||
if (pendingTiles.length > 0 && requestBuckets > 0) {
|
||||
|
||||
// the stock site calculates bucket grouping with the simplistic <8 tiles: 1 bucket, otherwise 4 buckets
|
||||
var maxBuckets = Math.ceil(pendingTiles.length/this.MIN_TILES_PER_REQUEST);
|
||||
|
||||
requestBuckets = Math.min (maxBuckets, requestBuckets);
|
||||
|
||||
var lastTileIndex = Math.min(requestBuckets*this.MAX_TILES_PER_REQUEST, pendingTiles.length);
|
||||
|
||||
for (var bucket=0; bucket<requestBuckets; bucket++) {
|
||||
// create each request by taking tiles interleaved from the request
|
||||
|
||||
var tiles = [];
|
||||
for (var i=bucket; i<lastTileIndex; i+=requestBuckets) {
|
||||
tiles.push (pendingTiles[i]);
|
||||
}
|
||||
|
||||
for (var bucket=0; bucket < requestBuckets; bucket++) {
|
||||
var tiles = pendingTiles.splice(0, this.NUM_TILES_PER_REQUEST);
|
||||
if (tiles.length > 0) {
|
||||
// console.log('-- new request: '+tiles.length+' tiles');
|
||||
this.sendTileRequest(tiles);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
11
code/munge.js
Normal file → Executable file
11
code/munge.js
Normal file → Executable file
@ -110,7 +110,7 @@ function extractMungeFromStock() {
|
||||
foundMunges.version_parameter = result[5];
|
||||
|
||||
// GET_THINNED_ENTITIES parameters
|
||||
var reg = new RegExp('GET_THINNED_ENTITIES, {'+mungeRegExpLit+'[a-z]');
|
||||
var reg = new RegExp('GET_THINNED_ENTITIES, nemesis.dashboard.network.XhrController.Priority.[A-Z]+, {'+mungeRegExpLit+'[a-z]');
|
||||
var result = reg.exec(nemesis.dashboard.network.DataFetcher.prototype.getGameEntities.toString());
|
||||
foundMunges.quadKeys = result[1] || result[2];
|
||||
|
||||
@ -119,7 +119,7 @@ function extractMungeFromStock() {
|
||||
var result = reg.exec(nemesis.dashboard.network.PlextStore.prototype.getPlexts.toString());
|
||||
|
||||
foundMunges.desiredNumItems = result[1] || result[2];
|
||||
|
||||
|
||||
foundMunges.minLatE6 = result[3] || result[4];
|
||||
foundMunges.minLngE6 = result[5] || result[6];
|
||||
foundMunges.maxLatE6 = result[7] || result[8];
|
||||
@ -130,7 +130,7 @@ function extractMungeFromStock() {
|
||||
foundMunges.ascendingTimestampOrder = result[17] || result[18];
|
||||
|
||||
// SEND_PLEXT
|
||||
var reg = new RegExp('SEND_PLEXT, {'+mungeRegExpLit+'[a-z], '+mungeRegExpLit+'[a-z], '+mungeRegExpLit+'[a-z], '+mungeRegExpLit+'[a-z]}');
|
||||
var reg = new RegExp('SEND_PLEXT, nemesis.dashboard.network.XhrController.Priority.[A-Z]+, {'+mungeRegExpLit+'[a-z], '+mungeRegExpLit+'[a-z], '+mungeRegExpLit+'[a-z], '+mungeRegExpLit+'[a-z]}');
|
||||
var result = reg.exec(nemesis.dashboard.network.PlextStore.prototype.sendPlext.toString());
|
||||
|
||||
foundMunges.message = result[1] || result[2];
|
||||
@ -140,13 +140,12 @@ function extractMungeFromStock() {
|
||||
if (chatTab != foundMunges.chatTab) throw 'Error: inconsistent munge parsing for chatTab';
|
||||
|
||||
// GET_PORTAL_DETAILS
|
||||
var reg = new RegExp('GET_PORTAL_DETAILS, {'+mungeRegExpLit+'a}');
|
||||
var reg = new RegExp('GET_PORTAL_DETAILS, nemesis.dashboard.network.XhrController.Priority.[A-Z]+, {'+mungeRegExpLit+'a}');
|
||||
var result = reg.exec(nemesis.dashboard.network.DataFetcher.prototype.getPortalDetails.toString());
|
||||
|
||||
foundMunges.guid = result[1] || result[2];
|
||||
|
||||
// SEND_INVITE_EMAIL
|
||||
var reg = new RegExp('SEND_INVITE_EMAIL, {'+mungeRegExpLit+'b}');
|
||||
var reg = new RegExp('SEND_INVITE_EMAIL, nemesis.dashboard.network.XhrController.Priority.[A-Z]+, {'+mungeRegExpLit+'b}');
|
||||
foundMunges.inviteeEmailAddress = result[1] || result[2];
|
||||
|
||||
return foundMunges;
|
||||
|
@ -202,21 +202,27 @@ window.getPortalMiscDetails = function(guid,d) {
|
||||
|
||||
// artifact details
|
||||
|
||||
//niantic hard-code the fact it's just jarvis shards/targets - so until more examples exist, we'll do the same
|
||||
//(at some future point we can iterate through all the artifact types and add rows as needed)
|
||||
var jarvisArtifact = artifact.getPortalData (guid, 'jarvis');
|
||||
if (jarvisArtifact) {
|
||||
// the genFourColumnTable function below doesn't handle cases where one column is null and the other isn't - so default to *something* in both columns
|
||||
var target = ['',''], shards = ['shards','(none)'];
|
||||
if (jarvisArtifact.target) {
|
||||
target = ['target', '<span class="'+TEAM_TO_CSS[jarvisArtifact.target]+'">'+(jarvisArtifact.target==TEAM_RES?'Resistance':'Enlightened')+'</span>'];
|
||||
}
|
||||
if (jarvisArtifact.fragments) {
|
||||
shards = [jarvisArtifact.fragments.length>1?'shards':'shard', '#'+jarvisArtifact.fragments.join(', #')];
|
||||
}
|
||||
// 2014-02-06: stock site changed from supporting 'jarvis shards' to 'amar artifacts'(?) - so let's see what we can do to be generic...
|
||||
var artifactTypes = {
|
||||
'jarvis': { 'name': 'Jarvis', 'fragmentName': 'shard(s)' },
|
||||
'amar': { 'name': 'Amar', 'fragmentName': 'artifact(s)' },
|
||||
};
|
||||
|
||||
randDetailsData.push (target, shards);
|
||||
}
|
||||
$.each(artifactTypes,function(type,details) {
|
||||
var artdata = artifact.getPortalData (guid, type);
|
||||
if (artdata) {
|
||||
// the genFourColumnTable function below doesn't handle cases where one column is null and the other isn't - so default to *something* in both columns
|
||||
var target = ['',''], shards = [details.fragmentName,'(none)'];
|
||||
if (artdata.target) {
|
||||
target = ['target', '<span class="'+TEAM_TO_CSS[artdata.target]+'">'+(artdata.target==TEAM_RES?'Resistance':'Enlightened')+'</span>'];
|
||||
}
|
||||
if (artdata.fragments) {
|
||||
shards = [details.fragmentName, '#'+artdata.fragments.join(', #')];
|
||||
}
|
||||
|
||||
randDetailsData.push (target, shards);
|
||||
}
|
||||
});
|
||||
|
||||
randDetails = '<table id="randdetails">' + genFourColumnTable(randDetailsData) + '</table>';
|
||||
|
||||
|
@ -279,21 +279,11 @@ window.androidPermalink = function() {
|
||||
}
|
||||
|
||||
|
||||
window.getMinPortalLevelForZoom = function(z) {
|
||||
|
||||
// these values are from the stock intel map. however, they're too detailed for reasonable speed, and increasing
|
||||
// detail at a higher zoom level shows enough detail still, AND speeds up IITC considerably
|
||||
//var ZOOM_TO_LEVEL = [8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 4, 4, 3, 2, 2, 1, 1];
|
||||
var ZOOM_TO_LEVEL = [8, 8, 8, 8, 8, 7, 7, 7, 6, 5, 4, 4, 3, 2, 2, 1, 1];
|
||||
|
||||
var l = ZOOM_TO_LEVEL[z] || 0;
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
window.getMinPortalLevel = function() {
|
||||
var z = map.getZoom();
|
||||
return getMinPortalLevelForZoom(z);
|
||||
z = getDataZoomForMapZoom(z);
|
||||
return getMapZoomTileParameters(z).level;
|
||||
}
|
||||
|
||||
// returns number of pixels left to scroll down before reaching the
|
||||
|
2
external/leaflet.draw.css
vendored
2
external/leaflet.draw.css
vendored
@ -50,6 +50,7 @@
|
||||
position: absolute;
|
||||
left: 26px; /* leaflet-draw-toolbar.left + leaflet-draw-toolbar.width */
|
||||
top: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.leaflet-right .leaflet-draw-actions {
|
||||
@ -98,7 +99,6 @@
|
||||
|
||||
.leaflet-draw-actions-top {
|
||||
margin-top: 1px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.leaflet-draw-actions-top a,
|
||||
|
12
images/marker-icon.svg.template
Normal file
12
images/marker-icon.svg.template
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1" baseProfile="full"
|
||||
width="25px" height="41px" viewBox="0 0 25 41">
|
||||
|
||||
<path d="M1.36241844765,18.67488124675 A12.5,12.5 0 1,1 23.63758155235,18.67488124675 L12.5,40.5336158073 Z" style="stroke:none; fill: %COLOR%;" />
|
||||
<path d="M1.80792170975,18.44788599685 A12,12 0 1,1 23.19207829025,18.44788599685 L12.5,39.432271175 Z" style="stroke:#000000; stroke-width:1px; stroke-opacity: 0.15; fill: none;" />
|
||||
<path d="M2.921679865,17.8803978722 A10.75,10.75 0 1,1 22.078320135,17.8803978722 L12.5,36.6789095943 Z" style="stroke:#ffffff; stroke-width:1.5px; stroke-opacity: 0.35; fill: none;" />
|
||||
|
||||
<path d="M19.86121593215,17.25 L12.5,21.5 L5.13878406785,17.25 L5.13878406785,8.75 L12.5,4.5 L19.86121593215,8.75 Z M7.7368602792,10.25 L17.2631397208,10.25 L12.5,18.5 Z M12.5,13 L7.7368602792,10.25 M12.5,13 L17.2631397208,10.25 M12.5,13 L12.5,18.5 M19.86121593215,17.25 L16.39711431705,15.25 M5.13878406785,17.25 L8.60288568295,15.25 M12.5,4.5 L12.5,8.5" style="stroke:#ffffff; stroke-width:1.25px; stroke-opacity: 1; fill: none;" />
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
2
main.js
2
main.js
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @id ingress-intel-total-conversion@jonatkins
|
||||
// @name IITC: Ingress intel map total conversion
|
||||
// @version 0.16.4.@@DATETIMEVERSION@@
|
||||
// @version 0.16.5.@@DATETIMEVERSION@@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
|
@ -2,8 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.cradle.iitc_mobile"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="65"
|
||||
android:versionName="0.10.4">
|
||||
android:versionCode="66"
|
||||
android:versionName="0.10.5">
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
@ -72,6 +72,45 @@
|
||||
<data android:scheme="geo"/>
|
||||
</intent-filter>
|
||||
|
||||
<!-- Handles external user plugins -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
|
||||
<data android:mimeType="application/x-javascript" />
|
||||
<data android:mimeType="text/javascript" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
|
||||
<data
|
||||
android:host="*"
|
||||
android:scheme="file"
|
||||
android:pathPattern=".*\\.user.js"/>
|
||||
|
||||
<data
|
||||
android:host="*"
|
||||
android:scheme="content"
|
||||
android:pathPattern=".*\\.user.js"/>
|
||||
|
||||
<data
|
||||
android:host="*"
|
||||
android:scheme="http"
|
||||
android:pathPattern=".*\\.user.js"/>
|
||||
|
||||
<data
|
||||
android:host="*"
|
||||
android:scheme="https"
|
||||
android:pathPattern=".*\\.user.js"/>
|
||||
</intent-filter>
|
||||
|
||||
<!-- Points to searchable meta data. -->
|
||||
<meta-data
|
||||
android:name="android.app.searchable"
|
||||
|
BIN
mobile/res/drawable-hdpi/ic_action_new.png
Normal file
BIN
mobile/res/drawable-hdpi/ic_action_new.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 262 B |
BIN
mobile/res/drawable-mdpi/ic_action_new.png
Normal file
BIN
mobile/res/drawable-mdpi/ic_action_new.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 185 B |
BIN
mobile/res/drawable-xhdpi/ic_action_new.png
Normal file
BIN
mobile/res/drawable-xhdpi/ic_action_new.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 250 B |
BIN
mobile/res/drawable-xxhdpi/ic_action_new.png
Normal file
BIN
mobile/res/drawable-xxhdpi/ic_action_new.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 288 B |
12
mobile/res/menu/plugins.xml
Normal file
12
mobile/res/menu/plugins.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_plugins_add"
|
||||
android:icon="@drawable/ic_action_new"
|
||||
android:orderInCategory="10"
|
||||
android:showAsAction="ifRoom|collapseActionView"
|
||||
android:title="@string/menu_plugins_add"/>
|
||||
|
||||
</menu>
|
@ -86,9 +86,8 @@
|
||||
<string name="notice_extplugins">
|
||||
<![CDATA[Hint:<br><br>
|
||||
IITC Mobile is able to load external plugins too!<br><br>
|
||||
• create <b>%1$s</b><br>
|
||||
• move *.user.js files there<br>
|
||||
• plugins should be listed above the official plugins]]>
|
||||
Add them by clicking the (+) icon at the top right.
|
||||
The plugin files have to end with \'.user.js\' and are copied to <b>%1$s</b><br>]]>
|
||||
</string>
|
||||
<string name="notice_sharing">
|
||||
<![CDATA[With <em>Share portal</em> you can:<br>
|
||||
@ -162,6 +161,7 @@
|
||||
<string name="menu_clear_cookies">Clear Cookies</string>
|
||||
<string name="menu_search">Search</string>
|
||||
<string name="menu_debug">Debug</string>
|
||||
<string name="menu_plugins_add">Add external plugins</string>
|
||||
<string name="choose_account_to_login">Choose account to login</string>
|
||||
<string name="login_failed">Login failed.</string>
|
||||
<string name="search_hint">Search Locations</string>
|
||||
@ -177,4 +177,11 @@
|
||||
<string name="label_base_layer">Base Layer</string>
|
||||
<string name="label_overlay_layers">Overlay Layers</string>
|
||||
|
||||
<string name="install_dialog_top">Install external plugin?</string>
|
||||
<string name="install_dialog_msg">
|
||||
<![CDATA[IITCm was requested to install the following javascript file:<br><br>%1$s<br><br>
|
||||
<b>Be careful:</b> Javascript from external sources may contain harmful code (spyware etc.)!<br><br>
|
||||
Are you sure you want to proceed?]]>
|
||||
</string>
|
||||
|
||||
</resources>
|
||||
|
@ -1,13 +1,18 @@
|
||||
package com.cradle.iitc_mobile;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.AssetManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.Html;
|
||||
import android.util.Base64;
|
||||
import android.util.Base64OutputStream;
|
||||
import android.webkit.WebResourceResponse;
|
||||
@ -22,11 +27,14 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.HashMap;
|
||||
|
||||
@ -82,6 +90,7 @@ public class IITC_FileManager {
|
||||
// get a list of key-value
|
||||
final String[] attributes = header.split(" +");
|
||||
// add default values
|
||||
map.put("id", "unknown");
|
||||
map.put("version", "not found");
|
||||
map.put("name", "unknown");
|
||||
map.put("description", "");
|
||||
@ -89,6 +98,9 @@ public class IITC_FileManager {
|
||||
// add parsed values
|
||||
for (int i = 0; i < attributes.length; i++) {
|
||||
// search for attributes and use the value
|
||||
if (attributes[i].equals("@id")) {
|
||||
map.put("id", attributes[i + 1]);
|
||||
}
|
||||
if (attributes[i].equals("@version")) {
|
||||
map.put("version", attributes[i + 1]);
|
||||
}
|
||||
@ -118,15 +130,17 @@ public class IITC_FileManager {
|
||||
}
|
||||
|
||||
private final AssetManager mAssetManager;
|
||||
private final IITC_Mobile mIitc;
|
||||
private final Activity mActivity;
|
||||
private final String mIitcPath;
|
||||
private final SharedPreferences mPrefs;
|
||||
public static final String PLUGINS_PATH = Environment.getExternalStorageDirectory().getPath()
|
||||
+ "/IITC_Mobile/plugins/";
|
||||
|
||||
public IITC_FileManager(final IITC_Mobile iitc) {
|
||||
mIitc = iitc;
|
||||
mIitcPath = Environment.getExternalStorageDirectory().getPath() + "/IITC_Mobile/";
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(iitc);
|
||||
mAssetManager = mIitc.getAssets();
|
||||
public IITC_FileManager(final Activity activity) {
|
||||
mActivity = activity;
|
||||
mIitcPath = Environment.getExternalStorageDirectory().getPath() + "/Activity/";
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
mAssetManager = mActivity.getAssets();
|
||||
}
|
||||
|
||||
private InputStream getAssetFile(final String filename) throws IOException {
|
||||
@ -135,10 +149,10 @@ public class IITC_FileManager {
|
||||
try {
|
||||
return new FileInputStream(file);
|
||||
} catch (final FileNotFoundException e) {
|
||||
mIitc.runOnUiThread(new Runnable() {
|
||||
mActivity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(mIitc, "File " + mIitcPath +
|
||||
Toast.makeText(mActivity, "File " + mIitcPath +
|
||||
"dev/" + filename + " not found. " +
|
||||
"Disable developer mode or add iitc files to the dev folder.",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
@ -233,6 +247,77 @@ public class IITC_FileManager {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
public void installPlugin(final Uri uri, final boolean invalidateHeaders) {
|
||||
if (uri != null) {
|
||||
String text = mActivity.getString(R.string.install_dialog_msg);
|
||||
text = String.format(text, uri);
|
||||
|
||||
// create alert dialog
|
||||
new AlertDialog.Builder(mActivity)
|
||||
.setTitle(mActivity.getString(R.string.install_dialog_top))
|
||||
.setMessage(Html.fromHtml(text))
|
||||
.setCancelable(true)
|
||||
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog, final int which) {
|
||||
copyPlugin(uri, invalidateHeaders);
|
||||
}
|
||||
})
|
||||
.setNegativeButton("No", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog, final int which) {
|
||||
dialog.cancel();
|
||||
}
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
private void copyPlugin(final Uri uri, final boolean invalidateHeaders) {
|
||||
final Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final String url = uri.toString();
|
||||
InputStream is;
|
||||
String fileName;
|
||||
if (uri.getScheme().contains("http")) {
|
||||
URLConnection conn = new URL(url).openConnection();
|
||||
is = conn.getInputStream();
|
||||
fileName = uri.getLastPathSegment();
|
||||
} else {
|
||||
// we need 2 streams since an inputStream is useless after read once
|
||||
// we read it twice because we first need the script ID for the fileName and
|
||||
// afterwards reading it again while copying
|
||||
is = mActivity.getContentResolver().openInputStream(uri);
|
||||
final InputStream isCopy = mActivity.getContentResolver().openInputStream(uri);
|
||||
fileName = getScriptInfo(isCopy).get("id") + ".user.js";
|
||||
}
|
||||
// create IITCm external plugins directory if it doesn't already exist
|
||||
final File pluginsDirectory = new File(PLUGINS_PATH);
|
||||
pluginsDirectory.mkdirs();
|
||||
|
||||
// create in and out streams and copy plugin
|
||||
final File outFile = new File(pluginsDirectory, fileName);
|
||||
final OutputStream os = new FileOutputStream(outFile);
|
||||
IITC_FileManager.copyStream(is, os, true);
|
||||
} catch (final IOException e) {
|
||||
Log.w(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
if (invalidateHeaders) {
|
||||
try {
|
||||
thread.join();
|
||||
((IITC_PluginPreferenceActivity) mActivity).invalidateHeaders();
|
||||
} catch (final InterruptedException e) {
|
||||
Log.w(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FileRequest extends WebResourceResponse implements ResponseHandler, Runnable {
|
||||
private Intent mData;
|
||||
private final String mFunctionName;
|
||||
@ -253,21 +338,23 @@ public class IITC_FileManager {
|
||||
mFunctionName = uri.getPathSegments().get(0);
|
||||
|
||||
// create the chooser Intent
|
||||
final Intent target = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
target.setType("file/*");
|
||||
target.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
final Intent target = new Intent(Intent.ACTION_GET_CONTENT)
|
||||
.setType("*/*")
|
||||
.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
final IITC_Mobile iitc = (IITC_Mobile) mActivity;
|
||||
|
||||
try {
|
||||
mIitc.startActivityForResult(Intent.createChooser(target, "Choose file"), this);
|
||||
iitc.startActivityForResult(Intent.createChooser(target, "Choose file"), this);
|
||||
} catch (final ActivityNotFoundException e) {
|
||||
Toast.makeText(mIitc, "No activity to select a file found." +
|
||||
Toast.makeText(mActivity, "No activity to select a file found." +
|
||||
"Please install a file browser of your choice!", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(final int resultCode, final Intent data) {
|
||||
mIitc.deleteResponseHandler(this); // to enable garbage collection
|
||||
final IITC_Mobile iitc = (IITC_Mobile) mActivity;
|
||||
iitc.deleteResponseHandler(this); // to enable garbage collection
|
||||
|
||||
mResultCode = resultCode;
|
||||
mData = data;
|
||||
@ -281,18 +368,18 @@ public class IITC_FileManager {
|
||||
try {
|
||||
if (mResultCode == Activity.RESULT_OK && mData != null) {
|
||||
final Uri uri = mData.getData();
|
||||
final File file = new File(uri.getPath());
|
||||
|
||||
// now create a resource that basically looks like:
|
||||
// someFunctionName('<url encoded filename>', '<base64 encoded content>');
|
||||
|
||||
mStreamOut.write(
|
||||
(mFunctionName + "('" + URLEncoder.encode(file.getName(), "UTF-8") + "', '").getBytes());
|
||||
final String filename = uri.getLastPathSegment();
|
||||
final String call = mFunctionName + "('" + URLEncoder.encode(filename, "UTF-8") + "', '";
|
||||
mStreamOut.write(call.getBytes());
|
||||
|
||||
final Base64OutputStream encoder =
|
||||
new Base64OutputStream(mStreamOut, Base64.NO_CLOSE | Base64.NO_WRAP | Base64.DEFAULT);
|
||||
|
||||
final FileInputStream fileinput = new FileInputStream(file);
|
||||
final InputStream fileinput = mActivity.getContentResolver().openInputStream(uri);
|
||||
|
||||
copyStream(fileinput, encoder, true);
|
||||
|
||||
@ -310,4 +397,56 @@ public class IITC_FileManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
public class FileSaveRequest implements ResponseHandler, Runnable {
|
||||
private Intent mData;
|
||||
private final IITC_Mobile mIitc;
|
||||
private final String mContent;
|
||||
|
||||
public FileSaveRequest(final String filename, final String type, final String content) {
|
||||
final Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||
.setType(type)
|
||||
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
.putExtra(Intent.EXTRA_TITLE, filename);
|
||||
|
||||
mContent = content;
|
||||
mIitc = (IITC_Mobile) mActivity;
|
||||
mIitc.startActivityForResult(intent, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(final int resultCode, final Intent data) {
|
||||
mIitc.deleteResponseHandler(this);
|
||||
|
||||
if (resultCode != Activity.RESULT_OK) return;
|
||||
|
||||
mData = data;
|
||||
|
||||
new Thread(this, "FileSaveRequest").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (mData == null) return;
|
||||
|
||||
final Uri uri = mData.getData();
|
||||
OutputStream os = null;
|
||||
|
||||
try {
|
||||
final ParcelFileDescriptor fd = mIitc.getContentResolver().openFileDescriptor(uri, "w");
|
||||
|
||||
try {
|
||||
os = new FileOutputStream(fd.getFileDescriptor());
|
||||
os.write(mContent.getBytes());
|
||||
os.close();
|
||||
} catch (final IOException e) {
|
||||
Log.w("Could not save file!", e);
|
||||
}
|
||||
fd.close();
|
||||
} catch (final IOException e) {
|
||||
Log.w("Could not save file!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,16 +6,21 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Environment;
|
||||
import android.webkit.JavascriptInterface;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.cradle.iitc_mobile.IITC_NavigationHelper.Pane;
|
||||
import com.cradle.iitc_mobile.share.ShareActivity;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
// provide communication between IITC script and android app
|
||||
public class IITC_JSInterface {
|
||||
// context of main activity
|
||||
private final IITC_Mobile mIitc;
|
||||
protected final IITC_Mobile mIitc;
|
||||
|
||||
IITC_JSInterface(final IITC_Mobile iitc) {
|
||||
mIitc = iitc;
|
||||
@ -232,4 +237,20 @@ public class IITC_JSInterface {
|
||||
public void setPermalink(final String href) {
|
||||
mIitc.setPermalink(href);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void saveFile(final String filename, final String type, final String content) {
|
||||
try {
|
||||
final File outFile = new File(Environment.getExternalStorageDirectory().getPath() +
|
||||
"/IITC_Mobile/export/" + filename);
|
||||
outFile.getParentFile().mkdirs();
|
||||
|
||||
final FileOutputStream outStream = new FileOutputStream(outFile);
|
||||
outStream.write(content.getBytes("UTF-8"));
|
||||
outStream.close();
|
||||
Toast.makeText(mIitc, "File exported to " + outFile.getPath(), Toast.LENGTH_SHORT).show();
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
package com.cradle.iitc_mobile;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.webkit.JavascriptInterface;
|
||||
|
||||
@TargetApi(19)
|
||||
public class IITC_JSInterfaceKitkat extends IITC_JSInterface {
|
||||
public IITC_JSInterfaceKitkat(final IITC_Mobile iitc) {
|
||||
super(iitc);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@Override
|
||||
public void saveFile(final String filename, final String type, final String content) {
|
||||
mIitc.getFileManager().new FileSaveRequest(filename, type, content);
|
||||
}
|
||||
}
|
@ -47,6 +47,8 @@ import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Stack;
|
||||
import java.util.Vector;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class IITC_Mobile extends Activity
|
||||
implements OnSharedPreferenceChangeListener, NfcAdapter.CreateNdefMessageCallback {
|
||||
@ -74,6 +76,7 @@ public class IITC_Mobile extends Activity
|
||||
private boolean mShowMapInDebug = false;
|
||||
private final Stack<String> mDialogStack = new Stack<String>();
|
||||
private String mPermalink = null;
|
||||
private String mSearchTerm = null;
|
||||
|
||||
// Used for custom back stack handling
|
||||
private final Stack<Pane> mBackStack = new Stack<IITC_NavigationHelper.Pane>();
|
||||
@ -104,7 +107,9 @@ public class IITC_Mobile extends Activity
|
||||
mEditCommand.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
@Override
|
||||
public boolean onEditorAction(final TextView v, final int actionId, final KeyEvent event) {
|
||||
if (EditorInfo.IME_ACTION_GO == actionId) {
|
||||
if (EditorInfo.IME_ACTION_GO == actionId ||
|
||||
EditorInfo.IME_ACTION_SEND == actionId ||
|
||||
EditorInfo.IME_ACTION_DONE == actionId) {
|
||||
onBtnRunCodeClick(v);
|
||||
|
||||
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
@ -193,8 +198,8 @@ public class IITC_Mobile extends Activity
|
||||
handleIntent(intent, false);
|
||||
}
|
||||
|
||||
// handles ingress intel url intents, search intents, geo intents and javascript file intents
|
||||
private void handleIntent(final Intent intent, final boolean onCreate) {
|
||||
// load new iitc web view with ingress intel page
|
||||
final String action = intent.getAction();
|
||||
if (Intent.ACTION_VIEW.equals(action) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
|
||||
final Uri uri = intent.getData();
|
||||
@ -228,6 +233,15 @@ public class IITC_Mobile extends Activity
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
// intent MIME type and uri path may be null
|
||||
final String type = intent.getType() == null ? "" : intent.getType();
|
||||
final String path = uri.getPath() == null ? "" : uri.getPath();
|
||||
if (path.endsWith(".user.js") || type.contains("javascript")) {
|
||||
final Intent prefIntent = new Intent(this, IITC_PluginPreferenceActivity.class);
|
||||
prefIntent.setDataAndType(uri, intent.getType());
|
||||
startActivity(prefIntent);
|
||||
}
|
||||
}
|
||||
|
||||
if (Intent.ACTION_SEARCH.equals(action)) {
|
||||
@ -250,20 +264,20 @@ public class IITC_Mobile extends Activity
|
||||
|
||||
private void handleGeoUri(final Uri uri) throws URISyntaxException {
|
||||
final String[] parts = uri.getSchemeSpecificPart().split("\\?", 2);
|
||||
Double lat, lon;
|
||||
Double lat = null, lon = null;
|
||||
Integer z = null;
|
||||
String search = null;
|
||||
|
||||
// parts[0] may contain an 'uncertainty' parameter, delimited by a semicolon
|
||||
final String[] pos = parts[0].split(";", 2)[0].split(",", 2);
|
||||
if (pos.length != 2) throw new URISyntaxException(uri.toString(), "URI does not contain a valid position");
|
||||
|
||||
try {
|
||||
lat = Double.valueOf(pos[0]);
|
||||
lon = Double.valueOf(pos[1]);
|
||||
} catch (final NumberFormatException e) {
|
||||
final URISyntaxException use = new URISyntaxException(uri.toString(), "position could not be parsed");
|
||||
use.initCause(e);
|
||||
throw use;
|
||||
if (pos.length == 2) {
|
||||
try {
|
||||
lat = Double.valueOf(pos[0]);
|
||||
lon = Double.valueOf(pos[1]);
|
||||
} catch (final NumberFormatException e) {
|
||||
lat = null;
|
||||
lon = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (parts.length > 1) { // query string present
|
||||
@ -273,21 +287,47 @@ public class IITC_Mobile extends Activity
|
||||
try {
|
||||
z = Integer.valueOf(param.substring(2));
|
||||
} catch (final NumberFormatException e) {
|
||||
final URISyntaxException use = new URISyntaxException(
|
||||
uri.toString(), "could not parse zoom level");
|
||||
use.initCause(e);
|
||||
throw use;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (param.startsWith("q=")) {
|
||||
search = param.substring(2);
|
||||
final Pattern pattern = Pattern.compile("^(-?\\d+(\\.\\d+)?),(-?\\d+(\\.\\d+)?)\\s*\\(.+\\)");
|
||||
final Matcher matcher = pattern.matcher(search);
|
||||
if (matcher.matches()) {
|
||||
try {
|
||||
lat = Double.valueOf(matcher.group(1));
|
||||
lon = Double.valueOf(matcher.group(3));
|
||||
search = null; // if we have a position, we don't need the search term
|
||||
} catch (final NumberFormatException e) {
|
||||
lat = null;
|
||||
lon = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String url = "http://www.ingress.com/intel?ll=" + lat + "," + lon;
|
||||
if (z != null) {
|
||||
url += "&z=" + z;
|
||||
if (lat != null && lon != null) {
|
||||
String url = mIntelUrl + "?ll=" + lat + "," + lon;
|
||||
if (z != null) {
|
||||
url += "&z=" + z;
|
||||
}
|
||||
loadUrl(url);
|
||||
return;
|
||||
}
|
||||
loadUrl(url);
|
||||
|
||||
if (search != null) {
|
||||
if (mIsLoading) {
|
||||
mSearchTerm = search;
|
||||
loadUrl(mIntelUrl);
|
||||
} else {
|
||||
switchToPane(Pane.MAP);
|
||||
mIitcWebView.loadUrl("javascript:search('" + search + "');");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
throw new URISyntaxException(uri.toString(), "position could not be parsed");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -439,8 +479,7 @@ public class IITC_Mobile extends Activity
|
||||
// Get the SearchView and set the searchable configuration
|
||||
final SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
|
||||
mSearchMenuItem = menu.findItem(R.id.menu_search);
|
||||
final SearchView searchView =
|
||||
(SearchView) mSearchMenuItem.getActionView();
|
||||
final SearchView searchView = (SearchView) mSearchMenuItem.getActionView();
|
||||
// Assumes current activity is the searchable activity
|
||||
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
|
||||
searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default
|
||||
@ -450,8 +489,8 @@ public class IITC_Mobile extends Activity
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(final Menu menu) {
|
||||
boolean visible = false;
|
||||
if (mNavigationHelper != null)
|
||||
visible = !mNavigationHelper.isDrawerOpened();
|
||||
if (mNavigationHelper != null) visible = !mNavigationHelper.isDrawerOpened();
|
||||
if (mIsLoading) visible = false;
|
||||
|
||||
for (int i = 0; i < menu.size(); i++) {
|
||||
final MenuItem item = menu.getItem(i);
|
||||
@ -467,6 +506,7 @@ public class IITC_Mobile extends Activity
|
||||
|
||||
case R.id.locate:
|
||||
item.setVisible(visible);
|
||||
item.setEnabled(!mIsLoading);
|
||||
item.setIcon(mUserLocation.isFollowing()
|
||||
? R.drawable.ic_action_location_follow
|
||||
: R.drawable.ic_action_location_found);
|
||||
@ -650,10 +690,20 @@ public class IITC_Mobile extends Activity
|
||||
|
||||
public void setLoadingState(final boolean isLoading) {
|
||||
mIsLoading = isLoading;
|
||||
|
||||
mNavigationHelper.onLoadingStateChanged();
|
||||
|
||||
invalidateOptionsMenu();
|
||||
updateViews();
|
||||
|
||||
if (mSearchTerm != null && !isLoading) {
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// switchToPane(Pane.MAP);
|
||||
mIitcWebView.loadUrl("javascript:search('" + mSearchTerm + "');");
|
||||
mSearchTerm = null;
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateViews() {
|
||||
|
@ -1,18 +1,22 @@
|
||||
package com.cradle.iitc_mobile;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.AssetManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.cradle.iitc_mobile.fragments.PluginsFragment;
|
||||
|
||||
@ -29,6 +33,8 @@ import java.util.TreeMap;
|
||||
|
||||
public class IITC_PluginPreferenceActivity extends PreferenceActivity {
|
||||
|
||||
private final static int COPY_PLUGIN_REQUEST = 1;
|
||||
|
||||
private List<Header> mHeaders;
|
||||
// we use a tree map to have a map with alphabetical order
|
||||
// don't initialize the asset plugin map, because it tells us if the settings are started the first time
|
||||
@ -39,6 +45,8 @@ public class IITC_PluginPreferenceActivity extends PreferenceActivity {
|
||||
new TreeMap<String, ArrayList<IITC_PluginPreference>>();
|
||||
private static int mDeletedPlugins = 0;
|
||||
|
||||
private IITC_FileManager mFileManager;
|
||||
|
||||
@Override
|
||||
public void setListAdapter(final ListAdapter adapter) {
|
||||
if (adapter == null) {
|
||||
@ -78,6 +86,13 @@ public class IITC_PluginPreferenceActivity extends PreferenceActivity {
|
||||
getIntent()
|
||||
.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, PluginsFragment.class.getName());
|
||||
}
|
||||
|
||||
mFileManager = new IITC_FileManager(this);
|
||||
|
||||
final Uri uri = getIntent().getData();
|
||||
if (uri != null) {
|
||||
mFileManager.installPlugin(uri, true);
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@ -103,17 +118,52 @@ public class IITC_PluginPreferenceActivity extends PreferenceActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.plugins, menu);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home: // exit settings when home button (iitc icon) is pressed
|
||||
onBackPressed();
|
||||
return true;
|
||||
case R.id.menu_plugins_add:
|
||||
// create the chooser Intent
|
||||
final Intent target = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
// iitcm only parses *.user.js scripts
|
||||
target.setType("file/*");
|
||||
target.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
|
||||
try {
|
||||
startActivityForResult(Intent.createChooser(target, "Choose file"), COPY_PLUGIN_REQUEST);
|
||||
} catch (final ActivityNotFoundException e) {
|
||||
Toast.makeText(this, "No activity to select a file found." +
|
||||
"Please install a file browser of your choice!", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
switch(requestCode) {
|
||||
case COPY_PLUGIN_REQUEST:
|
||||
if (data != null && data.getData() != null) {
|
||||
mFileManager.installPlugin(data.getData(), true);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidFragment(final String s) {
|
||||
return true;
|
||||
@ -141,9 +191,7 @@ public class IITC_PluginPreferenceActivity extends PreferenceActivity {
|
||||
}
|
||||
|
||||
private File[] getUserPlugins() {
|
||||
final String iitc_path = Environment.getExternalStorageDirectory().getPath()
|
||||
+ "/IITC_Mobile/";
|
||||
final File directory = new File(iitc_path + "plugins/");
|
||||
final File directory = new File(IITC_FileManager.PLUGINS_PATH);
|
||||
File[] files = directory.listFiles();
|
||||
if (files == null) {
|
||||
files = new File[0];
|
||||
@ -164,6 +212,8 @@ public class IITC_PluginPreferenceActivity extends PreferenceActivity {
|
||||
if ((userPlugins.length + officialPlugins.length) != (numPlugins + mDeletedPlugins)) {
|
||||
Log.d("new or less plugins found since last start, rebuild preferences");
|
||||
sAssetPlugins.clear();
|
||||
sUserPlugins.clear();
|
||||
mDeletedPlugins = 0;
|
||||
setUpPluginPreferenceScreen();
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,13 @@ public class IITC_WebView extends WebView {
|
||||
mSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
|
||||
mSettings.setAppCachePath(getContext().getCacheDir().getAbsolutePath());
|
||||
mSettings.setDatabasePath(getContext().getApplicationInfo().dataDir + "/databases/");
|
||||
mJsInterface = new IITC_JSInterface(mIitc);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
mJsInterface = new IITC_JSInterfaceKitkat(mIitc);
|
||||
} else {
|
||||
mJsInterface = new IITC_JSInterface(mIitc);
|
||||
}
|
||||
|
||||
addJavascriptInterface(mJsInterface, "android");
|
||||
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(mIitc);
|
||||
mDefaultUserAgent = mSettings.getUserAgentString();
|
||||
|
@ -95,7 +95,7 @@ public class IITC_WebViewClient extends WebViewClient {
|
||||
public void onReceivedLoginRequest(final WebView view, final String realm, final String account, final String args) {
|
||||
mIitcInjected = false;
|
||||
// Log.d("iitcm", "Login requested: " + realm + " " + account + " " + args);
|
||||
// ((IITC_Mobile) mContext).onReceivedLoginRequest(this, view, realm, account, args);
|
||||
// mIitc.onReceivedLoginRequest(this, view, realm, account, args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,6 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.Bundle;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
@ -23,39 +22,39 @@ import com.cradle.iitc_mobile.R;
|
||||
|
||||
public class MainSettings extends PreferenceFragment {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
|
||||
// set versions
|
||||
String iitcVersion = getArguments().getString("iitc_version");
|
||||
final String iitcVersion = getArguments().getString("iitc_version");
|
||||
|
||||
String buildVersion = "unknown";
|
||||
|
||||
PackageManager pm = getActivity().getPackageManager();
|
||||
final PackageManager pm = getActivity().getPackageManager();
|
||||
try {
|
||||
PackageInfo info = pm.getPackageInfo(getActivity().getPackageName(), 0);
|
||||
final PackageInfo info = pm.getPackageInfo(getActivity().getPackageName(), 0);
|
||||
buildVersion = info.versionName;
|
||||
} catch (NameNotFoundException e) {
|
||||
} catch (final NameNotFoundException e) {
|
||||
Log.w(e);
|
||||
}
|
||||
|
||||
IITC_AboutDialogPreference pref_about = (IITC_AboutDialogPreference) findPreference("pref_about");
|
||||
final IITC_AboutDialogPreference pref_about = (IITC_AboutDialogPreference) findPreference("pref_about");
|
||||
pref_about.setVersions(iitcVersion, buildVersion);
|
||||
|
||||
final ListPreference pref_user_location_mode = (ListPreference) findPreference("pref_user_location_mode");
|
||||
pref_user_location_mode.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
int mode = Integer.parseInt((String) newValue);
|
||||
public boolean onPreferenceChange(final Preference preference, final Object newValue) {
|
||||
final int mode = Integer.parseInt((String) newValue);
|
||||
preference.setSummary(getResources().getStringArray(R.array.pref_user_location_titles)[mode]);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
String value = getPreferenceManager().getSharedPreferences().getString("pref_user_location_mode", "0");
|
||||
int mode = Integer.parseInt(value);
|
||||
final String value = getPreferenceManager().getSharedPreferences().getString("pref_user_location_mode", "0");
|
||||
final int mode = Integer.parseInt(value);
|
||||
pref_user_location_mode.setSummary(getResources().getStringArray(R.array.pref_user_location_titles)[mode]);
|
||||
}
|
||||
|
||||
@ -64,7 +63,7 @@ public class MainSettings extends PreferenceFragment {
|
||||
// so we need some additional hacks...
|
||||
// thx to http://stackoverflow.com/a/16800527/2638486 !!
|
||||
@Override
|
||||
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
|
||||
public boolean onPreferenceTreeClick(final PreferenceScreen preferenceScreen, final Preference preference) {
|
||||
if (preference.getTitle().toString().equals(getString(R.string.pref_advanced_options))
|
||||
|| preference.getTitle().toString().equals(getString(R.string.pref_about_title))) {
|
||||
initializeActionBar((PreferenceScreen) preference);
|
||||
@ -76,27 +75,27 @@ public class MainSettings extends PreferenceFragment {
|
||||
// because PreferenceScreens are dialogs which swallow
|
||||
// events instead of passing to the activity
|
||||
// Related Issue: https://code.google.com/p/android/issues/detail?id=4611
|
||||
public static void initializeActionBar(PreferenceScreen preferenceScreen) {
|
||||
public static void initializeActionBar(final PreferenceScreen preferenceScreen) {
|
||||
final Dialog dialog = preferenceScreen.getDialog();
|
||||
|
||||
if (dialog != null) {
|
||||
if (dialog.getActionBar() != null) dialog.getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
View homeBtn = dialog.findViewById(android.R.id.home);
|
||||
final View homeBtn = dialog.findViewById(android.R.id.home);
|
||||
|
||||
if (homeBtn != null) {
|
||||
View.OnClickListener dismissDialogClickListener = new View.OnClickListener() {
|
||||
final View.OnClickListener dismissDialogClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
public void onClick(final View v) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
};
|
||||
|
||||
ViewParent homeBtnContainer = homeBtn.getParent();
|
||||
final ViewParent homeBtnContainer = homeBtn.getParent();
|
||||
|
||||
// The home button is an ImageView inside a FrameLayout
|
||||
if (homeBtnContainer instanceof FrameLayout) {
|
||||
ViewGroup containerParent = (ViewGroup) homeBtnContainer.getParent();
|
||||
final ViewGroup containerParent = (ViewGroup) homeBtnContainer.getParent();
|
||||
|
||||
if (containerParent instanceof LinearLayout) {
|
||||
// This view also contains the title text, set the whole view as clickable
|
||||
|
@ -7,16 +7,16 @@ import android.support.v4.app.FragmentPagerAdapter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class IntentFragmentAdapter extends FragmentPagerAdapter {
|
||||
private final List<IntentFragment> mTabs;
|
||||
public class FragmentAdapter extends FragmentPagerAdapter {
|
||||
private final List<IntentListFragment> mTabs;
|
||||
|
||||
public IntentFragmentAdapter(FragmentManager fm) {
|
||||
public FragmentAdapter(final FragmentManager fm) {
|
||||
super(fm);
|
||||
|
||||
mTabs = new ArrayList<IntentFragment>();
|
||||
mTabs = new ArrayList<IntentListFragment>();
|
||||
}
|
||||
|
||||
public void add(IntentFragment fragment) {
|
||||
public void add(final IntentListFragment fragment) {
|
||||
mTabs.add(fragment);
|
||||
}
|
||||
|
||||
@ -26,12 +26,12 @@ public class IntentFragmentAdapter extends FragmentPagerAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
public Fragment getItem(final int position) {
|
||||
return mTabs.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
public CharSequence getPageTitle(final int position) {
|
||||
return mTabs.get(position).getTitle();
|
||||
}
|
||||
}
|
68
mobile/src/com/cradle/iitc_mobile/share/IntentAdapter.java
Normal file
68
mobile/src/com/cradle/iitc_mobile/share/IntentAdapter.java
Normal file
@ -0,0 +1,68 @@
|
||||
package com.cradle.iitc_mobile.share;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.cradle.iitc_mobile.Log;
|
||||
import com.cradle.iitc_mobile.R;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
class IntentAdapter extends ArrayAdapter<Intent> {
|
||||
private static final int MDPI_PX = 36;
|
||||
|
||||
private final PackageManager mPackageManager;
|
||||
|
||||
public IntentAdapter(final Context context) {
|
||||
super(context, android.R.layout.simple_list_item_1);
|
||||
mPackageManager = getContext().getPackageManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final int position, final View convertView, final ViewGroup parent) {
|
||||
final LayoutInflater inflater = ((Activity) getContext()).getLayoutInflater();
|
||||
final TextView view = (TextView) inflater.inflate(android.R.layout.simple_list_item_1, parent, false);
|
||||
|
||||
final Intent item = getItem(position);
|
||||
|
||||
view.setText(IntentGenerator.getTitle(item));
|
||||
view.setCompoundDrawablePadding((int) getContext().getResources().getDimension(R.dimen.icon_margin));
|
||||
|
||||
// get icon and scale it manually to ensure that all have the same size
|
||||
final DisplayMetrics dm = new DisplayMetrics();
|
||||
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(dm);
|
||||
final float densityScale = dm.density;
|
||||
final float scaledWidth = MDPI_PX * densityScale;
|
||||
final float scaledHeight = MDPI_PX * densityScale;
|
||||
|
||||
try {
|
||||
final Drawable icon = mPackageManager.getActivityIcon(item);
|
||||
icon.setBounds(0, 0, Math.round(scaledWidth), Math.round(scaledHeight));
|
||||
view.setCompoundDrawables(icon, null, null, null);
|
||||
} catch (final NameNotFoundException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public void setIntents(final List<Intent> intents) {
|
||||
Collections.sort(intents, ((ShareActivity) getContext()).getIntentComparator());
|
||||
|
||||
setNotifyOnChange(false);
|
||||
clear();
|
||||
addAll(intents);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
package com.cradle.iitc_mobile.share;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ResolveInfo;
|
||||
|
||||
import com.cradle.iitc_mobile.Log;
|
||||
@ -16,7 +17,112 @@ import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class IntentComparator implements Comparator<ResolveInfo> {
|
||||
public class IntentComparator implements Comparator<Intent> {
|
||||
private static final String INTENT_MAP_FILE = "share_intent_map";
|
||||
|
||||
private final ShareActivity mActivity;
|
||||
|
||||
private HashMap<Component, Integer> mIntentMap = new HashMap<Component, Integer>();
|
||||
|
||||
IntentComparator(final ShareActivity activity) {
|
||||
mActivity = activity;
|
||||
load();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void load() {
|
||||
ObjectInputStream objectIn = null;
|
||||
|
||||
try {
|
||||
final FileInputStream fileIn = mActivity.openFileInput(INTENT_MAP_FILE);
|
||||
objectIn = new ObjectInputStream(fileIn);
|
||||
mIntentMap = (HashMap<Component, Integer>) objectIn.readObject();
|
||||
} catch (final FileNotFoundException e) {
|
||||
// Do nothing
|
||||
} catch (final IOException e) {
|
||||
Log.w(e);
|
||||
} catch (final ClassNotFoundException e) {
|
||||
Log.w(e);
|
||||
} finally {
|
||||
if (objectIn != null) {
|
||||
try {
|
||||
objectIn.close();
|
||||
} catch (final IOException e) {
|
||||
Log.w(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(final Intent lhs, final Intent rhs) {
|
||||
int order;
|
||||
|
||||
// we might be merging multiple intents, so there could be more than one default
|
||||
if (IntentGenerator.isDefault(lhs) && !IntentGenerator.isDefault(rhs))
|
||||
return -1;
|
||||
if (IntentGenerator.isDefault(rhs) && !IntentGenerator.isDefault(lhs))
|
||||
return 1;
|
||||
|
||||
final ComponentName lComponent = lhs.getComponent();
|
||||
final ComponentName rComponent = rhs.getComponent();
|
||||
|
||||
// Show more frequently used items in top
|
||||
Integer lCount = mIntentMap.get(new Component(lComponent));
|
||||
Integer rCount = mIntentMap.get(new Component(rComponent));
|
||||
|
||||
if (lCount == null) lCount = 0;
|
||||
if (rCount == null) rCount = 0;
|
||||
|
||||
if (lCount > rCount) return -1;
|
||||
if (lCount < rCount) return 1;
|
||||
|
||||
// still no order. fall back to alphabetical order
|
||||
order = IntentGenerator.getTitle(lhs).compareTo(IntentGenerator.getTitle(rhs));
|
||||
if (order != 0) return order;
|
||||
|
||||
order = lComponent.getPackageName().compareTo(rComponent.getPackageName());
|
||||
if (order != 0) return order;
|
||||
|
||||
order = lComponent.getClassName().compareTo(rComponent.getClassName());
|
||||
if (order != 0) return order;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
ObjectOutputStream objectOut = null;
|
||||
try {
|
||||
final FileOutputStream fileOut = mActivity.openFileOutput(INTENT_MAP_FILE, Activity.MODE_PRIVATE);
|
||||
objectOut = new ObjectOutputStream(fileOut);
|
||||
objectOut.writeObject(mIntentMap);
|
||||
fileOut.getFD().sync();
|
||||
} catch (final IOException e) {
|
||||
Log.w(e);
|
||||
} finally {
|
||||
if (objectOut != null) {
|
||||
try {
|
||||
objectOut.close();
|
||||
} catch (final IOException e) {
|
||||
Log.w(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void trackIntentSelection(final Intent intent) {
|
||||
final Component component = new Component(intent.getComponent());
|
||||
|
||||
Integer counter = mIntentMap.get(component);
|
||||
if (counter == null) {
|
||||
counter = 1;
|
||||
} else {
|
||||
counter++;
|
||||
}
|
||||
|
||||
mIntentMap.put(component, counter);
|
||||
}
|
||||
|
||||
public static class Component implements Serializable {
|
||||
private static final long serialVersionUID = -5043782754318376792L;
|
||||
|
||||
@ -28,19 +134,24 @@ public class IntentComparator implements Comparator<ResolveInfo> {
|
||||
packageName = null;
|
||||
}
|
||||
|
||||
public Component(ResolveInfo info) {
|
||||
public Component(final ComponentName cn) {
|
||||
name = cn.getClassName();
|
||||
packageName = cn.getPackageName();
|
||||
}
|
||||
|
||||
public Component(final ResolveInfo info) {
|
||||
name = info.activityInfo.name;
|
||||
packageName = info.activityInfo.applicationInfo.packageName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
if (o == null) return false;
|
||||
if (o.getClass() != this.getClass()) return false;
|
||||
|
||||
Component c = (Component) o;
|
||||
final Component c = (Component) o;
|
||||
|
||||
if (name == null) {
|
||||
if (c.name != null) return false;
|
||||
@ -83,120 +194,4 @@ public class IntentComparator implements Comparator<ResolveInfo> {
|
||||
: name);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String INTENT_MAP_FILE = "share_intent_map";
|
||||
|
||||
private ShareActivity mActivity;
|
||||
private HashMap<Component, Integer> mIntentMap = new HashMap<Component, Integer>();
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
IntentComparator(ShareActivity activity) {
|
||||
mActivity = activity;
|
||||
mPackageManager = activity.getPackageManager();
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void load() {
|
||||
ObjectInputStream objectIn = null;
|
||||
|
||||
try {
|
||||
FileInputStream fileIn = mActivity.openFileInput(INTENT_MAP_FILE);
|
||||
objectIn = new ObjectInputStream(fileIn);
|
||||
mIntentMap = (HashMap<Component, Integer>) objectIn.readObject();
|
||||
} catch (FileNotFoundException e) {
|
||||
// Do nothing
|
||||
} catch (IOException e) {
|
||||
Log.w(e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.w(e);
|
||||
} finally {
|
||||
if (objectIn != null) {
|
||||
try {
|
||||
objectIn.close();
|
||||
} catch (IOException e) {
|
||||
Log.w(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(ResolveInfo lhs, ResolveInfo rhs) {
|
||||
int order;
|
||||
|
||||
// we might be merging multiple intents, so there could be more than one default
|
||||
if (lhs.isDefault && !rhs.isDefault)
|
||||
return -1;
|
||||
if (rhs.isDefault && !lhs.isDefault)
|
||||
return 1;
|
||||
|
||||
// Show more frequently used items in top
|
||||
Integer lCount = mIntentMap.get(new Component(lhs));
|
||||
Integer rCount = mIntentMap.get(new Component(rhs));
|
||||
|
||||
if (lCount == null) lCount = 0;
|
||||
if (rCount == null) rCount = 0;
|
||||
|
||||
if (lCount > rCount) return -1;
|
||||
if (lCount < rCount) return 1;
|
||||
|
||||
// don't known how these are set (or if they can be set at all), but it sounds promising...
|
||||
if (lhs.preferredOrder != rhs.preferredOrder)
|
||||
return rhs.preferredOrder - lhs.preferredOrder;
|
||||
if (lhs.priority != rhs.priority)
|
||||
return rhs.priority - lhs.priority;
|
||||
|
||||
// still no order. fall back to alphabetical order
|
||||
order = lhs.loadLabel(mPackageManager).toString().compareTo(
|
||||
rhs.loadLabel(mPackageManager).toString());
|
||||
if (order != 0) return order;
|
||||
|
||||
if (lhs.nonLocalizedLabel != null && rhs.nonLocalizedLabel != null) {
|
||||
order = lhs.nonLocalizedLabel.toString().compareTo(rhs.nonLocalizedLabel.toString());
|
||||
if (order != 0) return order;
|
||||
}
|
||||
|
||||
order = lhs.activityInfo.packageName.compareTo(rhs.activityInfo.packageName);
|
||||
if (order != 0) return order;
|
||||
|
||||
order = lhs.activityInfo.name.compareTo(rhs.activityInfo.name);
|
||||
if (order != 0) return order;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
ObjectOutputStream objectOut = null;
|
||||
try {
|
||||
FileOutputStream fileOut = mActivity.openFileOutput(INTENT_MAP_FILE, Activity.MODE_PRIVATE);
|
||||
objectOut = new ObjectOutputStream(fileOut);
|
||||
objectOut.writeObject(mIntentMap);
|
||||
fileOut.getFD().sync();
|
||||
} catch (IOException e) {
|
||||
Log.w(e);
|
||||
} finally {
|
||||
if (objectOut != null) {
|
||||
try {
|
||||
objectOut.close();
|
||||
} catch (IOException e) {
|
||||
Log.w(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void trackIntentSelection(ResolveInfo info) {
|
||||
Component component = new Component(info);
|
||||
|
||||
Integer counter = mIntentMap.get(component);
|
||||
if (counter == null) {
|
||||
counter = 1;
|
||||
} else {
|
||||
counter++;
|
||||
}
|
||||
|
||||
mIntentMap.put(component, counter);
|
||||
}
|
||||
}
|
148
mobile/src/com/cradle/iitc_mobile/share/IntentGenerator.java
Normal file
148
mobile/src/com/cradle/iitc_mobile/share/IntentGenerator.java
Normal file
@ -0,0 +1,148 @@
|
||||
package com.cradle.iitc_mobile.share;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.cradle.iitc_mobile.Log;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
public class IntentGenerator {
|
||||
private static final String EXTRA_FLAG_IS_DEFAULT = "IITCM_IS_DEFAULT";
|
||||
private static final String EXTRA_FLAG_TITLE = "IITCM_TITLE";
|
||||
private static final HashSet<ComponentName> KNOWN_COPY_HANDLERS = new HashSet<ComponentName>();
|
||||
|
||||
static {
|
||||
if (KNOWN_COPY_HANDLERS.isEmpty()) {
|
||||
|
||||
KNOWN_COPY_HANDLERS.add(new ComponentName(
|
||||
"com.google.android.apps.docs",
|
||||
"com.google.android.apps.docs.app.SendTextToClipboardActivity"));
|
||||
|
||||
KNOWN_COPY_HANDLERS.add(new ComponentName(
|
||||
"com.aokp.romcontrol",
|
||||
"com.aokp.romcontrol.ShareToClipboard"));
|
||||
}
|
||||
}
|
||||
|
||||
public static String getTitle(final Intent intent) {
|
||||
if (intent.hasExtra(EXTRA_FLAG_TITLE))
|
||||
return intent.getStringExtra(EXTRA_FLAG_TITLE);
|
||||
|
||||
throw new IllegalArgumentException("Got an intent not generated by IntentGenerator");
|
||||
}
|
||||
|
||||
public static boolean isDefault(final Intent intent) {
|
||||
return intent.hasExtra(EXTRA_FLAG_IS_DEFAULT) && intent.getBooleanExtra(EXTRA_FLAG_IS_DEFAULT, false);
|
||||
}
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
private final PackageManager mPackageManager;
|
||||
|
||||
public IntentGenerator(final Context context) {
|
||||
mContext = context;
|
||||
mPackageManager = mContext.getPackageManager();
|
||||
}
|
||||
|
||||
private boolean containsCopyIntent(final List<Intent> targets) {
|
||||
for (final Intent intent : targets) {
|
||||
for (final ComponentName handler : KNOWN_COPY_HANDLERS) {
|
||||
if (handler.equals(intent.getComponent())) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private ArrayList<Intent> resolveTargets(final Intent intent) {
|
||||
final String packageName = mContext.getPackageName();
|
||||
final List<ResolveInfo> activityList = mPackageManager.queryIntentActivities(intent, 0);
|
||||
final ResolveInfo defaultTarget = mPackageManager.resolveActivity(intent, 0);
|
||||
|
||||
final ArrayList<Intent> list = new ArrayList<Intent>(activityList.size());
|
||||
|
||||
for (final ResolveInfo resolveInfo : activityList) {
|
||||
final ActivityInfo activity = resolveInfo.activityInfo;
|
||||
final ComponentName component = new ComponentName(activity.packageName, activity.name);
|
||||
|
||||
// remove IITCm from list
|
||||
if (activity.packageName.equals(packageName)) continue;
|
||||
|
||||
final Intent targetIntent = new Intent(intent)
|
||||
.setComponent(component)
|
||||
.putExtra(EXTRA_FLAG_TITLE, activity.loadLabel(mPackageManager));
|
||||
|
||||
if (resolveInfo.activityInfo.name.equals(defaultTarget.activityInfo.name) &&
|
||||
resolveInfo.activityInfo.packageName.equals(defaultTarget.activityInfo.packageName)) {
|
||||
targetIntent.putExtra(EXTRA_FLAG_IS_DEFAULT, true);
|
||||
}
|
||||
|
||||
list.add(targetIntent);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public void cleanup(final Intent intent) {
|
||||
intent.removeExtra(EXTRA_FLAG_IS_DEFAULT);
|
||||
intent.removeExtra(EXTRA_FLAG_TITLE);
|
||||
}
|
||||
|
||||
public ArrayList<Intent> getBrowserIntents(final String title, final String url) {
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||
|
||||
return resolveTargets(intent);
|
||||
}
|
||||
|
||||
public ArrayList<Intent> getGeoIntents(final String title, final String mLl, final int mZoom) {
|
||||
final Intent intent = new Intent(android.content.Intent.ACTION_VIEW,
|
||||
Uri.parse(String.format("geo:%s?z=%d", mLl, mZoom)))
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||
|
||||
final ArrayList<Intent> targets = resolveTargets(intent);
|
||||
|
||||
// According to https://developer.android.com/guide/components/intents-common.html, markers can be labeled.
|
||||
// Unfortunately, only Google Maps supports this, most other apps fail
|
||||
for (final Intent target : targets) {
|
||||
final ComponentName cn = target.getComponent();
|
||||
if ("com.google.android.apps.maps".equals(cn.getPackageName())) {
|
||||
try {
|
||||
final String encodedTitle = URLEncoder.encode(title, "UTF-8");
|
||||
target.setData(Uri.parse(String.format("geo:0,0?q=%s%%20(%s)&z=%d", mLl, encodedTitle, mZoom)));
|
||||
} catch (final UnsupportedEncodingException e) {
|
||||
Log.w(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
public ArrayList<Intent> getShareIntents(final String title, final String text) {
|
||||
final Intent intent = new Intent(Intent.ACTION_SEND)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
|
||||
.setType("text/plain")
|
||||
.putExtra(Intent.EXTRA_TEXT, text)
|
||||
.putExtra(Intent.EXTRA_SUBJECT, title);
|
||||
|
||||
final ArrayList<Intent> targets = resolveTargets(intent);
|
||||
|
||||
if (!containsCopyIntent(targets)) {
|
||||
// add SendToClipboard intent in case Drive is not installed
|
||||
targets.add(new Intent(intent).setComponent(new ComponentName(mContext, SendToClipboard.class)));
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
}
|
@ -10,12 +10,14 @@ import android.widget.AbsListView;
|
||||
import android.widget.AbsListView.OnScrollListener;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ListView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class IntentFragment extends Fragment implements OnScrollListener, OnItemClickListener {
|
||||
public class IntentListFragment extends Fragment implements OnScrollListener, OnItemClickListener {
|
||||
private IntentAdapter mAdapter;
|
||||
private ArrayList<Intent> mIntents;
|
||||
private IntentListView mListView;
|
||||
private ListView mListView;
|
||||
private int mScrollIndex, mScrollTop;
|
||||
|
||||
public int getIcon() {
|
||||
@ -27,12 +29,16 @@ public class IntentFragment extends Fragment implements OnScrollListener, OnItem
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
Bundle args = getArguments();
|
||||
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
|
||||
final Bundle args = getArguments();
|
||||
|
||||
mIntents = args.getParcelableArrayList("intents");
|
||||
mListView = new IntentListView(getActivity());
|
||||
mListView.setIntents(mIntents);
|
||||
|
||||
mAdapter = new IntentAdapter(getActivity());
|
||||
mAdapter.setIntents(mIntents);
|
||||
|
||||
mListView = new ListView(getActivity());
|
||||
mListView.setAdapter(mAdapter);
|
||||
if (mScrollIndex != -1 && mScrollTop != -1) {
|
||||
mListView.setSelectionFromTop(mScrollIndex, mScrollTop);
|
||||
}
|
||||
@ -43,23 +49,18 @@ public class IntentFragment extends Fragment implements OnScrollListener, OnItem
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
((ShareActivity) getActivity()).getIntentComparator().trackIntentSelection(mListView.getItem(position));
|
||||
|
||||
Intent intent = mListView.getTargetIntent(position);
|
||||
startActivity(intent);
|
||||
|
||||
getActivity().finish();
|
||||
public void onItemClick(final AdapterView<?> parent, final View view, final int position, final long id) {
|
||||
((ShareActivity) getActivity()).launch(mAdapter.getItem(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
|
||||
public void onScroll(final AbsListView lv, final int firstItem, final int visibleItems, final int totalItems) {
|
||||
mScrollIndex = mListView.getFirstVisiblePosition();
|
||||
View v = mListView.getChildAt(0);
|
||||
final View v = mListView.getChildAt(0);
|
||||
mScrollTop = (v == null) ? 0 : v.getTop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(AbsListView view, int scrollState) {
|
||||
public void onScrollStateChanged(final AbsListView view, final int scrollState) {
|
||||
}
|
||||
}
|
@ -1,209 +0,0 @@
|
||||
package com.cradle.iitc_mobile.share;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Pair;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.cradle.iitc_mobile.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
public class IntentListView extends ListView {
|
||||
private static class CopyHandler extends Pair<String, String> {
|
||||
public CopyHandler(ResolveInfo resolveInfo) {
|
||||
super(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name);
|
||||
}
|
||||
|
||||
public CopyHandler(String packageName, String name) {
|
||||
super(packageName, name);
|
||||
}
|
||||
}
|
||||
|
||||
private class IntentAdapter extends ArrayAdapter<ResolveInfo> {
|
||||
|
||||
// actually the mdpi pixel size is 48, but this looks ugly...so scale icons down for listView
|
||||
private static final int MDPI_PX = 36;
|
||||
|
||||
private IntentAdapter() {
|
||||
super(IntentListView.this.getContext(), android.R.layout.simple_list_item_1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
LayoutInflater inflater = ((Activity) getContext()).getLayoutInflater();
|
||||
TextView view = (TextView) inflater.inflate(android.R.layout.simple_list_item_1, parent, false);
|
||||
|
||||
ActivityInfo info = getItem(position).activityInfo;
|
||||
CharSequence label = info.loadLabel(mPackageManager);
|
||||
|
||||
// get icon and scale it manually to ensure that all have the same size
|
||||
Drawable icon = info.loadIcon(mPackageManager);
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(dm);
|
||||
float densityScale = dm.density;
|
||||
float scaledWidth = MDPI_PX * densityScale;
|
||||
float scaledHeight = MDPI_PX * densityScale;
|
||||
icon.setBounds(0,0,Math.round(scaledWidth),Math.round(scaledHeight));
|
||||
|
||||
view.setText(label);
|
||||
view.setCompoundDrawablePadding((int) getResources().getDimension(R.dimen.icon_margin));
|
||||
view.setCompoundDrawables(icon, null, null, null);
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
private static final HashSet<CopyHandler> KNOWN_COPY_HANDLERS = new HashSet<CopyHandler>();
|
||||
|
||||
static {
|
||||
if (KNOWN_COPY_HANDLERS.isEmpty()) {
|
||||
|
||||
KNOWN_COPY_HANDLERS.add(new CopyHandler(
|
||||
"com.google.android.apps.docs",
|
||||
"com.google.android.apps.docs.app.SendTextToClipboardActivity"));
|
||||
|
||||
KNOWN_COPY_HANDLERS.add(new CopyHandler(
|
||||
"com.aokp.romcontrol",
|
||||
"com.aokp.romcontrol.ShareToClipboard"));
|
||||
}
|
||||
}
|
||||
|
||||
private HashMap<ComponentName, Intent> mActivities = new HashMap<ComponentName, Intent>();
|
||||
|
||||
private IntentAdapter mAdapter;
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
public IntentListView(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public IntentListView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public IntentListView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mPackageManager = getContext().getPackageManager();
|
||||
mAdapter = new IntentAdapter();
|
||||
setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
public ResolveInfo getItem(int position) {
|
||||
return mAdapter.getItem(position);
|
||||
}
|
||||
|
||||
public Intent getTargetIntent(int position) {
|
||||
ActivityInfo activity = mAdapter.getItem(position).activityInfo;
|
||||
|
||||
ComponentName activityId = new ComponentName(activity.packageName, activity.name);
|
||||
|
||||
Intent intentType = mActivities.get(activityId);
|
||||
|
||||
Intent intent = new Intent(intentType)
|
||||
.setComponent(activityId)
|
||||
.setPackage(activity.packageName);
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
// wrapper method for single intents
|
||||
public void setIntent(Intent intent) {
|
||||
ArrayList<Intent> intentList = new ArrayList<Intent>(1);
|
||||
intentList.add(intent);
|
||||
setIntents(intentList);
|
||||
}
|
||||
|
||||
public void setIntents(ArrayList<Intent> intents) {
|
||||
mAdapter.setNotifyOnChange(false);
|
||||
mAdapter.clear();
|
||||
|
||||
String packageName = getContext().getPackageName();
|
||||
|
||||
ArrayList<ResolveInfo> allActivities = new ArrayList<ResolveInfo>();
|
||||
|
||||
for (Intent intent : intents) {
|
||||
List<ResolveInfo> activityList = mPackageManager.queryIntentActivities(intent, 0);
|
||||
|
||||
ResolveInfo defaultTarget = mPackageManager.resolveActivity(intent, 0);
|
||||
|
||||
boolean hasCopyIntent = false;
|
||||
for (ResolveInfo resolveInfo : activityList) { // search for "Copy to clipboard" handler
|
||||
CopyHandler handler = new CopyHandler(resolveInfo);
|
||||
|
||||
if (KNOWN_COPY_HANDLERS.contains(handler)) {
|
||||
hasCopyIntent = true;
|
||||
}
|
||||
}
|
||||
|
||||
// use traditional loop since list may change during iteration
|
||||
for (int i = 0; i < activityList.size(); i++) {
|
||||
ResolveInfo info = activityList.get(i);
|
||||
ActivityInfo activity = info.activityInfo;
|
||||
|
||||
// fix bug in PackageManager - a replaced package name might cause non-exported intents to appear
|
||||
if (!activity.exported && !activity.packageName.equals(packageName)) {
|
||||
activityList.remove(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// remove all IITCm intents, except for SendToClipboard in case Drive is not installed
|
||||
if (activity.packageName.equals(packageName)) {
|
||||
if (hasCopyIntent || !activity.name.equals(SendToClipboard.class.getCanonicalName())) {
|
||||
activityList.remove(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add to activity hash map if they doesn't exist
|
||||
for (ResolveInfo resolveInfo : activityList) {
|
||||
|
||||
ActivityInfo activity = resolveInfo.activityInfo;
|
||||
ComponentName activityId = new ComponentName(activity.packageName, activity.name);
|
||||
|
||||
// ResolveInfo.isDefault usually means "The target would like to be considered a default action that the
|
||||
// user can perform on this data." It is set by the package manager, but we overwrite it to store
|
||||
// whether this app is the default for the given intent
|
||||
resolveInfo.isDefault = resolveInfo.activityInfo.name.equals(defaultTarget.activityInfo.name)
|
||||
&& resolveInfo.activityInfo.packageName.equals(defaultTarget.activityInfo.packageName);
|
||||
|
||||
if (!mActivities.containsKey(activityId)) {
|
||||
mActivities.put(activityId, intent);
|
||||
allActivities.add(resolveInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(allActivities, ((ShareActivity) getContext()).getIntentComparator());
|
||||
|
||||
mAdapter.addAll(allActivities);
|
||||
mAdapter.setNotifyOnChange(true);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@ import android.app.ActionBar;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
@ -12,16 +11,14 @@ import android.support.v4.app.NavUtils;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.cradle.iitc_mobile.Log;
|
||||
import com.cradle.iitc_mobile.R;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ShareActivity extends FragmentActivity implements ActionBar.TabListener {
|
||||
private IntentComparator mComparator;
|
||||
private IntentFragmentAdapter mFragmentAdapter;
|
||||
private FragmentAdapter mFragmentAdapter;
|
||||
private IntentGenerator mGenerator;
|
||||
private boolean mIsPortal;
|
||||
private String mLl;
|
||||
private SharedPreferences mSharedPrefs = null;
|
||||
@ -29,9 +26,9 @@ public class ShareActivity extends FragmentActivity implements ActionBar.TabList
|
||||
private ViewPager mViewPager;
|
||||
private int mZoom;
|
||||
|
||||
private void addTab(ArrayList<Intent> intents, int label, int icon) {
|
||||
IntentFragment fragment = new IntentFragment();
|
||||
Bundle args = new Bundle();
|
||||
private void addTab(final ArrayList<Intent> intents, final int label, final int icon) {
|
||||
final IntentListFragment fragment = new IntentListFragment();
|
||||
final Bundle args = new Bundle();
|
||||
args.putParcelableArrayList("intents", intents);
|
||||
args.putString("title", getString(label));
|
||||
args.putInt("icon", icon);
|
||||
@ -39,13 +36,7 @@ public class ShareActivity extends FragmentActivity implements ActionBar.TabList
|
||||
mFragmentAdapter.add(fragment);
|
||||
}
|
||||
|
||||
private void addTab(Intent intent, int label, int icon) {
|
||||
ArrayList<Intent> intents = new ArrayList<Intent>(1);
|
||||
intents.add(intent);
|
||||
addTab(intents, label, icon);
|
||||
}
|
||||
|
||||
private String getUrl() {
|
||||
private String getIntelUrl() {
|
||||
String url = "http://www.ingress.com/intel?ll=" + mLl + "&z=" + mZoom;
|
||||
if (mIsPortal) {
|
||||
url += "&pll=" + mLl;
|
||||
@ -53,7 +44,7 @@ public class ShareActivity extends FragmentActivity implements ActionBar.TabList
|
||||
return url;
|
||||
}
|
||||
|
||||
private void setSelected(int position) {
|
||||
private void setSelected(final int position) {
|
||||
// Activity not fully loaded yet (may occur during tab creation)
|
||||
if (mSharedPrefs == null) return;
|
||||
|
||||
@ -63,53 +54,20 @@ public class ShareActivity extends FragmentActivity implements ActionBar.TabList
|
||||
.apply();
|
||||
}
|
||||
|
||||
private void setupIntents() {
|
||||
setupShareIntent(getUrl());
|
||||
|
||||
// we merge gmaps intents with geo intents since it is not possible
|
||||
// anymore to set a labeled marker on geo intents
|
||||
ArrayList<Intent> intents = new ArrayList<Intent>();
|
||||
String gMapsUri;
|
||||
try {
|
||||
gMapsUri = "http://maps.google.com/?q=loc:" + mLl
|
||||
+ "%20(" + URLEncoder.encode(mTitle, "UTF-8") + ")&z=" + mZoom;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
gMapsUri = "http://maps.google.com/?ll=" + mLl + "&z=" + mZoom;
|
||||
Log.w(e);
|
||||
}
|
||||
Intent gMapsIntent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(gMapsUri));
|
||||
intents.add(gMapsIntent);
|
||||
String geoUri = "geo:" + mLl;
|
||||
Intent geoIntent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(geoUri));
|
||||
intents.add(geoIntent);
|
||||
addTab(intents, R.string.tab_map, R.drawable.ic_action_place);
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getUrl()));
|
||||
addTab(intent, R.string.tab_browser, R.drawable.ic_action_web_site);
|
||||
}
|
||||
|
||||
private void setupShareIntent(String str) {
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||
intent.setType("text/plain");
|
||||
intent.putExtra(Intent.EXTRA_TEXT, str);
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, mTitle);
|
||||
addTab(intent, R.string.tab_share, R.drawable.ic_action_share);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_share);
|
||||
|
||||
mComparator = new IntentComparator(this);
|
||||
mGenerator = new IntentGenerator(this);
|
||||
|
||||
mFragmentAdapter = new IntentFragmentAdapter(getSupportFragmentManager());
|
||||
mFragmentAdapter = new FragmentAdapter(getSupportFragmentManager());
|
||||
|
||||
final ActionBar actionBar = getActionBar();
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
Intent intent = getIntent();
|
||||
final Intent intent = getIntent();
|
||||
// from portallinks/permalinks we build 3 intents (share / geo / vanilla-intel-link)
|
||||
if (!intent.getBooleanExtra("onlyShare", false)) {
|
||||
mTitle = intent.getStringExtra("title");
|
||||
@ -118,10 +76,21 @@ public class ShareActivity extends FragmentActivity implements ActionBar.TabList
|
||||
mIsPortal = intent.getBooleanExtra("isPortal", false);
|
||||
|
||||
actionBar.setTitle(mTitle);
|
||||
setupIntents();
|
||||
|
||||
addTab(mGenerator.getShareIntents(mTitle, getIntelUrl()),
|
||||
R.string.tab_share,
|
||||
R.drawable.ic_action_share);
|
||||
addTab(mGenerator.getGeoIntents(mTitle, mLl, mZoom),
|
||||
R.string.tab_map,
|
||||
R.drawable.ic_action_place);
|
||||
addTab(mGenerator.getBrowserIntents(mTitle, getIntelUrl()),
|
||||
R.string.tab_browser,
|
||||
R.drawable.ic_action_web_site);
|
||||
} else {
|
||||
mTitle = getString(R.string.app_name);
|
||||
setupShareIntent(intent.getStringExtra("shareString"));
|
||||
final String shareString = intent.getStringExtra("shareString");
|
||||
|
||||
addTab(mGenerator.getShareIntents(mTitle, shareString), R.string.tab_share, R.drawable.ic_action_share);
|
||||
}
|
||||
|
||||
mViewPager = (ViewPager) findViewById(R.id.pager);
|
||||
@ -129,7 +98,7 @@ public class ShareActivity extends FragmentActivity implements ActionBar.TabList
|
||||
|
||||
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
public void onPageSelected(final int position) {
|
||||
if (actionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_STANDARD) {
|
||||
actionBar.setSelectedNavigationItem(position);
|
||||
}
|
||||
@ -138,7 +107,7 @@ public class ShareActivity extends FragmentActivity implements ActionBar.TabList
|
||||
});
|
||||
|
||||
for (int i = 0; i < mFragmentAdapter.getCount(); i++) {
|
||||
IntentFragment fragment = (IntentFragment) mFragmentAdapter.getItem(i);
|
||||
final IntentListFragment fragment = (IntentListFragment) mFragmentAdapter.getItem(i);
|
||||
|
||||
actionBar.addTab(actionBar
|
||||
.newTab()
|
||||
@ -152,7 +121,7 @@ public class ShareActivity extends FragmentActivity implements ActionBar.TabList
|
||||
}
|
||||
|
||||
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
int selected = mSharedPrefs.getInt("pref_share_selected_tab", 0);
|
||||
final int selected = mSharedPrefs.getInt("pref_share_selected_tab", 0);
|
||||
if (selected < mFragmentAdapter.getCount()) {
|
||||
mViewPager.setCurrentItem(selected);
|
||||
if (actionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_STANDARD) {
|
||||
@ -171,8 +140,15 @@ public class ShareActivity extends FragmentActivity implements ActionBar.TabList
|
||||
return mComparator;
|
||||
}
|
||||
|
||||
public void launch(final Intent intent) {
|
||||
mComparator.trackIntentSelection(intent);
|
||||
mGenerator.cleanup(intent);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
NavUtils.navigateUpFromSameTask(this);
|
||||
@ -182,17 +158,17 @@ public class ShareActivity extends FragmentActivity implements ActionBar.TabList
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
|
||||
public void onTabReselected(final ActionBar.Tab tab, final FragmentTransaction fragmentTransaction) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
|
||||
int position = tab.getPosition();
|
||||
public void onTabSelected(final ActionBar.Tab tab, final FragmentTransaction fragmentTransaction) {
|
||||
final int position = tab.getPosition();
|
||||
mViewPager.setCurrentItem(position);
|
||||
setSelected(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
|
||||
public void onTabUnselected(final ActionBar.Tab tab, final FragmentTransaction fragmentTransaction) {
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// @id iitc-plugin-bookmarks@ZasoGD
|
||||
// @name IITC plugin: Bookmarks for maps and portals
|
||||
// @category Controls
|
||||
// @version 0.2.7.@@DATETIMEVERSION@@
|
||||
// @version 0.2.8.@@DATETIMEVERSION@@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
@ -151,18 +151,11 @@
|
||||
}
|
||||
|
||||
if(newStatus === 1) {
|
||||
$('#bookmarksBox').show();
|
||||
$('#bkmrksTrigger').hide();
|
||||
$('#bookmarksBox').css('height', 'auto');
|
||||
$('#bkmrksTrigger').css('height', '0');
|
||||
} else {
|
||||
$('#bookmarksBox').hide();
|
||||
$('#bkmrksTrigger').show();
|
||||
}
|
||||
|
||||
if(window.plugin.bookmarks.isSmart) {
|
||||
var button = $('#bkmrksTrigger');
|
||||
button.toggleClass('open');
|
||||
if(button.hasClass('open')) { button.text('[-] Bookmarks'); }
|
||||
else{ button.text('[+] Bookmarks'); }
|
||||
$('#bkmrksTrigger').css('height', '64px');
|
||||
$('#bookmarksBox').css('height', '0');
|
||||
}
|
||||
|
||||
window.plugin.bookmarks.statusBox['show'] = newStatus;
|
||||
@ -433,6 +426,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.plugin.bookmarks.deleteMode = function() {
|
||||
$('#bookmarksBox').toggleClass('deleteMode');
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************************************************************************************/
|
||||
|
||||
// Saved the new sort of the folders (in the localStorage)
|
||||
@ -489,6 +487,7 @@
|
||||
items:"li.bookmarkFolder:not(.othersBookmarks)",
|
||||
handle:".bookmarksAnchor",
|
||||
placeholder:"sortable-placeholder",
|
||||
helper:'clone', // fix accidental click in firefox
|
||||
forcePlaceholderSize:true,
|
||||
update:function(event, ui) {
|
||||
var typeList = $('#'+ui.item.context.id).parent().parent('.bookmarkList').attr('id');
|
||||
@ -501,6 +500,7 @@
|
||||
connectWith:".bookmarkList ul ul",
|
||||
handle:".bookmarksLink",
|
||||
placeholder:"sortable-placeholder",
|
||||
helper:'clone', // fix accidental click in firefox
|
||||
forcePlaceholderSize:true,
|
||||
update:function(event, ui) {
|
||||
var typeList = $('#'+ui.item.context.id).parent().parent().parent().parent('.bookmarkList').attr('id');
|
||||
@ -520,11 +520,6 @@
|
||||
title: 'Bookmarks Options'
|
||||
});
|
||||
|
||||
if(window.plugin.bookmarks.isAndroid()) {
|
||||
$('a:contains(\'Save box\'), a:contains(\'Reset box\')').addClass('disabled');
|
||||
} else {
|
||||
$('a:contains(\'Share all\')').addClass('disabled');
|
||||
}
|
||||
window.runHooks('pluginBkmrksOpenOpt');
|
||||
}
|
||||
|
||||
@ -534,7 +529,7 @@
|
||||
}
|
||||
|
||||
window.plugin.bookmarks.optCopy = function() {
|
||||
if(typeof android !== 'undefined' && android && android.intentPosLink) {
|
||||
if(typeof android !== 'undefined' && android && android.shareString) {
|
||||
return android.shareString(localStorage[window.plugin.bookmarks.KEY_STORAGE]);
|
||||
} else {
|
||||
dialog({
|
||||
@ -545,20 +540,49 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.plugin.bookmarks.optPaste = function() {
|
||||
var promptAction = prompt('Press CTRL+V to paste it.', '');
|
||||
if(promptAction !== null && promptAction !== '') {
|
||||
localStorage[window.plugin.bookmarks.KEY_STORAGE] = promptAction;
|
||||
window.plugin.bookmarks.refreshBkmrks();
|
||||
window.runHooks('pluginBkmrksEdit', {"target": "all", "action": "import"});
|
||||
console.log('BOOKMARKS: reset and imported bookmarks');
|
||||
window.plugin.bookmarks.optAlert('Successful. ');
|
||||
window.plugin.bookmarks.optExport = function() {
|
||||
if(typeof android !== 'undefined' && android && android.saveFile) {
|
||||
android.saveFile("IITC-bookmarks.json", "application/json", localStorage[window.plugin.bookmarks.KEY_STORAGE]);
|
||||
}
|
||||
}
|
||||
|
||||
window.plugin.bookmarks.optPaste = function() {
|
||||
var promptAction = prompt('Press CTRL+V to paste it.', '');
|
||||
if(promptAction !== null && promptAction !== '') {
|
||||
try {
|
||||
JSON.parse(promptAction); // try to parse JSON first
|
||||
localStorage[window.plugin.bookmarks.KEY_STORAGE] = promptAction;
|
||||
window.plugin.bookmarks.refreshBkmrks();
|
||||
window.runHooks('pluginBkmrksEdit', {"target": "all", "action": "import"});
|
||||
console.log('BOOKMARKS: reset and imported bookmarks');
|
||||
window.plugin.bookmarks.optAlert('Successful. ');
|
||||
} catch(e) {
|
||||
console.warn('BOOKMARKS: failed to import data: '+e);
|
||||
window.plugin.bookmarks.optAlert('<span style="color: #f88">Import failed </span>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.plugin.bookmarks.optImport = function() {
|
||||
if (window.requestFile === undefined) return;
|
||||
window.requestFile(function(filename, content) {
|
||||
try {
|
||||
JSON.parse(content); // try to parse JSON first
|
||||
localStorage[window.plugin.bookmarks.KEY_STORAGE] = content;
|
||||
window.plugin.bookmarks.refreshBkmrks();
|
||||
window.runHooks('pluginBkmrksEdit', {"target": "all", "action": "import"});
|
||||
console.log('BOOKMARKS: reset and imported bookmarks');
|
||||
window.plugin.bookmarks.optAlert('Successful. ');
|
||||
} catch(e) {
|
||||
console.warn('BOOKMARKS: failed to import data: '+e);
|
||||
window.plugin.bookmarks.optAlert('<span style="color: #f88">Import failed </span>');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.plugin.bookmarks.optReset = function() {
|
||||
var promptAction = prompt('All bookmarks will be deleted. Are you sure? [Y/N]', '');
|
||||
if(promptAction !== null && (promptAction === 'Y' || promptAction === 'y')) {
|
||||
var promptAction = confirm('All bookmarks will be deleted. Are you sure?', '');
|
||||
if(promptAction) {
|
||||
delete localStorage[window.plugin.bookmarks.KEY_STORAGE];
|
||||
window.plugin.bookmarks.createStorage();
|
||||
window.plugin.bookmarks.loadStorage();
|
||||
@ -618,6 +642,8 @@
|
||||
if(latlngs.length >= 2 && latlngs.length <= 3) {
|
||||
// TODO: add an API to draw-tools rather than assuming things about it's internals
|
||||
var newItem;
|
||||
window.plugin.drawTools.setOptions();
|
||||
|
||||
if(latlngs.length == 2) {
|
||||
newItem = L.geodesicPolyline(latlngs, window.plugin.drawTools.lineOptions);
|
||||
} else {
|
||||
@ -885,6 +911,7 @@
|
||||
+'<div id="topBar">'
|
||||
+'<a id="bookmarksMin" class="btn" onclick="window.plugin.bookmarks.switchStatusBkmrksBox(0);return false;" title="Minimize">-</a>'
|
||||
+'<div class="handle">...</div>'
|
||||
+'<a id="bookmarksDel" class="btn" onclick="window.plugin.bookmarks.deleteMode();return false;" title="Show/Hide \'X\' button">Show/Hide "X" button</a>'
|
||||
+'</div>'
|
||||
+'<div id="bookmarksTypeBar">'
|
||||
+'<h5 class="bkmrk_maps current" onclick="window.plugin.bookmarks.switchPageBkmrksBox(this, 0);return false">Maps</h5>'
|
||||
@ -904,19 +931,27 @@
|
||||
+'<a class="newFolder" onclick="window.plugin.bookmarks.addElement(this, \'folder\');return false;">+ Folder</a>'
|
||||
+'</div>'
|
||||
+'</div>'
|
||||
+'<div style="border-bottom-width:1px;"></div>'
|
||||
+'</div>';
|
||||
|
||||
plugin.bookmarks.htmlDisabledMessage = '<div title="Your browser do not support localStorage">Plugin Bookmarks disabled*.</div>';
|
||||
plugin.bookmarks.htmlStar = '<a class="bkmrksStar" onclick="window.plugin.bookmarks.switchStarPortal();return false;" title="Save this portal in your bookmarks"><span></span></a>';
|
||||
plugin.bookmarks.htmlCalldrawBox = '<a onclick="window.plugin.bookmarks.dialogDrawer();return false;" title="Draw lines/triangles between bookmarked portals">Auto draw</a>';
|
||||
plugin.bookmarks.htmlCallSetBox = '<a onclick="window.plugin.bookmarks.manualOpt();return false;">Bookmarks Opt</a>';
|
||||
plugin.bookmarks.htmlSetbox = '<div id="bkmrksSetbox">'
|
||||
+'<a onclick="window.plugin.bookmarks.optCopy();">Copy/Export Bookmarks</a>'
|
||||
+'<a onclick="window.plugin.bookmarks.optPaste();return false;">Paste/Import Bookmarks</a>'
|
||||
+'<a onclick="window.plugin.bookmarks.optReset();return false;">Reset Bookmarks</a>'
|
||||
+'<a onclick="window.plugin.bookmarks.optBox(\'save\');">Save box position (No IITCm)</a>'
|
||||
+'<a onclick="window.plugin.bookmarks.optBox(\'reset\');">Reset box position (No IITCm)</a>'
|
||||
+'</div>';
|
||||
|
||||
var actions = '';
|
||||
actions += '<a onclick="window.plugin.bookmarks.optReset();return false;">Reset bookmarks</a>';
|
||||
actions += '<a onclick="window.plugin.bookmarks.optCopy();return false;">Copy bookmarks</a>';
|
||||
actions += '<a onclick="window.plugin.bookmarks.optPaste();return false;">Paste bookmarks</a>';
|
||||
|
||||
if(plugin.bookmarks.isAndroid()) {
|
||||
actions += '<a onclick="window.plugin.bookmarks.optImport();return false;">Import bookmarks</a>';
|
||||
actions += '<a onclick="window.plugin.bookmarks.optExport();return false;">Export bookmarks</a>';
|
||||
} else {
|
||||
actions += '<a onclick="window.plugin.bookmarks.optBox(\'save\');return false;">Save box position</a>';
|
||||
actions += '<a onclick="window.plugin.bookmarks.optBox(\'reset\');return false;">Reset box position</a>';
|
||||
}
|
||||
plugin.bookmarks.htmlSetbox = '<div id="bkmrksSetbox">' + actions + '</div>';
|
||||
}
|
||||
|
||||
/***************************************************************************************************************************************************************/
|
||||
|
@ -9,13 +9,20 @@
|
||||
line-height:22px;
|
||||
text-indent:0;
|
||||
text-decoration:none;
|
||||
-webkit-box-sizing:border-box;
|
||||
-moz-box-sizing:border-box;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
|
||||
#bookmarksBox{
|
||||
display:block;
|
||||
position:absolute !important;
|
||||
z-index:4001;
|
||||
top:100px;
|
||||
left:100px;
|
||||
width:231px;
|
||||
height:auto;
|
||||
overflow:hidden;
|
||||
}
|
||||
#bookmarksBox .addForm,
|
||||
#bookmarksBox #bookmarksTypeBar,
|
||||
@ -25,33 +32,40 @@
|
||||
color:#fff;
|
||||
font-size:14px;
|
||||
}
|
||||
#bookmarksBox #topBar,
|
||||
#bookmarksBox #topBar *{
|
||||
#bookmarksBox #topBar{
|
||||
height:15px !important;
|
||||
}
|
||||
#bookmarksBox #topBar *{
|
||||
height: 14px !important;
|
||||
}
|
||||
#bookmarksBox #topBar *{
|
||||
float:left !important;
|
||||
}
|
||||
#bookmarksBox .handle{
|
||||
text-indent:-20px;
|
||||
width:209px;
|
||||
width:80%;
|
||||
text-align:center;
|
||||
color:#fff;
|
||||
line-height:8px;
|
||||
line-height:6px;
|
||||
cursor:move;
|
||||
}
|
||||
#bookmarksBox #topBar .btn{
|
||||
display:block;
|
||||
width:19px;
|
||||
width:10%;
|
||||
cursor:pointer;
|
||||
color:#20a8b1;
|
||||
}
|
||||
#bookmarksBox #topBar #bookmarksMin{
|
||||
|
||||
font-weight:bold;
|
||||
text-align:center;
|
||||
line-height:14px;
|
||||
line-height:13px;
|
||||
font-size:18px;
|
||||
}
|
||||
|
||||
#bookmarksBox #topBar #bookmarksDel{
|
||||
overflow:hidden;
|
||||
text-indent:-999px;
|
||||
background:#B42E2E;
|
||||
}
|
||||
|
||||
#bookmarksBox #topBar #bookmarksMin:hover{
|
||||
color:#ffce00;
|
||||
}
|
||||
@ -59,8 +73,9 @@
|
||||
clear:both;
|
||||
}
|
||||
#bookmarksBox h5{
|
||||
padding:4px 0;
|
||||
width:114px;
|
||||
padding:4px 0 23px;
|
||||
width:50%;
|
||||
height:93px !important;
|
||||
text-align:center;
|
||||
color:#788;
|
||||
}
|
||||
@ -73,9 +88,8 @@
|
||||
color:#ffce00;
|
||||
background:rgba(0,0,0,0);
|
||||
}
|
||||
#bookmarksBox #topBar .btn,
|
||||
#bookmarksBox #topBar,
|
||||
#bookmarksBox .addForm,
|
||||
#bookmarksBox .handle,
|
||||
#bookmarksBox #bookmarksTypeBar,
|
||||
#bookmarksBox .bookmarkList li.bookmarksEmpty,
|
||||
#bookmarksBox .bookmarkList li.bkmrk a,
|
||||
@ -90,16 +104,16 @@
|
||||
#bookmarksBox .addForm *{
|
||||
display:block;
|
||||
float:left;
|
||||
padding:4px 8px 3px;
|
||||
height:28px !important;
|
||||
}
|
||||
#bookmarksBox .addForm a{
|
||||
cursor:pointer;
|
||||
color:#20a8b1;
|
||||
font-size:12px;
|
||||
width:65px;
|
||||
width:35%;
|
||||
text-align:center;
|
||||
line-height:20px;
|
||||
padding:4px 0 3px;
|
||||
padding:4px 0 23px;
|
||||
}
|
||||
#bookmarksBox .addForm a:hover{
|
||||
background:#ffce00;
|
||||
@ -109,15 +123,19 @@
|
||||
#bookmarksBox .addForm input{
|
||||
font-size:11px !important;
|
||||
color:#ffce00;
|
||||
width:81px;
|
||||
line-height:11px;
|
||||
height:28px;
|
||||
padding:4px 8px 1px;
|
||||
line-height:12px;
|
||||
font-size:12px;
|
||||
-webkit-box-sizing:content-box;
|
||||
-moz-box-sizing:content-box;
|
||||
box-sizing:content-box;
|
||||
}
|
||||
#bookmarksBox #bkmrk_portals .addForm input{
|
||||
width:147px;
|
||||
width:65%;
|
||||
}
|
||||
#bookmarksBox #bkmrk_maps .addForm input{
|
||||
width:42%;
|
||||
}
|
||||
#bookmarksBox #bkmrk_maps .addForm a{
|
||||
width:29%;
|
||||
}
|
||||
#bookmarksBox .addForm input:hover,
|
||||
#bookmarksBox .addForm input:focus{
|
||||
@ -125,11 +143,12 @@
|
||||
background:rgba(0,0,0,.6);
|
||||
}
|
||||
#bookmarksBox .bookmarkList > ul{
|
||||
width:231px;
|
||||
clear:both;
|
||||
list-style-type:none;
|
||||
color:#fff;
|
||||
overflow:hidden;
|
||||
overflow-y:auto;
|
||||
max-height:580px;
|
||||
}
|
||||
#bookmarksBox .sortable-placeholder{
|
||||
background:rgba(8,48,78,.55);
|
||||
@ -161,12 +180,12 @@
|
||||
color:#eee;
|
||||
}
|
||||
#bookmarksBox ul .bookmarksRemoveFrom{
|
||||
width:19px;
|
||||
width:10%;
|
||||
text-align:center;
|
||||
color:#fff;
|
||||
}
|
||||
#bookmarksBox ul .bookmarksLink{
|
||||
width:171px;
|
||||
width:90%;
|
||||
padding:0 10px 0 8px;
|
||||
color:#ffce00;
|
||||
}
|
||||
@ -174,7 +193,7 @@
|
||||
color:#03fe03;
|
||||
}
|
||||
#bookmarksBox ul .othersBookmarks .bookmarksLink{
|
||||
width:190px;
|
||||
width:90%;
|
||||
}
|
||||
#bookmarksBox ul .bookmarksLink:hover{
|
||||
color:#03fe03;
|
||||
@ -183,6 +202,8 @@
|
||||
color:#fff;
|
||||
background:#e22 !important;
|
||||
}
|
||||
|
||||
/*---- UI border -----*/
|
||||
#bookmarksBox,
|
||||
#bookmarksBox *{
|
||||
border-color:#20a8b1;
|
||||
@ -193,40 +214,44 @@
|
||||
#bookmarksBox ul .bookmarkFolder{
|
||||
border-top-width:1px;
|
||||
}
|
||||
|
||||
#bookmarksBox #topBar,
|
||||
#bookmarksBox #bookmarksTypeBar,
|
||||
#bookmarksBox .addForm,
|
||||
#bookmarksBox ul .bookmarkFolder .folderLabel,
|
||||
#bookmarksBox ul li.bkmrk{
|
||||
#bookmarksBox ul li.bkmrk a {
|
||||
border-bottom-width:1px;
|
||||
}
|
||||
#bookmarksBox ul .bookmarkFolder,
|
||||
#bookmarksBox ul .bookmarksRemoveFrom{
|
||||
#bookmarksBox ul .bookmarkFolder{
|
||||
border-right-width:1px;
|
||||
border-left-width:1px;
|
||||
}
|
||||
#bookmarksBox #topBar *,
|
||||
#bookmarksBox #bookmarksTypeBar *,
|
||||
#bookmarksBox .addForm *{
|
||||
#bookmarksBox .addForm *,
|
||||
#bookmarksBox ul li.bkmrk{
|
||||
border-left-width:1px;
|
||||
}
|
||||
#bookmarksBox #topBar,
|
||||
#bookmarksBox #bookmarksTypeBar,
|
||||
#bookmarksBox .addForm{
|
||||
#bookmarksBox .addForm,
|
||||
#bookmarksBox ul .bookmarksRemoveFrom{
|
||||
border-right-width:1px;
|
||||
}
|
||||
#bookmarksBox ul .othersBookmarks .bookmarksRemoveFrom,
|
||||
#bookmarksBox ul .bookmarkFolder.othersBookmarks li.bkmrk,
|
||||
#bookmarksBox ul .bookmarkFolder .folderLabel .bookmarksRemoveFrom{
|
||||
border-left-width:0;
|
||||
}
|
||||
#bkmrksTrigger{
|
||||
display:none;
|
||||
display:block !important;
|
||||
position:absolute;
|
||||
overflow:hidden;
|
||||
top:0;
|
||||
left:277px;
|
||||
width:47px;
|
||||
margin-top:-36px;
|
||||
height:64px;
|
||||
height:0;
|
||||
cursor:pointer;
|
||||
z-index:2999;
|
||||
background-position:center bottom;
|
||||
@ -272,17 +297,25 @@
|
||||
}
|
||||
#bookmarksBox .bookmarkList .bkmrk.ui-sortable-helper{
|
||||
border-right-width:1px;
|
||||
border-left-width:1px;
|
||||
border-left-width:1px !important;
|
||||
}
|
||||
#bookmarksBox .bookmarkList ul li ul li.sortable-placeholder{
|
||||
height:23px;
|
||||
box-shadow:inset 0 -1px 0 #20a8b1,inset 1px 0 0 #20a8b1;
|
||||
}
|
||||
|
||||
#bookmarksBox .bookmarkList ul li.bookmarkFolder.ui-sortable-helper,
|
||||
#bookmarksBox .bookmarkList ul li.othersBookmarks ul,
|
||||
#bookmarksBox .bookmarkList ul li.othersBookmarks ul li.sortable-placeholder{
|
||||
box-shadow:inset 0 -1px 0 #20a8b1;
|
||||
}
|
||||
|
||||
#bookmarksBox #topBar #bookmarksDel,
|
||||
#bookmarksBox .bookmarkList .bookmarkFolder .folderLabel:hover .bookmarksRemoveFrom,
|
||||
#bookmarksBox .bookmarkList .bookmarkFolder .folderLabel:hover .bookmarksAnchor{
|
||||
border-bottom-width:1px;
|
||||
}
|
||||
|
||||
/*---------*/
|
||||
#bookmarksBox .bookmarkList .bookmarkFolder .folderLabel .bookmarksAnchor span,
|
||||
#bookmarksBox .bookmarkList .bookmarkFolder .folderLabel > span,
|
||||
#bookmarksBox .bookmarkList .bookmarkFolder .folderLabel > span > span,
|
||||
@ -304,7 +337,7 @@
|
||||
#bookmarksBox .bookmarkList .bookmarkFolder .folderLabel .bookmarksAnchor{
|
||||
line-height:25px;
|
||||
color:#fff;
|
||||
width:209px;
|
||||
width:90%;
|
||||
}
|
||||
#bookmarksBox .bookmarkList .bookmarkFolder .folderLabel .bookmarksAnchor span{
|
||||
float:left;
|
||||
@ -336,6 +369,7 @@
|
||||
#bookmarksBox .bookmarkList .bookmarkFolder.open .folderLabel > span,
|
||||
#bookmarksBox .bookmarkList .bookmarkFolder.open .folderLabel > span > span{
|
||||
display:block;
|
||||
display:none;
|
||||
}
|
||||
#bookmarksBox .bookmarkList .bookmarkFolder.open .folderLabel:hover > span > span{
|
||||
border-color:transparent #036 transparent transparent;
|
||||
@ -345,7 +379,7 @@
|
||||
}
|
||||
#bookmarksBox .bookmarkList .bookmarkFolder ul{
|
||||
display:none;
|
||||
margin-left:19px;
|
||||
margin-left:10%;
|
||||
}
|
||||
#bookmarksBox .bookmarkList .bookmarkFolder.open ul{
|
||||
display:block;
|
||||
@ -355,6 +389,26 @@
|
||||
margin-left:0;
|
||||
}
|
||||
|
||||
/*---- Width for deleteMode -----*/
|
||||
#bookmarksBox .bookmarksRemoveFrom{
|
||||
display:none !important;
|
||||
}
|
||||
#bookmarksBox.deleteMode .bookmarksRemoveFrom{
|
||||
display:block !important;
|
||||
}
|
||||
|
||||
#bookmarksBox .bookmarkList .bookmarkFolder .folderLabel .bookmarksAnchor,
|
||||
#bookmarksBox ul .bookmarksLink,
|
||||
#bookmarksBox ul .othersBookmarks .bookmarksLink{
|
||||
width:100% !important;
|
||||
}
|
||||
|
||||
#bookmarksBox.deleteMode .bookmarkList .bookmarkFolder .folderLabel .bookmarksAnchor,
|
||||
#bookmarksBox.deleteMode ul .bookmarksLink,
|
||||
#bookmarksBox.deleteMode ul .othersBookmarks .bookmarksLink{
|
||||
width:90% !important;
|
||||
}
|
||||
|
||||
/**********************************************
|
||||
MOBILE
|
||||
**********************************************/
|
||||
@ -367,7 +421,8 @@
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
border: 0 !important;
|
||||
background: transparent !important;;
|
||||
background: transparent !important;
|
||||
overflow:auto !important;
|
||||
}
|
||||
#bookmarksBox.mobile .bookmarkList ul,
|
||||
#bookmarksBox.mobile .bookmarkList ul li,
|
||||
@ -380,14 +435,27 @@
|
||||
box-shadow:none !important;
|
||||
border-width:0 !important;
|
||||
}
|
||||
#bookmarksBox.mobile #topBar{
|
||||
#bookmarksBox.mobile #topBar #bookmarksMin,
|
||||
#bookmarksBox.mobile #topBar .handle{
|
||||
display:none !important;
|
||||
}
|
||||
|
||||
#bookmarksBox.mobile #topBar #bookmarksDel{
|
||||
width:100%;
|
||||
height:34px !important;
|
||||
font-size:13px;
|
||||
color:#fff;
|
||||
font-weight:normal;
|
||||
padding-top:11px;
|
||||
text-indent:0;
|
||||
}
|
||||
|
||||
#bookmarksBox.mobile #bookmarksTypeBar h5{
|
||||
cursor:pointer;
|
||||
text-align:center;
|
||||
float:left;
|
||||
width:50%;
|
||||
height:auto !important;
|
||||
padding:7px 0;
|
||||
}
|
||||
#bookmarksBox.mobile #bookmarksTypeBar h5.current{
|
||||
@ -413,14 +481,14 @@
|
||||
#bookmarksBox.mobile .bookmarkList li.bookmarkFolder a.bookmarksRemoveFrom,
|
||||
#bookmarksBox.mobile .bookmarkList li.bkmrk a.bookmarksRemoveFrom{
|
||||
box-shadow:inset 0 1px 0 #20a8b1,inset -1px 0 0 #20a8b1 !important;
|
||||
width:15%;
|
||||
width:10%;
|
||||
background:none !important;
|
||||
}
|
||||
#bookmarksBox.mobile .bookmarkList li.bookmarkFolder a.bookmarksAnchor,
|
||||
#bookmarksBox.mobile .bookmarkList li.bkmrk a.bookmarksLink{
|
||||
text-indent:10px;
|
||||
width:85%;
|
||||
height:21px;
|
||||
height:36px;
|
||||
line-height:24px;
|
||||
overflow:hidden;
|
||||
}
|
||||
#bookmarksBox.mobile .bookmarkList ul li.bookmarkFolder ul{
|
||||
@ -428,7 +496,6 @@
|
||||
}
|
||||
#bookmarksBox.mobile .bookmarkList > ul{
|
||||
border-bottom:1px solid #20a8b1 !important;
|
||||
border-right:1px solid #20a8b1 !important;
|
||||
}
|
||||
|
||||
#bookmarksBox.mobile .bookmarkList .bookmarkFolder.othersBookmarks ul{
|
||||
@ -440,14 +507,14 @@
|
||||
}
|
||||
#bookmarksBox.mobile .bookmarkList > ul{
|
||||
max-height:none;
|
||||
width:85% !important;
|
||||
/* width:85% !important;*/
|
||||
}
|
||||
#bookmarksBox.mobile .bookmarkList li.bookmarkFolder .folderLabel{
|
||||
box-shadow:0 1px 0 #20a8b1 !important;
|
||||
}
|
||||
#bookmarksBox.mobile .bookmarkList ul li.bookmarkFolder ul{
|
||||
width:85% !important;
|
||||
margin-left:15% !important;
|
||||
width:90% !important;
|
||||
margin-left:10% !important;
|
||||
}
|
||||
#bookmarksBox.mobile .bookmarkList ul li.bookmarkFolder.othersBookmarks ul{
|
||||
width:100% !important;
|
||||
@ -461,7 +528,7 @@
|
||||
}
|
||||
#bookmarksBox.mobile .addForm,
|
||||
#bookmarksBox.mobile .addForm *{
|
||||
height:35px;
|
||||
height:35px !important;
|
||||
padding:0;
|
||||
}
|
||||
#bookmarksBox.mobile .addForm a{
|
||||
@ -469,14 +536,14 @@
|
||||
}
|
||||
|
||||
#bookmarksBox.mobile .addForm a{
|
||||
width:25% !important;
|
||||
/* width:25% !important;*/
|
||||
}
|
||||
#bookmarksBox.mobile .addForm input{
|
||||
width:50% !important;
|
||||
/* width:50% !important;*/
|
||||
text-indent:10px;
|
||||
}
|
||||
#bookmarksBox.mobile #bkmrk_portals .addForm input{
|
||||
width:75% !important;
|
||||
/* width:75% !important;*/
|
||||
}
|
||||
#bookmarksBox.mobile #bookmarksTypeBar h5,
|
||||
#bookmarksBox.mobile .bookmarkList .addForm a{
|
||||
@ -530,10 +597,10 @@
|
||||
#bookmarksBox.mobile .bookmarkList .bookmarkFolder.open .folderLabel > span > span{
|
||||
display:block !important;
|
||||
}
|
||||
|
||||
/**********************************************
|
||||
DIALOG BOX
|
||||
**********************************************/
|
||||
|
||||
/*---- Auto Drawer -----*/
|
||||
#bkmrksAutoDrawer,
|
||||
#bkmrksAutoDrawer p,
|
||||
@ -597,4 +664,4 @@
|
||||
width:96%;
|
||||
height:120px;
|
||||
resize:vertical;
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
// @id iitc-plugin-default-intel-detail@jonatkins
|
||||
// @name IITC plugin: Default intel detail level
|
||||
// @category Tweaks
|
||||
// @version 0.1.0.@@DATETIMEVERSION@@
|
||||
// @version 0.2.0.@@DATETIMEVERSION@@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
@ -24,23 +24,9 @@ window.plugin.defaultIntelDetail = function() {};
|
||||
|
||||
window.plugin.defaultIntelDetail.setup = function() {
|
||||
|
||||
var stockIntelDetail = nemesis.dashboard.zoomlevel.ZOOM_TO_LOD_;
|
||||
|
||||
// save the original function - so we can chain to it for levels we don't modify
|
||||
var origGetMinPortalLevelForZoom = window.getMinPortalLevelForZoom;
|
||||
|
||||
// replace the window.getMinPortalLevelForZoom function - modify behaviour when L1+ or L3+ portals are shown
|
||||
|
||||
window.getMinPortalLevelForZoom = function(z) {
|
||||
// for the further out zoom levels, use the stock intel site detail levels
|
||||
if (z <= 11) {
|
||||
return stockIntelDetail[z];
|
||||
}
|
||||
// for the closer zoom levels, stock intel and IITC default is the same. falling back
|
||||
// in this case allows this plugin to work alongside show-more-portals
|
||||
return origGetMinPortalLevelForZoom(z);
|
||||
}
|
||||
|
||||
// NOTE: the logic required is closely tied to the IITC+stock map detail level code - so the logic is moved there now
|
||||
// and just enabled by this flag
|
||||
window.CONFIG_ZOOM_DEFAULT_DETAIL_LEVEL=true;
|
||||
|
||||
};
|
||||
|
||||
|
@ -34,8 +34,27 @@ window.plugin.drawTools.loadExternals = function() {
|
||||
$('head').append('<style>@@INCLUDESTRING:external/spectrum/spectrum.css@@</style>');
|
||||
}
|
||||
|
||||
window.plugin.drawTools.currentColor = '#a24ac3';
|
||||
window.plugin.drawTools.getMarkerIcon = function(color) {
|
||||
if (typeof(color) === 'undefined') {
|
||||
console.warn('Color is not set or not a valid color. Using default color as fallback.');
|
||||
color = '#a24ac3';
|
||||
}
|
||||
|
||||
var svgIcon = window.plugin.drawTools.markerTemplate.replace(/%COLOR%/g, color);
|
||||
|
||||
return L.divIcon({
|
||||
iconSize: new L.Point(25, 41),
|
||||
iconAnchor: new L.Point(12, 41),
|
||||
html: svgIcon,
|
||||
className: 'leaflet-iitc-custom-icon',
|
||||
// L.divIcon does not use the option color, but we store it here to
|
||||
// be able to simply retrieve the color for serializing markers
|
||||
color: color
|
||||
});
|
||||
}
|
||||
|
||||
window.plugin.drawTools.currentColor = '#a24ac3';
|
||||
window.plugin.drawTools.markerTemplate = '@@INCLUDESTRING:images/marker-icon.svg.template@@';
|
||||
|
||||
window.plugin.drawTools.setOptions = function() {
|
||||
|
||||
@ -60,7 +79,7 @@ window.plugin.drawTools.setOptions = function() {
|
||||
delete window.plugin.drawTools.editOptions.color;
|
||||
|
||||
window.plugin.drawTools.markerOptions = {
|
||||
icon: new L.Icon.Default(),
|
||||
icon: window.plugin.drawTools.currentMarker,
|
||||
zIndexOffset: 2000
|
||||
};
|
||||
|
||||
@ -68,11 +87,13 @@ window.plugin.drawTools.setOptions = function() {
|
||||
|
||||
window.plugin.drawTools.setDrawColor = function(color) {
|
||||
window.plugin.drawTools.currentColor = color;
|
||||
window.plugin.drawTools.currentMarker = window.plugin.drawTools.getMarkerIcon(color);
|
||||
|
||||
window.plugin.drawTools.drawControl.setDrawingOptions({
|
||||
'polygon': { 'shapeOptions': { color: color } },
|
||||
'polyline': { 'shapeOptions': { color: color } },
|
||||
'circle': { 'shapeOptions': { color: color } },
|
||||
'marker': { 'icon': window.plugin.drawTools.currentMarker },
|
||||
});
|
||||
}
|
||||
|
||||
@ -113,14 +134,15 @@ window.plugin.drawTools.addDrawControl = function() {
|
||||
snapPoint: window.plugin.drawTools.getSnapLatLng,
|
||||
},
|
||||
|
||||
marker: {
|
||||
// Options for marker (icon, zIndexOffset) are not set via shapeOptions,
|
||||
// so we have merge them here!
|
||||
marker: L.extend({}, window.plugin.drawTools.markerOptions, {
|
||||
title: 'Add a marker.\n\n'
|
||||
+ 'Click on the button, then click on the map where\n'
|
||||
+ 'you want the marker to appear.',
|
||||
shapeOptions: window.plugin.drawTools.markerOptions,
|
||||
snapPoint: window.plugin.drawTools.getSnapLatLng,
|
||||
repeatMode: true,
|
||||
},
|
||||
repeatMode: true
|
||||
}),
|
||||
|
||||
},
|
||||
|
||||
@ -190,6 +212,7 @@ window.plugin.drawTools.save = function() {
|
||||
} else if (layer instanceof L.Marker) {
|
||||
item.type = 'marker';
|
||||
item.latLng = layer.getLatLng();
|
||||
item.color = layer.options.icon.options.color;
|
||||
} else {
|
||||
console.warn('Unknown layer type when saving draw tools layer');
|
||||
return; //.eachLayer 'continue'
|
||||
@ -233,7 +256,9 @@ window.plugin.drawTools.import = function(data) {
|
||||
layer = L.geodesicCircle(item.latLng, item.radius, L.extend({},window.plugin.drawTools.polygonOptions,extraOpt));
|
||||
break;
|
||||
case 'marker':
|
||||
layer = L.marker(item.latLng, window.plugin.drawTools.markerOptions);
|
||||
var extraMarkerOpt = {};
|
||||
if (item.color) extraMarkerOpt.icon = window.plugin.drawTools.getMarkerIcon(item.color);
|
||||
layer = L.marker(item.latLng, L.extend({},window.plugin.drawTools.markerOptions,extraMarkerOpt));
|
||||
break;
|
||||
default:
|
||||
console.warn('unknown layer type "'+item.type+'" when loading draw tools layer');
|
||||
@ -256,8 +281,12 @@ window.plugin.drawTools.manualOpt = function() {
|
||||
//TODO: add line style choosers: thickness, maybe dash styles?
|
||||
+ '</div>'
|
||||
+ '<div class="drawtoolsSetbox">'
|
||||
+ '<a onclick="window.plugin.drawTools.optCopy();">Copy/Export Drawn Items</a>'
|
||||
+ '<a onclick="window.plugin.drawTools.optPaste();return false;">Paste/Import Drawn Items</a>'
|
||||
+ '<a onclick="window.plugin.drawTools.optCopy();">Copy Drawn Items</a>'
|
||||
+ '<a onclick="window.plugin.drawTools.optPaste();return false;">Paste Drawn Items</a>'
|
||||
+ (window.requestFile != undefined
|
||||
? '<a onclick="window.plugin.drawTools.optImport();return false;">Import Drawn Items</a>' : '')
|
||||
+ ((typeof android !== 'undefined' && android && android.saveFile)
|
||||
? '<a onclick="window.plugin.drawTools.optExport();return false;">Export Drawn Items</a>' : '')
|
||||
+ '<a onclick="window.plugin.drawTools.optReset();return false;">Reset Drawn Items</a>'
|
||||
+ '</div>';
|
||||
|
||||
@ -302,6 +331,12 @@ window.plugin.drawTools.optCopy = function() {
|
||||
}
|
||||
}
|
||||
|
||||
window.plugin.drawTools.optExport = function() {
|
||||
if(typeof android !== 'undefined' && android && android.saveFile) {
|
||||
android.saveFile('IITC-drawn-items.json', 'application/json', localStorage['plugin-draw-tools-layer']);
|
||||
}
|
||||
}
|
||||
|
||||
window.plugin.drawTools.optPaste = function() {
|
||||
var promptAction = prompt('Press CTRL+V to paste it.', '');
|
||||
if(promptAction !== null && promptAction !== '') {
|
||||
@ -318,10 +353,28 @@ window.plugin.drawTools.optPaste = function() {
|
||||
console.warn('DRAWTOOLS: failed to import data: '+e);
|
||||
window.plugin.drawTools.optAlert('<span style="color: #f88">Import failed</span>');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
window.plugin.drawTools.optImport = function() {
|
||||
if (window.requestFile === undefined) return;
|
||||
window.requestFile(function(filename, content) {
|
||||
try {
|
||||
var data = JSON.parse(content);
|
||||
window.plugin.drawTools.drawnItems.clearLayers();
|
||||
window.plugin.drawTools.import(data);
|
||||
console.log('DRAWTOOLS: reset and imported drawn tiems');
|
||||
window.plugin.drawTools.optAlert('Import Successful.');
|
||||
|
||||
// to write back the data to localStorage
|
||||
window.plugin.drawTools.save();
|
||||
} catch(e) {
|
||||
console.warn('DRAWTOOLS: failed to import data: '+e);
|
||||
window.plugin.drawTools.optAlert('<span style="color: #f88">Import failed</span>');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.plugin.drawTools.optReset = function() {
|
||||
var promptAction = confirm('All drawn items will be deleted. Are you sure?', '');
|
||||
if(promptAction) {
|
||||
@ -334,6 +387,8 @@ window.plugin.drawTools.optReset = function() {
|
||||
}
|
||||
|
||||
window.plugin.drawTools.boot = function() {
|
||||
window.plugin.drawTools.currentMarker = window.plugin.drawTools.getMarkerIcon(window.plugin.drawTools.currentColor);
|
||||
|
||||
window.plugin.drawTools.setOptions();
|
||||
|
||||
//create a leaflet FeatureGroup to hold drawn items
|
||||
|
@ -3,7 +3,7 @@
|
||||
// @name IITC plugin: Favorite Portals
|
||||
// @category Deleted
|
||||
// @version 0.2.0.@@DATETIMEVERSION@@
|
||||
// @description PLUGIN CURRENTLY UNAVAILABLE
|
||||
// @description Plugin replaced by the "Bookmarks for maps and portals" plugin
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
|
198
plugins/fix-googlemap-china-offset.user.js
Normal file
198
plugins/fix-googlemap-china-offset.user.js
Normal file
@ -0,0 +1,198 @@
|
||||
// ==UserScript==
|
||||
// @id iitc-plugin-fix-googlemap-china-offset@breezewish
|
||||
// @name IITC plugin: Fix Google Map offsets in China
|
||||
// @category Tweaks
|
||||
// @version 0.0.1.@@DATETIMEVERSION@@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description [@@BUILDNAME@@-@@BUILDDATE@@] Show correct Google Map for China user by applying offset tweaks.
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
// @match http://www.ingress.com/intel*
|
||||
// @grant none
|
||||
// ==/UserScript==
|
||||
|
||||
@@PLUGINSTART@@
|
||||
|
||||
// PLUGIN START ////////////////////////////////////////////////////////
|
||||
|
||||
// use own namespace for plugin
|
||||
window.plugin.fixChinaOffset = {};
|
||||
|
||||
// Before understanding how this plugin works, you should know 3 points:
|
||||
//
|
||||
// Point1.
|
||||
// The coordinate system of Ingress is WGS-84.
|
||||
// However, the tiles of Google maps (except satellite map) in China have
|
||||
// offsets (base on GCJ-02 coordinate system) by China policy.
|
||||
// That means, if you request map tiles by giving GCJ-02 position, you
|
||||
// will get the correct map.
|
||||
//
|
||||
// Point2.
|
||||
// Currently there are no easy algorithm to transform from GCJ-02 to WGS-84,
|
||||
// but we can easily transform data from WGS-84 to GCJ-02.
|
||||
//
|
||||
// Point3.
|
||||
// When using Google maps in IITC, the layer structure looks like this:
|
||||
// ----------------------
|
||||
// | Other Leaflet layers | (Including portals, links, fields, and so on)
|
||||
// ----------------------
|
||||
// | L.Google | (Only for controling)
|
||||
// ----------------------
|
||||
// | Google Map layer | (Generated by Google Map APIs, for rendering maps)
|
||||
// ----------------------
|
||||
//
|
||||
// When users are interacting with L.Google (for example, dragging, zooming),
|
||||
// L.Google will perform the same action on the Google Map layer using Google
|
||||
// Map APIs.
|
||||
//
|
||||
// So, here is the internal of the plugin:
|
||||
//
|
||||
// The plugin overwrites behaviours of L.Google. When users are dragging the map,
|
||||
// L.Google will pass offseted positions to Google Map APIs (WGS-84 to GCJ-02).
|
||||
// So Google Map APIs will render a correct map.
|
||||
//
|
||||
// The offset between Google maps and Ingress objects can also be fixed by applying
|
||||
// WGS-84 to GCJ-02 transformation on Ingress objects. However we cannot simply know
|
||||
// the requesting bounds of Ingress objects because we cannot transform GCJ-02 to
|
||||
// WGS-84. As a result, the Ingress objects on maps would be incomplete.
|
||||
//
|
||||
// The algorithm of transforming WGS-84 to GCJ-02 comes from:
|
||||
// https://on4wp7.codeplex.com/SourceControl/changeset/view/21483#353936
|
||||
// There is no official algorithm because it is classified information.
|
||||
|
||||
/////////// begin WGS84 to GCJ-02 transformer /////////
|
||||
var WGS84transformer = window.plugin.fixChinaOffset.WGS84transformer = function() {};
|
||||
// Krasovsky 1940
|
||||
//
|
||||
// a = 6378245.0, 1/f = 298.3
|
||||
// b = a * (1 - f)
|
||||
// ee = (a^2 - b^2) / a^2;
|
||||
WGS84transformer.prototype.a = 6378245.0;
|
||||
WGS84transformer.prototype.ee = 0.00669342162296594323;
|
||||
|
||||
WGS84transformer.prototype.transform = function(wgLat, wgLng) {
|
||||
|
||||
if(this.isOutOfChina(wgLat, wgLng))
|
||||
return {lat: wgLat, lng: wgLng};
|
||||
|
||||
dLat = this.transformLat(wgLng - 105.0, wgLat - 35.0);
|
||||
dLng = this.transformLng(wgLng - 105.0, wgLat - 35.0);
|
||||
radLat = wgLat / 180.0 * Math.PI;
|
||||
magic = Math.sin(radLat);
|
||||
magic = 1 - this.ee * magic * magic;
|
||||
sqrtMagic = Math.sqrt(magic);
|
||||
dLat = (dLat * 180.0) / ((this.a * (1 - this.ee)) / (magic * sqrtMagic) * Math.PI);
|
||||
dLng = (dLng * 180.0) / (this.a / sqrtMagic * Math.cos(radLat) * Math.PI);
|
||||
mgLat = wgLat + dLat;
|
||||
mgLng = wgLng + dLng;
|
||||
|
||||
return {lat: mgLat, lng: mgLng};
|
||||
|
||||
};
|
||||
|
||||
WGS84transformer.prototype.isOutOfChina = function(lat, lng) {
|
||||
|
||||
if(lng < 72.004 || lng > 137.8347) return true;
|
||||
if(lat < 0.8293 || lat > 55.8271) return true;
|
||||
|
||||
return false;
|
||||
|
||||
};
|
||||
|
||||
WGS84transformer.prototype.transformLat = function(x, y) {
|
||||
|
||||
var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
|
||||
ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
|
||||
ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0;
|
||||
ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0;
|
||||
|
||||
return ret;
|
||||
|
||||
};
|
||||
|
||||
WGS84transformer.prototype.transformLng = function(x, y) {
|
||||
|
||||
var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
|
||||
ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
|
||||
ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0;
|
||||
ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0;
|
||||
|
||||
return ret;
|
||||
|
||||
};
|
||||
/////////// end WGS84 to GCJ-02 transformer /////////
|
||||
|
||||
var WGS84toGCJ02 = new WGS84transformer();
|
||||
|
||||
/////////// begin overwrited L.Google /////////
|
||||
window.plugin.fixChinaOffset.L = {};
|
||||
window.plugin.fixChinaOffset.L.Google = {
|
||||
|
||||
_update: function(e) {
|
||||
|
||||
if(!this._google) return;
|
||||
this._resize();
|
||||
|
||||
var center = e && e.latlng ? e.latlng : this._map.getCenter();
|
||||
|
||||
///// modified here ///
|
||||
var _center = window.plugin.fixChinaOffset.getLatLng(center, this._type);
|
||||
///////////////////////
|
||||
|
||||
this._google.setCenter(_center);
|
||||
this._google.setZoom(this._map.getZoom());
|
||||
|
||||
this._checkZoomLevels();
|
||||
|
||||
},
|
||||
|
||||
_handleZoomAnim: function (e) {
|
||||
|
||||
var center = e.center;
|
||||
|
||||
///// modified here ///
|
||||
var _center = window.plugin.fixChinaOffset.getLatLng(center, this._type);
|
||||
///////////////////////
|
||||
|
||||
this._google.setCenter(_center);
|
||||
this._google.setZoom(e.zoom);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/////////// end overwrited L.Google /////////
|
||||
|
||||
window.plugin.fixChinaOffset.getLatLng = function(pos, type) {
|
||||
|
||||
// No offsets in satellite and hybrid maps
|
||||
if(type !== 'SATELLITE' && type !== 'HYBRID') {
|
||||
var newPos = WGS84toGCJ02.transform(pos.lat, pos.lng);
|
||||
return new google.maps.LatLng(newPos.lat, newPos.lng);
|
||||
} else {
|
||||
return new google.maps.LatLng(pos.lat, pos.lng);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
window.plugin.fixChinaOffset.overwrite = function(dest, src) {
|
||||
|
||||
for(var key in src) {
|
||||
if(src.hasOwnProperty(key)) {
|
||||
dest[key] = src[key];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var setup = function() {
|
||||
|
||||
window.plugin.fixChinaOffset.overwrite(L.Google.prototype, window.plugin.fixChinaOffset.L.Google);
|
||||
|
||||
}
|
||||
|
||||
// PLUGIN END //////////////////////////////////////////////////////////
|
||||
|
||||
@@PLUGINEND@@
|
@ -6,7 +6,7 @@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description PLUGIN CURRENTLY UNAVAILABLE
|
||||
// @description This plugin is no longer available, as Niantic optimisations have removed the data it needed.
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
|
@ -6,7 +6,7 @@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description PLUGIN CURRENTLY UNAVAILABLE
|
||||
// @description This plugin is no longer available, as Niantic optimisations have removed the data it needed.
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
|
@ -6,7 +6,7 @@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description PLUGIN CURRENTLY UNAVAILABLE
|
||||
// @description This plugin is no longer available, as Niantic optimisations have removed the data it needed.
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
|
@ -6,7 +6,7 @@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description PLUGIN CURRENTLY UNAVAILABLE
|
||||
// @description This plugin is no longer available, as Niantic optimisations have removed the data it needed.
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
|
@ -6,7 +6,7 @@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description PLUGIN CURRENTLY UNAVAILABLE
|
||||
// @description This plugin is no longer available, as Niantic optimisations have removed the data it needed.
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
|
@ -6,7 +6,7 @@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description PLUGIN CURRENTLY UNAVAILABLE
|
||||
// @description This plugin is no longer available, as Niantic optimisations have removed the data it needed.
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
|
@ -6,7 +6,7 @@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description PLUGIN CURRENTLY UNAVAILABLE
|
||||
// @description This plugin is no longer available, as Niantic optimisations have removed the data it needed.
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
|
@ -6,7 +6,7 @@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description PLUGIN CURRENTLY UNAVAILABLE
|
||||
// @description This plugin is no longer available, as Niantic optimisations have removed the data it needed.
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
|
@ -6,7 +6,7 @@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description PLUGIN CURRENTLY UNAVAILABLE
|
||||
// @description This plugin is no longer available, as Niantic optimisations have removed the data it needed.
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
|
@ -6,7 +6,7 @@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description PLUGIN CURRENTLY UNAVAILABLE
|
||||
// @description This plugin is no longer available, as Niantic optimisations have removed the data it needed.
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
|
@ -6,7 +6,7 @@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description PLUGIN CURRENTLY UNAVAILABLE
|
||||
// @description This plugin is no longer available, as Niantic optimisations have removed the data it needed.
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
|
@ -6,7 +6,7 @@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description PLUGIN CURRENTLY UNAVAILABLE
|
||||
// @description This plugin is no longer available, as Niantic optimisations have removed the data it needed.
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
|
@ -150,13 +150,13 @@ window.plugin.portalNames.updatePortalLabels = function() {
|
||||
|
||||
// ass calculating portal marker visibility can take some time when there's lots of portals shown, we'll do it on
|
||||
// a short timer. this way it doesn't get repeated so much
|
||||
window.plugin.portalNames.delayedUpdatePortalLabels = function() {
|
||||
window.plugin.portalNames.delayedUpdatePortalLabels = function(wait) {
|
||||
|
||||
if (window.plugin.portalNames.timer === undefined) {
|
||||
window.plugin.portalNames.timer = setTimeout ( function() {
|
||||
window.plugin.portalNames.timer = undefined;
|
||||
window.plugin.portalNames.updatePortalLabels();
|
||||
}, 0.5*1000);
|
||||
}, wait*1000);
|
||||
|
||||
}
|
||||
}
|
||||
@ -168,9 +168,9 @@ var setup = function() {
|
||||
window.plugin.portalNames.labelLayerGroup = new L.LayerGroup();
|
||||
window.addLayerGroup('Portal Names', window.plugin.portalNames.labelLayerGroup, true);
|
||||
|
||||
window.addHook('requestFinished', window.plugin.portalNames.delayedUpdatePortalLabels);
|
||||
window.addHook('mapDataRefreshEnd', window.plugin.portalNames.delayedUpdatePortalLabels);
|
||||
window.map.on('overlayadd overlayremove', window.plugin.portalNames.delayedUpdatePortalLabels);
|
||||
window.addHook('requestFinished', function() { setTimeout(function(){window.plugin.portalNames.delayedUpdatePortalLabels(3.0);},1); });
|
||||
window.addHook('mapDataRefreshEnd', function() { window.plugin.portalNames.delayedUpdatePortalLabels(0.5); });
|
||||
window.map.on('overlayadd overlayremove', function() { setTimeout(function(){window.plugin.portalNames.delayedUpdatePortalLabels(1.0);},1); });
|
||||
}
|
||||
|
||||
// PLUGIN END //////////////////////////////////////////////////////////
|
||||
|
@ -6,7 +6,7 @@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description PLUGIN CURRENTLY UNAVAILABLE
|
||||
// @description This plugin is no longer available, as Niantic optimisations have removed the data it needed.
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
|
@ -2,7 +2,7 @@
|
||||
// @id iitc-plugin-show-more-portals@jonatkins
|
||||
// @name IITC plugin: Show more portals
|
||||
// @category Tweaks
|
||||
// @version 0.1.6.@@DATETIMEVERSION@@
|
||||
// @version 0.2.0.@@DATETIMEVERSION@@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
@ -24,27 +24,9 @@ window.plugin.showMorePortals = function() {};
|
||||
|
||||
window.plugin.showMorePortals.setup = function() {
|
||||
|
||||
// save the original function - so we can chain to it for levels we don't modify
|
||||
var origGetMinPortalLevelForZoom = window.getMinPortalLevelForZoom;
|
||||
|
||||
// replace the window.getMinPortalLevelForZoom function - modify behaviour when L1+ or L3+ portals are shown
|
||||
|
||||
window.getMinPortalLevelForZoom = function(z) {
|
||||
var level = origGetMinPortalLevelForZoom(z);
|
||||
|
||||
// as of 2013-10-16...
|
||||
|
||||
// the stock site uses the same tile size for both L1+ portals and all portals
|
||||
// therefore, changing the L1+ zoom levels into all portals zoom level is not unfriendly to the servers
|
||||
// and the same applies for L2+ and L3+ detail levels
|
||||
// (in some ways it's nicer, as IITC caches better)
|
||||
|
||||
if (level == 1) level = 0;
|
||||
if (level == 3) level = 2;
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
// NOTE: the logic required is closely tied to the IITC+stock map detail level code - so the logic is moved there now
|
||||
// and just enabled by this flag
|
||||
window.CONFIG_ZOOM_SHOW_MORE_PORTALS=true;
|
||||
|
||||
};
|
||||
|
||||
|
@ -158,19 +158,17 @@ From here you can remove/disable individual plugins or IITC itself.
|
||||
END
|
||||
),
|
||||
|
||||
'mobile-plugins' => Array ( "IITC Mobile: Is it possible to add other plugins to IITC Mobile?",
|
||||
'mobile-plugins' => Array ( "IITC Mobile: Is it possible to add external plugins to IITC Mobile?",
|
||||
<<<'END'
|
||||
Yes it is!
|
||||
<ul>
|
||||
<li>Create a folder named "IITC_Mobile" in your home directory.</li>
|
||||
<li>Inside this folder, create a new folder named "plugins".</li>
|
||||
<li>Copy all your additional plugins to this folder.</li>
|
||||
<li>You should see your plugins listed above the official plugins.</li>
|
||||
<li>Navigate to the IITC Plugins preference screen and click the (+) icon at the top right. You can select the script using a file explorer of your choice.</li>
|
||||
<li>IITCm creates a new folder in your home directory, named "IITC_Mobile". Inside this folder you'll find a "plugins" folder where all external plugins are copied to.</li>
|
||||
</ul>
|
||||
Note:
|
||||
<ul>
|
||||
<li>The filename has to end with *.user.js.</li>
|
||||
<li>If you don't know where to find your home directory: Enable dev-mode in the settings and follow the hint.</li>
|
||||
<li>You can also trigger installation by clicking on http(s) links pointing to a plugin, selecting plugins with a file explorer, opening javascript e-mail attachments etc...
|
||||
</ul>
|
||||
END
|
||||
),
|
||||
|
@ -13,39 +13,30 @@ offers many more features. It is available for
|
||||
|
||||
<h3>Latest news</h3>
|
||||
|
||||
<h4>13th January 2014</h4>
|
||||
<h4>6th February 2014</h4>
|
||||
<p>
|
||||
A new IITC release, 0.16.2 and IITC Mobile 0.10.2 have been released. These are needed to work with a change to the
|
||||
standard intel site.
|
||||
</p>
|
||||
<p>
|
||||
Additionally, the 'Compute AP Statistics' plugin has been brought back, the 'blank map' base layer has a new black option
|
||||
to go with the white, and the 'Yandex' base map has had some bug fixes. Also, IITC Mobile features some changes to
|
||||
the 'show my location' feature. You may need to turn this option on again for it to work.
|
||||
</p>
|
||||
<p>
|
||||
<b>Update 14th January 2014</b>: An updated IITC Mobile, 0.10.3, has been released, to fix a crash issue seen by some.
|
||||
Also, a minor update was made to the main IITC script which changes the order the data is loaded, to match a change made to
|
||||
the standard intel site.
|
||||
</p>
|
||||
|
||||
<h4>21st December 2013</h4>
|
||||
<p>
|
||||
Just in time for the holidays, another IITC update. IITC 0.16.1 and IITC Mobile 0.10.1 have just been released.
|
||||
Changes include
|
||||
IITC 0.16.4 and IITC Mobile 0.10.4 have just been released. This version is required to fix a bug with showing portal details
|
||||
due to a change made by Niantic to the intel site protocol. Also, the following changes have been made:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Portals list plugin returns - but less data than before due to the Niantic backend changes</li>
|
||||
<li>Resonators plugin returns - but only shows the selected portal</li>
|
||||
<li>Mobile:
|
||||
<li>Portal markers are now reduced in size when you zoom out, reducing clutter when viewing large areas of the map</li>
|
||||
<li>Blocked a 3rd party plugin, arc, from running - it had spyware features hidden within it
|
||||
(<a href="https://plus.google.com/105383756361375410867/posts/4b2EjP3Du42">details here</a>).</li>
|
||||
<li>Plugins
|
||||
<ul>
|
||||
<li>Some plugins moved to panes from the left-swipe menu: portals list, portal counts</li>
|
||||
<li>Immersive fullscreen mode on Android 4.4 KitKat</li>
|
||||
<li>Sort apps in share activity - most used at the top</li>
|
||||
<li>Fix links sometimes being badly drawn on mobile</li>
|
||||
<li>add-kml: support for opening files on mobile added</li>
|
||||
<li>regions: new plugin to draw the scoreboard regions on the map. <i>No support for showing scores - this needs Niantic to add it to the standard intel site first</i></li>
|
||||
<li>score-cycle-times: new plugin to show the times of the scoreboard cycles</li>
|
||||
<li>draw-tools: added basic import/export (via copy+paste), and colour choosing options (click on "DrawTools Opt" in the sidebar)</li>
|
||||
<li>compute-ap-stats and portal-names: changed code to reduce the performance impact when a large number of portals are shown</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>.. and, as always, other various bug fixes, improvements, etc</li>
|
||||
<li>Mobile:
|
||||
<ul>
|
||||
<li>NFC support for sharing map view/selected portal - app permissions updated for this</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>.. plus various minor bugfixes and improvements</li>
|
||||
</ul>
|
||||
|
||||
<a class="btn btn-default btn-sm" href="?page=news">Older news</a>
|
||||
|
@ -1,5 +1,31 @@
|
||||
<h2>News</h2>
|
||||
|
||||
<h4>6th February 2014</h4>
|
||||
<p>
|
||||
IITC 0.16.4 and IITC Mobile 0.10.4 have just been released. This version is required to fix a bug with showing portal details
|
||||
due to a change made by Niantic to the intel site protocol. Also, the following changes have been made:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Portal markers are now reduced in size when you zoom out, reducing clutter when viewing large areas of the map</li>
|
||||
<li>Blocked a 3rd party plugin, arc, from running - it had spyware features hidden within it
|
||||
(<a href="https://plus.google.com/105383756361375410867/posts/4b2EjP3Du42">details here</a>).</li>
|
||||
<li>Plugins
|
||||
<ul>
|
||||
<li>add-kml: support for opening files on mobile added</li>
|
||||
<li>regions: new plugin to draw the scoreboard regions on the map. <i>No support for showing scores - this needs Niantic to add it to the standard intel site first</i></li>
|
||||
<li>score-cycle-times: new plugin to show the times of the scoreboard cycles</li>
|
||||
<li>draw-tools: added basic import/export (via copy+paste), and colour choosing options (click on "DrawTools Opt" in the sidebar)</li>
|
||||
<li>compute-ap-stats and portal-names: changed code to reduce the performance impact when a large number of portals are shown</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Mobile:
|
||||
<ul>
|
||||
<li>NFC support for sharing map view/selected portal - app permissions updated for this</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>.. plus various minor bugfixes and improvements</li>
|
||||
</ul>
|
||||
|
||||
<h4>13th January 2014</h4>
|
||||
<p>
|
||||
A new IITC release, 0.16.2 and IITC Mobile 0.10.2 have been released. These are needed to work with a change to the
|
||||
|
Loading…
x
Reference in New Issue
Block a user