From 706d48db00a7ccc79c55948098660650b55fbde7 Mon Sep 17 00:00:00 2001 From: Marcus Winkler Date: Sat, 1 Feb 2014 14:54:31 +0100 Subject: [PATCH 01/58] implemented color choosing support for markers --- plugins/draw-tools.user.js | 43 ++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/plugins/draw-tools.user.js b/plugins/draw-tools.user.js index 55aaf01b..64ffcc2a 100644 --- a/plugins/draw-tools.user.js +++ b/plugins/draw-tools.user.js @@ -34,8 +34,36 @@ window.plugin.drawTools.loadExternals = function() { $('head').append(''); } -window.plugin.drawTools.currentColor = '#a24ac3'; +window.plugin.drawTools.getMarkerIcon = function(color) { +console.log('getMarkerIcon("' + color + '")'); + if (typeof(color) === "undefined" || color.search(/^[0-9A-Fa-f]{6}$/) == -1) { + // TODO maybe improve error handling here. + console.log('Color is not set of not a valid color. Using default color as fallback.'); + color = "a24ac3"; + } + + // As the length of the color string is 6 charaters which translates to 8 base64 encodes characters + // and we ensured that the color in the svg template is properly aligned we can construct the + // resulting base64 encoded svg by replacing the color inside the base64 encoded template! + var svgIcon = 'data:image/svg+xml;base64,' + + 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIg0KCXZlcnNpb249IjEuMSIgYmFzZVByb2ZpbGU9ImZ1bGwiDQoJd2lkdGg9IjUwcHgiIGhlaWdodD0iODJweCIgdmlld0JveD0iMCAwIDUwIDgyIj4NCg0KCTxwYXRoIGQ9Ik0yLjcyNDgzNjg5NTMsMzcuMzQ5NzYyNDkzNSBBMjUsMjUgMCAxLDEgNDcuMjc1MTYzMTA0NywzNy4zNDk3NjI0OTM1IEwyNSw4MS4wNjcyMzE2MTQ2IFoiIHN0eWxlPSJzdHJva2U6bm9uZTsgICBmaWxsOiAj' + + btoa(color) + + 'OyIgLz4NCgk8cGF0aCBkPSJNMy42MTU4NDM0MTk1LDM2Ljg5NTc3MTk5MzcgQTI0LDI0IDAgMSwxIDQ2LjM4NDE1NjU4MDUsMzYuODk1NzcxOTkzNyBMMjUsNzguODY0NTQyMzUgWiIgc3R5bGU9InN0cm9rZTojMDAwMDAwOyBzdHJva2Utd2lkdGg6MnB4OyBzdHJva2Utb3BhY2l0eTogMC4xNTsgZmlsbDogbm9uZTsiIC8+DQoJPHBhdGggZD0iTTUuODQzMzU5NzMsMzUuNzYwNzk1NzQ0NCBBMjEuNSwyMS41IDAgMSwxIDQ0LjE1NjY0MDI3LDM1Ljc2MDc5NTc0NDQgTDI1LDczLjM1NzgxOTE4ODYgWiIgc3R5bGU9InN0cm9rZTojZmZmZmZmOyBzdHJva2Utd2lkdGg6M3B4OyBzdHJva2Utb3BhY2l0eTogMC4zNTsgZmlsbDogbm9uZTsiIC8+DQoNCgk8cGF0aCBkPSJNMzkuNzIyNDMxODY0MywzNC41IEwyNSw0MyBMMTAuMjc3NTY4MTM1NywzNC41IEwxMC4yNzc1NjgxMzU3LDE3LjUgTDI1LDkgTDM5LjcyMjQzMTg2NDMsMTcuNSBaIE0xNS40NzM3MjA1NTg0LDIwLjUgTDM0LjUyNjI3OTQ0MTYsMjAuNSBMMjUsMzcgWiBNMjUsMjYgTDE1LjQ3MzcyMDU1ODQsMjAuNSBNMjUsMjYgTDM0LjUyNjI3OTQ0MTYsMjAuNSBNMjUsMjYgTDI1LDM3IE0zOS43MjI0MzE4NjQzLDM0LjUgTDMyLjc5NDIyODYzNDEsMzAuNSBNMTAuMjc3NTY4MTM1NywzNC41IEwxNy4yMDU3NzEzNjU5LDMwLjUgTTI1LDkgTDI1LDE3IiBzdHlsZT0ic3Ryb2tlOiNmZmZmZmY7IHN0cm9rZS13aWR0aDoyLjVweDsgc3Ryb2tlLW9wYWNpdHk6IDE7IGZpbGw6IG5vbmU7IiAvPg0KDQoNCjwvc3ZnPg=='; + + // As we have a svg icon, we can use the same icon for the regular and the retina version. + return L.icon({ + iconUrl: svgIcon, + iconRetinaUrl: svgIcon, + iconSize: new L.Point(25, 41), + iconAnchor: new L.Point(12, 41), + popupAnchor: new L.Point(1, -34), + shadowSize: new L.Point(41, 41) + }); +} + +window.plugin.drawTools.currentColor = '#a24ac3'; +window.plugin.drawTools.currentMarker = window.plugin.drawTools.getMarkerIcon('a24ac3'); window.plugin.drawTools.setOptions = function() { @@ -60,7 +88,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 +96,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.substr(1)); 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 +143,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 + }), }, From adb0baa46fed00982920cec87f68c5da4b493c31 Mon Sep 17 00:00:00 2001 From: Marcus Winkler Date: Sat, 1 Feb 2014 15:45:48 +0100 Subject: [PATCH 02/58] added color support to serializing/deserializing drawn markers --- plugins/draw-tools.user.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugins/draw-tools.user.js b/plugins/draw-tools.user.js index 64ffcc2a..c8cfc05e 100644 --- a/plugins/draw-tools.user.js +++ b/plugins/draw-tools.user.js @@ -39,7 +39,7 @@ window.plugin.drawTools.getMarkerIcon = function(color) { console.log('getMarkerIcon("' + color + '")'); if (typeof(color) === "undefined" || color.search(/^[0-9A-Fa-f]{6}$/) == -1) { // TODO maybe improve error handling here. - console.log('Color is not set of not a valid color. Using default color as fallback.'); + console.warn('Color is not set or not a valid color. Using default color as fallback.'); color = "a24ac3"; } @@ -58,7 +58,10 @@ console.log('getMarkerIcon("' + color + '")'); iconSize: new L.Point(25, 41), iconAnchor: new L.Point(12, 41), popupAnchor: new L.Point(1, -34), - shadowSize: new L.Point(41, 41) + shadowSize: new L.Point(41, 41), + // L.icon does not use the option color, but we store it here to + // be able to simply retrieve the color for serializing markers + color: '#' + color }); } @@ -221,6 +224,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' @@ -264,7 +268,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.substr(1)); + 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'); From 7d8c4a9794d395b05c9ec32af6b68528bd8d0b12 Mon Sep 17 00:00:00 2001 From: Dunstkreis Date: Sun, 2 Feb 2014 11:18:11 +0100 Subject: [PATCH 03/58] Update leaflet.draw.css fixed line break between save/cancel for delete layers --- external/leaflet.draw.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/leaflet.draw.css b/external/leaflet.draw.css index 900608fd..31e59c89 100644 --- a/external/leaflet.draw.css +++ b/external/leaflet.draw.css @@ -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, From fb09a53b075848579b1c486b9720595e9dfd11b9 Mon Sep 17 00:00:00 2001 From: Giacinto Garcea Date: Sun, 2 Feb 2014 22:28:07 +0100 Subject: [PATCH 04/58] Bookmarks 2.8 - vertical scrollbar; - button to hide/show "x" buttons; - bookmarks full-width in mobile (no margin-right); - minor UI and UX fix; --- plugins/bookmarks-by-zaso.user.js | 10 +- plugins/bookmarks-css.css | 165 +++++++++++++++++++++--------- 2 files changed, 124 insertions(+), 51 deletions(-) diff --git a/plugins/bookmarks-by-zaso.user.js b/plugins/bookmarks-by-zaso.user.js index e15075f9..5363e5b2 100644 --- a/plugins/bookmarks-by-zaso.user.js +++ b/plugins/bookmarks-by-zaso.user.js @@ -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@@ @@ -433,6 +433,11 @@ } } + window.plugin.bookmarks.deleteMode = function() { + $('#bookmarksBox').toggleClass('deleteMode'); + } + + /***************************************************************************************************************************************************************/ // Saved the new sort of the folders (in the localStorage) @@ -489,6 +494,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 +507,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'); @@ -885,6 +892,7 @@ +'
' +'-' +'
...
' + +'Show/Hide "X" button' +'
' +'
' +'
Maps
' diff --git a/plugins/bookmarks-css.css b/plugins/bookmarks-css.css index 9c160667..30334150 100644 --- a/plugins/bookmarks-css.css +++ b/plugins/bookmarks-css.css @@ -9,13 +9,18 @@ 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; } #bookmarksBox .addForm, #bookmarksBox #bookmarksTypeBar, @@ -25,33 +30,41 @@ 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; +/* text-indent:-20px;*/ + 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 +72,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 +87,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 +103,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 +122,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 +142,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 +179,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 +192,7 @@ color:#03fe03; } #bookmarksBox ul .othersBookmarks .bookmarksLink{ - width:190px; + width:90%; } #bookmarksBox ul .bookmarksLink:hover{ color:#03fe03; @@ -183,6 +201,8 @@ color:#fff; background:#e22 !important; } + +/*---- UI border -----*/ #bookmarksBox, #bookmarksBox *{ border-color:#20a8b1; @@ -193,29 +213,31 @@ #bookmarksBox ul .bookmarkFolder{ border-top-width:1px; } +#bookmarksBox, #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; } @@ -272,17 +294,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 +334,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 +366,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 +376,7 @@ } #bookmarksBox .bookmarkList .bookmarkFolder ul{ display:none; - margin-left:19px; + margin-left:10%; } #bookmarksBox .bookmarkList .bookmarkFolder.open ul{ display:block; @@ -355,6 +386,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 **********************************************/ @@ -380,14 +431,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 +477,15 @@ #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; +/* width:85%;*/ + height:36px; + line-height:24px; overflow:hidden; } #bookmarksBox.mobile .bookmarkList ul li.bookmarkFolder ul{ @@ -428,7 +493,7 @@ } #bookmarksBox.mobile .bookmarkList > ul{ border-bottom:1px solid #20a8b1 !important; - border-right:1px solid #20a8b1 !important; +/* border-right:1px solid #20a8b1 !important;*/ } #bookmarksBox.mobile .bookmarkList .bookmarkFolder.othersBookmarks ul{ @@ -440,14 +505,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 +526,7 @@ } #bookmarksBox.mobile .addForm, #bookmarksBox.mobile .addForm *{ - height:35px; + height:35px !important; padding:0; } #bookmarksBox.mobile .addForm a{ @@ -469,14 +534,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 +595,10 @@ #bookmarksBox.mobile .bookmarkList .bookmarkFolder.open .folderLabel > span > span{ display:block !important; } + /********************************************** DIALOG BOX **********************************************/ - /*---- Auto Drawer -----*/ #bkmrksAutoDrawer, #bkmrksAutoDrawer p, @@ -597,4 +662,4 @@ width:96%; height:120px; resize:vertical; -} +} \ No newline at end of file From 3f17320de9b035282cc548663af4f4ac92bc1b9e Mon Sep 17 00:00:00 2001 From: Giacinto Garcea Date: Mon, 3 Feb 2014 12:00:47 +0100 Subject: [PATCH 05/58] 'auto draw' support drawtools current color --- plugins/bookmarks-by-zaso.user.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/plugins/bookmarks-by-zaso.user.js b/plugins/bookmarks-by-zaso.user.js index 5363e5b2..a4997013 100644 --- a/plugins/bookmarks-by-zaso.user.js +++ b/plugins/bookmarks-by-zaso.user.js @@ -2,7 +2,7 @@ // @id iitc-plugin-bookmarks@ZasoGD // @name IITC plugin: Bookmarks for maps and portals // @category Controls -// @version 0.2.8.@@DATETIMEVERSION@@ +// @version 0.2.81.@@DATETIMEVERSION@@ // @namespace https://github.com/jonatkins/ingress-intel-total-conversion // @updateURL @@UPDATEURL@@ // @downloadURL @@DOWNLOADURL@@ @@ -555,17 +555,22 @@ 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. '); + try { + 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('Import failed '); + } } } 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(); @@ -625,6 +630,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 { From 2c743d0c17a2f4781769ffbeb9531a6c8d4f60c8 Mon Sep 17 00:00:00 2001 From: Marcus Winkler Date: Thu, 6 Feb 2014 00:40:54 +0100 Subject: [PATCH 06/58] replaced L.icon containing data-URI-images with L.divIcon --- images/marker-icon.svg.template | 12 +++++++++++ plugins/draw-tools.user.js | 35 +++++++++++---------------------- 2 files changed, 24 insertions(+), 23 deletions(-) create mode 100644 images/marker-icon.svg.template diff --git a/images/marker-icon.svg.template b/images/marker-icon.svg.template new file mode 100644 index 00000000..26b8da11 --- /dev/null +++ b/images/marker-icon.svg.template @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/plugins/draw-tools.user.js b/plugins/draw-tools.user.js index c8cfc05e..d07c4ebe 100644 --- a/plugins/draw-tools.user.js +++ b/plugins/draw-tools.user.js @@ -35,38 +35,27 @@ window.plugin.drawTools.loadExternals = function() { } window.plugin.drawTools.getMarkerIcon = function(color) { - -console.log('getMarkerIcon("' + color + '")'); - if (typeof(color) === "undefined" || color.search(/^[0-9A-Fa-f]{6}$/) == -1) { - // TODO maybe improve error handling here. + if (typeof(color) === 'undefined') { console.warn('Color is not set or not a valid color. Using default color as fallback.'); - color = "a24ac3"; + color = '#a24ac3'; } - // As the length of the color string is 6 charaters which translates to 8 base64 encodes characters - // and we ensured that the color in the svg template is properly aligned we can construct the - // resulting base64 encoded svg by replacing the color inside the base64 encoded template! - var svgIcon = 'data:image/svg+xml;base64,' + - 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIg0KCXZlcnNpb249IjEuMSIgYmFzZVByb2ZpbGU9ImZ1bGwiDQoJd2lkdGg9IjUwcHgiIGhlaWdodD0iODJweCIgdmlld0JveD0iMCAwIDUwIDgyIj4NCg0KCTxwYXRoIGQ9Ik0yLjcyNDgzNjg5NTMsMzcuMzQ5NzYyNDkzNSBBMjUsMjUgMCAxLDEgNDcuMjc1MTYzMTA0NywzNy4zNDk3NjI0OTM1IEwyNSw4MS4wNjcyMzE2MTQ2IFoiIHN0eWxlPSJzdHJva2U6bm9uZTsgICBmaWxsOiAj' + - btoa(color) + - 'OyIgLz4NCgk8cGF0aCBkPSJNMy42MTU4NDM0MTk1LDM2Ljg5NTc3MTk5MzcgQTI0LDI0IDAgMSwxIDQ2LjM4NDE1NjU4MDUsMzYuODk1NzcxOTkzNyBMMjUsNzguODY0NTQyMzUgWiIgc3R5bGU9InN0cm9rZTojMDAwMDAwOyBzdHJva2Utd2lkdGg6MnB4OyBzdHJva2Utb3BhY2l0eTogMC4xNTsgZmlsbDogbm9uZTsiIC8+DQoJPHBhdGggZD0iTTUuODQzMzU5NzMsMzUuNzYwNzk1NzQ0NCBBMjEuNSwyMS41IDAgMSwxIDQ0LjE1NjY0MDI3LDM1Ljc2MDc5NTc0NDQgTDI1LDczLjM1NzgxOTE4ODYgWiIgc3R5bGU9InN0cm9rZTojZmZmZmZmOyBzdHJva2Utd2lkdGg6M3B4OyBzdHJva2Utb3BhY2l0eTogMC4zNTsgZmlsbDogbm9uZTsiIC8+DQoNCgk8cGF0aCBkPSJNMzkuNzIyNDMxODY0MywzNC41IEwyNSw0MyBMMTAuMjc3NTY4MTM1NywzNC41IEwxMC4yNzc1NjgxMzU3LDE3LjUgTDI1LDkgTDM5LjcyMjQzMTg2NDMsMTcuNSBaIE0xNS40NzM3MjA1NTg0LDIwLjUgTDM0LjUyNjI3OTQ0MTYsMjAuNSBMMjUsMzcgWiBNMjUsMjYgTDE1LjQ3MzcyMDU1ODQsMjAuNSBNMjUsMjYgTDM0LjUyNjI3OTQ0MTYsMjAuNSBNMjUsMjYgTDI1LDM3IE0zOS43MjI0MzE4NjQzLDM0LjUgTDMyLjc5NDIyODYzNDEsMzAuNSBNMTAuMjc3NTY4MTM1NywzNC41IEwxNy4yMDU3NzEzNjU5LDMwLjUgTTI1LDkgTDI1LDE3IiBzdHlsZT0ic3Ryb2tlOiNmZmZmZmY7IHN0cm9rZS13aWR0aDoyLjVweDsgc3Ryb2tlLW9wYWNpdHk6IDE7IGZpbGw6IG5vbmU7IiAvPg0KDQoNCjwvc3ZnPg=='; + var svgIcon = window.plugin.drawTools.markerTemplate.replace(/%COLOR%/g, color); - // As we have a svg icon, we can use the same icon for the regular and the retina version. - return L.icon({ - iconUrl: svgIcon, - iconRetinaUrl: svgIcon, + return L.divIcon({ iconSize: new L.Point(25, 41), iconAnchor: new L.Point(12, 41), - popupAnchor: new L.Point(1, -34), - shadowSize: new L.Point(41, 41), - // L.icon does not use the option color, but we store it here to + 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 + color: color }); } window.plugin.drawTools.currentColor = '#a24ac3'; -window.plugin.drawTools.currentMarker = window.plugin.drawTools.getMarkerIcon('a24ac3'); +window.plugin.drawTools.markerTemplate = '@@INCLUDESTRING:images/marker-icon.svg.template@@'; +window.plugin.drawTools.currentMarker = window.plugin.drawTools.getMarkerIcon(window.plugin.drawTools.currentColor); window.plugin.drawTools.setOptions = function() { @@ -99,7 +88,7 @@ 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.substr(1)); + window.plugin.drawTools.currentMarker = window.plugin.drawTools.getMarkerIcon(color); window.plugin.drawTools.drawControl.setDrawingOptions({ 'polygon': { 'shapeOptions': { color: color } }, @@ -269,7 +258,7 @@ window.plugin.drawTools.import = function(data) { break; case 'marker': var extraMarkerOpt = {}; - if (item.color) extraMarkerOpt.icon = window.plugin.drawTools.getMarkerIcon(item.color.substr(1)); + 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: From 53d04c70a59e20b85d107892266915b08dbb41d3 Mon Sep 17 00:00:00 2001 From: Giacinto Garcea Date: Thu, 6 Feb 2014 10:08:16 +0100 Subject: [PATCH 07/58] Fixed number version --- plugins/bookmarks-by-zaso.user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/bookmarks-by-zaso.user.js b/plugins/bookmarks-by-zaso.user.js index a4997013..cbdd7a2a 100644 --- a/plugins/bookmarks-by-zaso.user.js +++ b/plugins/bookmarks-by-zaso.user.js @@ -2,7 +2,7 @@ // @id iitc-plugin-bookmarks@ZasoGD // @name IITC plugin: Bookmarks for maps and portals // @category Controls -// @version 0.2.81.@@DATETIMEVERSION@@ +// @version 0.2.8.@@DATETIMEVERSION@@ // @namespace https://github.com/jonatkins/ingress-intel-total-conversion // @updateURL @@UPDATEURL@@ // @downloadURL @@DOWNLOADURL@@ From 3fce1628884e389a9227b5a794cb1036c7847093 Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Thu, 6 Feb 2014 17:50:28 +0000 Subject: [PATCH 08/58] website: update for new 0.16.4 release --- website/page/home.php | 45 +++++++++++++++++-------------------------- website/page/news.php | 26 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/website/page/home.php b/website/page/home.php index f46f56e4..399a1b46 100644 --- a/website/page/home.php +++ b/website/page/home.php @@ -13,39 +13,30 @@ offers many more features. It is available for

Latest news

-

13th January 2014

+

6th February 2014

-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. -

-

-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. -

-

-Update 14th January 2014: 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. -

- -

21st December 2013

-

-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:

    -
  • Portals list plugin returns - but less data than before due to the Niantic backend changes
  • -
  • Resonators plugin returns - but only shows the selected portal
  • -
  • Mobile: +
  • Portal markers are now reduced in size when you zoom out, reducing clutter when viewing large areas of the map
  • +
  • Blocked a 3rd party plugin, arc, from running - it had spyware features hidden within it +(details here).
  • +
  • Plugins
      -
    • Some plugins moved to panes from the left-swipe menu: portals list, portal counts
    • -
    • Immersive fullscreen mode on Android 4.4 KitKat
    • -
    • Sort apps in share activity - most used at the top
    • -
    • Fix links sometimes being badly drawn on mobile
    • +
    • add-kml: support for opening files on mobile added
    • +
    • regions: new plugin to draw the scoreboard regions on the map. No support for showing scores - this needs Niantic to add it to the standard intel site first
    • +
    • score-cycle-times: new plugin to show the times of the scoreboard cycles
    • +
    • draw-tools: added basic import/export (via copy+paste), and colour choosing options (click on "DrawTools Opt" in the sidebar)
    • +
    • compute-ap-stats and portal-names: changed code to reduce the performance impact when a large number of portals are shown
  • -
  • .. and, as always, other various bug fixes, improvements, etc
  • +
  • Mobile: +
      +
    • NFC support for sharing map view/selected portal - app permissions updated for this
    • +
    +
  • +
  • .. plus various minor bugfixes and improvements
Older news diff --git a/website/page/news.php b/website/page/news.php index c14bc1c3..20c36b14 100644 --- a/website/page/news.php +++ b/website/page/news.php @@ -1,5 +1,31 @@

News

+

6th February 2014

+

+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: +

+
    +
  • Portal markers are now reduced in size when you zoom out, reducing clutter when viewing large areas of the map
  • +
  • Blocked a 3rd party plugin, arc, from running - it had spyware features hidden within it +(details here).
  • +
  • Plugins +
      +
    • add-kml: support for opening files on mobile added
    • +
    • regions: new plugin to draw the scoreboard regions on the map. No support for showing scores - this needs Niantic to add it to the standard intel site first
    • +
    • score-cycle-times: new plugin to show the times of the scoreboard cycles
    • +
    • draw-tools: added basic import/export (via copy+paste), and colour choosing options (click on "DrawTools Opt" in the sidebar)
    • +
    • compute-ap-stats and portal-names: changed code to reduce the performance impact when a large number of portals are shown
    • +
    +
  • +
  • Mobile: +
      +
    • NFC support for sharing map view/selected portal - app permissions updated for this
    • +
    +
  • +
  • .. plus various minor bugfixes and improvements
  • +
+

13th January 2014

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 From 24dbeaca9aa84ab4ac887b5fdb208f88816e6c3b Mon Sep 17 00:00:00 2001 From: Giacinto Garcea Date: Thu, 6 Feb 2014 19:56:56 +0100 Subject: [PATCH 09/58] Fixed problem with jQuery UI sortable - jQuery UI sortable drag initiation is slow when container has hidden items; - removed old code to open/close BookmarksBox in IITCm; --- plugins/bookmarks-by-zaso.user.js | 16 +++++----------- plugins/bookmarks-css.css | 13 +++++++------ 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/plugins/bookmarks-by-zaso.user.js b/plugins/bookmarks-by-zaso.user.js index cbdd7a2a..f880efba 100644 --- a/plugins/bookmarks-by-zaso.user.js +++ b/plugins/bookmarks-by-zaso.user.js @@ -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; @@ -919,6 +912,7 @@ +'+ Folder' +'

' +'' + +'
' +''; plugin.bookmarks.htmlDisabledMessage = '
Plugin Bookmarks disabled*.
'; diff --git a/plugins/bookmarks-css.css b/plugins/bookmarks-css.css index 30334150..2ad998d7 100644 --- a/plugins/bookmarks-css.css +++ b/plugins/bookmarks-css.css @@ -15,12 +15,14 @@ } #bookmarksBox{ - display:block; + display:block !important; position:absolute !important; z-index:4001; top:100px; left:100px; width:231px; + height:auto; + overflow:hidden; } #bookmarksBox .addForm, #bookmarksBox #bookmarksTypeBar, @@ -40,7 +42,6 @@ float:left !important; } #bookmarksBox .handle{ -/* text-indent:-20px;*/ width:80%; text-align:center; color:#fff; @@ -213,7 +214,7 @@ #bookmarksBox ul .bookmarkFolder{ border-top-width:1px; } -#bookmarksBox, + #bookmarksBox #topBar, #bookmarksBox #bookmarksTypeBar, #bookmarksBox .addForm, @@ -242,13 +243,15 @@ 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; @@ -483,7 +486,6 @@ #bookmarksBox.mobile .bookmarkList li.bookmarkFolder a.bookmarksAnchor, #bookmarksBox.mobile .bookmarkList li.bkmrk a.bookmarksLink{ text-indent:10px; -/* width:85%;*/ height:36px; line-height:24px; overflow:hidden; @@ -493,7 +495,6 @@ } #bookmarksBox.mobile .bookmarkList > ul{ border-bottom:1px solid #20a8b1 !important; -/* border-right:1px solid #20a8b1 !important;*/ } #bookmarksBox.mobile .bookmarkList .bookmarkFolder.othersBookmarks ul{ From daf75dbf7210a80cdc5aecdbf3fb9e6ac020d69b Mon Sep 17 00:00:00 2001 From: Giacinto Garcea Date: Thu, 6 Feb 2014 20:21:16 +0100 Subject: [PATCH 10/58] Fixed css for mobile version The Bookmarks Panel not closed. Now work. --- plugins/bookmarks-css.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/bookmarks-css.css b/plugins/bookmarks-css.css index 2ad998d7..a8630974 100644 --- a/plugins/bookmarks-css.css +++ b/plugins/bookmarks-css.css @@ -15,7 +15,7 @@ } #bookmarksBox{ - display:block !important; + display:block; position:absolute !important; z-index:4001; top:100px; @@ -421,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, From 019e044b921d4358582336d6c9ca169c21375ce2 Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Thu, 6 Feb 2014 22:24:25 +0000 Subject: [PATCH 11/58] add artifact support for likely new 'amar artifacts'. seems to be the same system as the jarvis shards, but different display names, and URLs for resources --- code/artifact.js | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/code/artifact.js b/code/artifact.js index d52abc72..d2926598 100644 --- a/code/artifact.js +++ b/code/artifact.js @@ -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() {} @@ -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,11 +235,16 @@ window.artifact.updateLayer = function() { window.artifact.showArtifactList = function() { - var html = '
Artifact portals
'; + 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 += 'No artifacts at this time'; + } + + $.each(artifact.artifactTypes, function(type,type2) { + var name = typeNames[type] || ('New artifact type: '+type); html += '
'+types[type]+'
'; From 06b2e422a2c4f1f3ec054aef01b0db8c15ed6c06 Mon Sep 17 00:00:00 2001 From: Breezewish Date: Fri, 7 Feb 2014 13:37:52 +0800 Subject: [PATCH 12/58] added fix-googlemap-china-offset plugin --- plugins/fix-googlemap-china-offset.user.js | 198 +++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 plugins/fix-googlemap-china-offset.user.js diff --git a/plugins/fix-googlemap-china-offset.user.js b/plugins/fix-googlemap-china-offset.user.js new file mode 100644 index 00000000..a25b271a --- /dev/null +++ b/plugins/fix-googlemap-china-offset.user.js @@ -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@@ From 2054b5f7902d780a826e4145cef3531e7e5d6861 Mon Sep 17 00:00:00 2001 From: Philipp Schaefer Date: Wed, 12 Feb 2014 14:53:31 +0100 Subject: [PATCH 13/58] added 'add external plugin' button to IITC plugins preference. ensures that the scripts are always copied to the correct folder. --- mobile/res/drawable-hdpi/ic_action_new.png | Bin 0 -> 262 bytes mobile/res/drawable-mdpi/ic_action_new.png | Bin 0 -> 185 bytes mobile/res/drawable-xhdpi/ic_action_new.png | Bin 0 -> 250 bytes mobile/res/drawable-xxhdpi/ic_action_new.png | Bin 0 -> 288 bytes mobile/res/menu/plugins.xml | 12 +++ mobile/res/values/strings.xml | 6 +- .../IITC_PluginPreferenceActivity.java | 75 +++++++++++++++++- website/page/faq.php | 10 +-- 8 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 mobile/res/drawable-hdpi/ic_action_new.png create mode 100644 mobile/res/drawable-mdpi/ic_action_new.png create mode 100644 mobile/res/drawable-xhdpi/ic_action_new.png create mode 100644 mobile/res/drawable-xxhdpi/ic_action_new.png create mode 100644 mobile/res/menu/plugins.xml diff --git a/mobile/res/drawable-hdpi/ic_action_new.png b/mobile/res/drawable-hdpi/ic_action_new.png new file mode 100644 index 0000000000000000000000000000000000000000..d866d6160db2dade92436e92401864bf79ab37f9 GIT binary patch literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GD?D8sLn>~)nSPMBK|z4!;0>128*SSfO(WZG({C@e<@zQ{J^CA$J^9^h-4+HW z4uu9V;m}>FaPIT6gtre({HFI@`KRUogk{zopr05h*# A?EnA( literal 0 HcmV?d00001 diff --git a/mobile/res/drawable-mdpi/ic_action_new.png b/mobile/res/drawable-mdpi/ic_action_new.png new file mode 100644 index 0000000000000000000000000000000000000000..f17e7980ee9aee545cb23bf93e9b21613278514c GIT binary patch literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1G(Vi}jAr-gYOn2mC5a3}6{c~dRo|E${f{)I<>^0$x%HC5M zDbb1y96PG#E}b*crF~7D(!F;|OdJXg4Qn5Lbvcy#A}ws*s|Kr{TRL)zK;aKt8+M77 g+o>-wVv%4tbz9`!VZkj~Kx-I0UHx3vIVCg!07CUbG5`Po literal 0 HcmV?d00001 diff --git a/mobile/res/drawable-xhdpi/ic_action_new.png b/mobile/res/drawable-xhdpi/ic_action_new.png new file mode 100644 index 0000000000000000000000000000000000000000..5508b5c0c80873705bd4a87b5b3f322682b8ecc6 GIT binary patch literal 250 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X=6Jd|hE&{ob8{o_0Rq5^R4#zoa!TTmI4pvG3cp7q|nHQyOG-t(>I#F z#D3}eTWRVH4?g^mC}cuY2XL>W2A$!YSA2~)w76%4K0S5*a0S5fcdD8Rv zceE*+xnB=)Yj?TB;6H(ZLxCZ%&M`j0O9;PKB(w9LEqJYWi4o`^fwP(i7(N^q{1!7k Q^CifIp00i_>zopr0M9i`&Hw-a literal 0 HcmV?d00001 diff --git a/mobile/res/menu/plugins.xml b/mobile/res/menu/plugins.xml new file mode 100644 index 00000000..3ea61645 --- /dev/null +++ b/mobile/res/menu/plugins.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/mobile/res/values/strings.xml b/mobile/res/values/strings.xml index 68552943..710a3140 100644 --- a/mobile/res/values/strings.xml +++ b/mobile/res/values/strings.xml @@ -86,9 +86,8 @@
IITC Mobile is able to load external plugins too!

- • create %1$s
- • move *.user.js files there
- • 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 %1$s
]]>
Share portal you can:
@@ -162,6 +161,7 @@ Clear Cookies Search Debug + Add external plugins Choose account to login Login failed. Search Locations diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_PluginPreferenceActivity.java b/mobile/src/com/cradle/iitc_mobile/IITC_PluginPreferenceActivity.java index 8c63b348..b3157a01 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_PluginPreferenceActivity.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_PluginPreferenceActivity.java @@ -1,26 +1,32 @@ package com.cradle.iitc_mobile; +import android.content.ActivityNotFoundException; import android.content.Context; +import android.content.Intent; import android.content.res.AssetManager; 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; 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.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -29,6 +35,8 @@ import java.util.TreeMap; public class IITC_PluginPreferenceActivity extends PreferenceActivity { + private final static int COPY_PLUGIN_REQUEST = 1; + private List
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 @@ -103,17 +111,75 @@ 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) { + String filePath = data.getData().getPath(); + copyPlugin(filePath); + return; + } + break; + default: + super.onActivityResult(requestCode, resultCode, data); + break; + + } + } + + private void copyPlugin(String pluginPath) { + try { + File inFile = new File(pluginPath); + // create IITCm external plugins directory if it doesn't already exist + File pluginsDirectory = getUserPluginsDirectory(); + pluginsDirectory.mkdirs(); + + // create in and out streams and copy plugin + File outFile = new File(pluginsDirectory.getPath() + "/" + inFile.getName()); + InputStream is = new FileInputStream(inFile); + OutputStream os = new FileOutputStream(outFile); + IITC_FileManager.copyStream(is, os, true); + + // invalidate headers to build a fresh preference screen with the new plugin listed + invalidateHeaders(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + @Override protected boolean isValidFragment(final String s) { return true; @@ -140,10 +206,15 @@ public class IITC_PluginPreferenceActivity extends PreferenceActivity { return asset_array; } - private File[] getUserPlugins() { + private File getUserPluginsDirectory() { final String iitc_path = Environment.getExternalStorageDirectory().getPath() + "/IITC_Mobile/"; final File directory = new File(iitc_path + "plugins/"); + return directory; + } + + private File[] getUserPlugins() { + final File directory = getUserPluginsDirectory(); File[] files = directory.listFiles(); if (files == null) { files = new File[0]; @@ -164,6 +235,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(); } } diff --git a/website/page/faq.php b/website/page/faq.php index cf706b6b..3c4f1e93 100644 --- a/website/page/faq.php +++ b/website/page/faq.php @@ -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!
    -
  • Create a folder named "IITC_Mobile" in your home directory.
  • -
  • Inside this folder, create a new folder named "plugins".
  • -
  • Copy all your additional plugins to this folder.
  • -
  • You should see your plugins listed above the official plugins.
  • +
  • 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.
  • +
  • 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.
Note:
  • The filename has to end with *.user.js.
  • -
  • If you don't know where to find your home directory: Enable dev-mode in the settings and follow the hint.
  • +
  • You need a file explorer app installed to add external plugins
END ), From b9499e2d1b85295c3bf7a1e181a3d4e9b24d9c56 Mon Sep 17 00:00:00 2001 From: Philipp Schaefer Date: Wed, 12 Feb 2014 23:01:02 +0100 Subject: [PATCH 14/58] trigger plugin installation on *.user.js file intent --- mobile/AndroidManifest.xml | 39 ++++++++ mobile/res/values/strings.xml | 5 + .../cradle/iitc_mobile/IITC_FileManager.java | 98 ++++++++++++++++--- .../com/cradle/iitc_mobile/IITC_Mobile.java | 7 ++ .../IITC_PluginPreferenceActivity.java | 45 +++------ 5 files changed, 149 insertions(+), 45 deletions(-) diff --git a/mobile/AndroidManifest.xml b/mobile/AndroidManifest.xml index 8472c725..f5a18f1b 100644 --- a/mobile/AndroidManifest.xml +++ b/mobile/AndroidManifest.xml @@ -72,6 +72,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Base Layer Overlay Layers + Install external plugin? + + IITCm was requested to install the following plugin:\n\n%1$s\n\nDo you want to proceed? + + diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java b/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java index 6ec999eb..c66a0ccb 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java @@ -1,7 +1,9 @@ package com.cradle.iitc_mobile; 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; @@ -22,11 +24,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; @@ -118,15 +123,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 +142,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 +240,73 @@ public class IITC_FileManager { return EMPTY; } + public void installPlugin(final String uri, final boolean invalidateHeaders) { + if (uri != null) { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mActivity); + + // set title + alertDialogBuilder.setTitle(mActivity.getString(R.string.install_dialog_top)); + + // set dialog message + String text = mActivity.getString(R.string.install_dialog_msg); + text = String.format(text, uri); + alertDialogBuilder + .setMessage(text) + .setCancelable(true) + .setPositiveButton("Yes", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + copyPlugin(uri, invalidateHeaders); + } + }) + .setNegativeButton("No", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + + // create alert dialog + AlertDialog alertDialog = alertDialogBuilder.create(); + + // show it + alertDialog.show(); + } + } + + private void copyPlugin(final String uri, final boolean invalidateHeaders) { + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + try { + final URL url = new URL(uri); + final URLConnection conn = url.openConnection(); + final String fileName = uri.substring( uri.lastIndexOf('/')+1, uri.length() ); + final InputStream is = conn.getInputStream(); + // 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 + File outFile = new File(pluginsDirectory + "/" + fileName); + OutputStream os = new FileOutputStream(outFile); + IITC_FileManager.copyStream(is, os, true); + } catch (IOException e) { + Log.w(e); + } + } + }); + thread.start(); + if (invalidateHeaders) { + try { + thread.join(); + ((IITC_PluginPreferenceActivity) mActivity).invalidateHeaders(); + } catch (InterruptedException e) { + Log.w(e); + } + } + } + private class FileRequest extends WebResourceResponse implements ResponseHandler, Runnable { private Intent mData; private final String mFunctionName; @@ -258,16 +332,18 @@ public class IITC_FileManager { target.addCategory(Intent.CATEGORY_OPENABLE); try { - mIitc.startActivityForResult(Intent.createChooser(target, "Choose file"), this); + final IITC_Mobile iitc = (IITC_Mobile) mActivity; + 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; diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java b/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java index 8c619247..2cea8b6a 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java @@ -228,6 +228,13 @@ public class IITC_Mobile extends Activity .show(); } } + + if (uri.getPath().endsWith(".user.js")) { + final Intent prefIntent = new Intent(this, IITC_PluginPreferenceActivity.class); + prefIntent.putExtra("url", uri.toString()); + startActivity(prefIntent); + // TODO receive intent, start dialog if user want to install $plugin, reload IITC + } } if (Intent.ACTION_SEARCH.equals(action)) { diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_PluginPreferenceActivity.java b/mobile/src/com/cradle/iitc_mobile/IITC_PluginPreferenceActivity.java index b3157a01..f588c272 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_PluginPreferenceActivity.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_PluginPreferenceActivity.java @@ -5,7 +5,6 @@ import android.content.Context; import android.content.Intent; import android.content.res.AssetManager; import android.os.Bundle; -import android.os.Environment; import android.preference.PreferenceActivity; import android.text.TextUtils; import android.view.LayoutInflater; @@ -23,10 +22,8 @@ import com.cradle.iitc_mobile.fragments.PluginsFragment; 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.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -47,6 +44,8 @@ public class IITC_PluginPreferenceActivity extends PreferenceActivity { new TreeMap>(); private static int mDeletedPlugins = 0; + private IITC_FileManager mFileManager; + @Override public void setListAdapter(final ListAdapter adapter) { if (adapter == null) { @@ -86,6 +85,13 @@ public class IITC_PluginPreferenceActivity extends PreferenceActivity { getIntent() .putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, PluginsFragment.class.getName()); } + + mFileManager = new IITC_FileManager(this); + + final String uri = getIntent().getStringExtra("url"); + if (uri != null) { + mFileManager.installPlugin(uri, true); + } super.onCreate(savedInstanceState); } @@ -147,7 +153,7 @@ public class IITC_PluginPreferenceActivity extends PreferenceActivity { case COPY_PLUGIN_REQUEST: if (data != null && data.getData() != null) { String filePath = data.getData().getPath(); - copyPlugin(filePath); + mFileManager.installPlugin("file://" + filePath, true); return; } break; @@ -158,28 +164,6 @@ public class IITC_PluginPreferenceActivity extends PreferenceActivity { } } - private void copyPlugin(String pluginPath) { - try { - File inFile = new File(pluginPath); - // create IITCm external plugins directory if it doesn't already exist - File pluginsDirectory = getUserPluginsDirectory(); - pluginsDirectory.mkdirs(); - - // create in and out streams and copy plugin - File outFile = new File(pluginsDirectory.getPath() + "/" + inFile.getName()); - InputStream is = new FileInputStream(inFile); - OutputStream os = new FileOutputStream(outFile); - IITC_FileManager.copyStream(is, os, true); - - // invalidate headers to build a fresh preference screen with the new plugin listed - invalidateHeaders(); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } - @Override protected boolean isValidFragment(final String s) { return true; @@ -206,15 +190,8 @@ public class IITC_PluginPreferenceActivity extends PreferenceActivity { return asset_array; } - private File getUserPluginsDirectory() { - final String iitc_path = Environment.getExternalStorageDirectory().getPath() - + "/IITC_Mobile/"; - final File directory = new File(iitc_path + "plugins/"); - return directory; - } - private File[] getUserPlugins() { - final File directory = getUserPluginsDirectory(); + final File directory = new File(IITC_FileManager.PLUGINS_PATH); File[] files = directory.listFiles(); if (files == null) { files = new File[0]; From b127059895cd07ffb70f3b9f75ae9d590c9be8fa Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Wed, 12 Feb 2014 22:30:36 +0000 Subject: [PATCH 15/58] remove references to jarvis shards. css class name changed --- code/artifact.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/artifact.js b/code/artifact.js index d2926598..736587bf 100644 --- a/code/artifact.js +++ b/code/artifact.js @@ -25,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(' Artifacts'); + $('#toolbox').append(' Artifacts'); } @@ -246,9 +246,9 @@ window.artifact.showArtifactList = function() { $.each(artifact.artifactTypes, function(type,type2) { var name = typeNames[type] || ('New artifact type: '+type); - html += '
'+types[type]+'
'; + html += '
'+name+'
'; - html += ''; + html += '
'; html += ''; var tableRows = []; From 9563ebcd1c0757d46594753d8e22751047a8f930 Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Wed, 12 Feb 2014 22:31:37 +0000 Subject: [PATCH 16/58] make artifact display on portal details more generic - might handle the new 'amar artifacts' now --- code/portal_detail_display.js | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/code/portal_detail_display.js b/code/portal_detail_display.js index 3bb64e93..1258c992 100644 --- a/code/portal_detail_display.js +++ b/code/portal_detail_display.js @@ -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', ''+(jarvisArtifact.target==TEAM_RES?'Resistance':'Enlightened')+'']; - } - 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', ''+(artdata.target==TEAM_RES?'Resistance':'Enlightened')+'']; + } + if (artdata.fragments) { + shards = [details.fragmentName, '#'+artdata.fragments.join(', #')]; + } + + randDetailsData.push (target, shards); + } + }); randDetails = '
PortalDetails
' + genFourColumnTable(randDetailsData) + '
'; From e46372a7759ced869bf9b4ab3c58912d9dceeb29 Mon Sep 17 00:00:00 2001 From: Philipp Schaefer Date: Thu, 13 Feb 2014 00:19:37 +0100 Subject: [PATCH 17/58] support e-mail attachments too for plugin installation --- .../cradle/iitc_mobile/IITC_FileManager.java | 29 +++++++++++++++---- .../com/cradle/iitc_mobile/IITC_Mobile.java | 5 ++-- .../IITC_PluginPreferenceActivity.java | 6 ++-- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java b/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java index c66a0ccb..62e2509d 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java @@ -3,6 +3,7 @@ package com.cradle.iitc_mobile; import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; +import android.content.ContentResolver; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; @@ -87,6 +88,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", ""); @@ -94,6 +96,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]); } @@ -240,7 +245,7 @@ public class IITC_FileManager { return EMPTY; } - public void installPlugin(final String uri, final boolean invalidateHeaders) { + public void installPlugin(final Uri uri, final boolean invalidateHeaders) { if (uri != null) { AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mActivity); @@ -274,15 +279,27 @@ public class IITC_FileManager { } } - private void copyPlugin(final String uri, final boolean invalidateHeaders) { + private void copyPlugin(final Uri uri, final boolean invalidateHeaders) { Thread thread = new Thread(new Runnable() { @Override public void run() { try { - final URL url = new URL(uri); - final URLConnection conn = url.openConnection(); - final String fileName = uri.substring( uri.lastIndexOf('/')+1, uri.length() ); - final InputStream is = conn.getInputStream(); + final String url = uri.toString(); + String fileName = url.substring( url.lastIndexOf('/')+1, url.length() ); + // we need 2 stream 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 + InputStream is, isCopy; + if (uri.getScheme().contains("http")) { + URLConnection conn = new URL(url).openConnection(); + URLConnection connCopy = new URL(url).openConnection(); + is = conn.getInputStream(); + isCopy = connCopy.getInputStream(); + } else { + is = mActivity.getContentResolver().openInputStream(uri); + 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(); diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java b/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java index 2cea8b6a..eac3b86a 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java @@ -229,11 +229,10 @@ public class IITC_Mobile extends Activity } } - if (uri.getPath().endsWith(".user.js")) { + if (uri.getPath().endsWith(".user.js") || intent.getType().contains("javascript")) { final Intent prefIntent = new Intent(this, IITC_PluginPreferenceActivity.class); - prefIntent.putExtra("url", uri.toString()); + prefIntent.setDataAndType(uri, intent.getType()); startActivity(prefIntent); - // TODO receive intent, start dialog if user want to install $plugin, reload IITC } } diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_PluginPreferenceActivity.java b/mobile/src/com/cradle/iitc_mobile/IITC_PluginPreferenceActivity.java index f588c272..a20ed8f0 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_PluginPreferenceActivity.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_PluginPreferenceActivity.java @@ -4,6 +4,7 @@ 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.preference.PreferenceActivity; import android.text.TextUtils; @@ -88,7 +89,7 @@ public class IITC_PluginPreferenceActivity extends PreferenceActivity { mFileManager = new IITC_FileManager(this); - final String uri = getIntent().getStringExtra("url"); + final Uri uri = getIntent().getData(); if (uri != null) { mFileManager.installPlugin(uri, true); } @@ -152,8 +153,7 @@ public class IITC_PluginPreferenceActivity extends PreferenceActivity { switch(requestCode) { case COPY_PLUGIN_REQUEST: if (data != null && data.getData() != null) { - String filePath = data.getData().getPath(); - mFileManager.installPlugin("file://" + filePath, true); + mFileManager.installPlugin(data.getData(), true); return; } break; From 4d5a67053a29597d8a52a4406e3720d9ac4cee58 Mon Sep 17 00:00:00 2001 From: Philipp Schaefer Date: Thu, 13 Feb 2014 00:32:35 +0100 Subject: [PATCH 18/58] modified installation msg --- mobile/res/values/strings.xml | 4 +++- mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mobile/res/values/strings.xml b/mobile/res/values/strings.xml index 38de7c91..f22db356 100644 --- a/mobile/res/values/strings.xml +++ b/mobile/res/values/strings.xml @@ -179,7 +179,9 @@ Install external plugin? - IITCm was requested to install the following plugin:\n\n%1$s\n\nDo you want to proceed? +
%1$s

+ Be careful: Javascript from external sources may contain harmful code (spyware etc.)!

+ Are you sure you want to proceed?]]>
diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java b/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java index 62e2509d..0872c2d1 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java @@ -11,6 +11,7 @@ import android.content.res.AssetManager; import android.net.Uri; import android.os.Environment; import android.preference.PreferenceManager; +import android.text.Html; import android.util.Base64; import android.util.Base64OutputStream; import android.webkit.WebResourceResponse; @@ -256,7 +257,7 @@ public class IITC_FileManager { String text = mActivity.getString(R.string.install_dialog_msg); text = String.format(text, uri); alertDialogBuilder - .setMessage(text) + .setMessage(Html.fromHtml(text)) .setCancelable(true) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override From 8d8103dfc1b8d4f43101f23caae953668d6d71ca Mon Sep 17 00:00:00 2001 From: Philipp Schaefer Date: Thu, 13 Feb 2014 01:17:53 +0100 Subject: [PATCH 19/58] updated external plugin faq --- website/page/faq.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/page/faq.php b/website/page/faq.php index 3c4f1e93..6ab7ab79 100644 --- a/website/page/faq.php +++ b/website/page/faq.php @@ -168,7 +168,7 @@ Yes it is! Note:
  • The filename has to end with *.user.js.
  • -
  • You need a file explorer app installed to add external plugins
  • +
  • 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...
END ), From 4d17e720ea4ae6ce99534ad074fa180a09ecdc0c Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Thu, 13 Feb 2014 20:23:12 +0000 Subject: [PATCH 20/58] add note to artifact table when there are zero rows --- code/artifact.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/artifact.js b/code/artifact.js index 736587bf..f816c3c0 100644 --- a/code/artifact.js +++ b/code/artifact.js @@ -283,6 +283,11 @@ window.artifact.showArtifactList = function() { } }); + // check for no rows, and add a note to the table instead + if (tableRows.length == 0) { + html += 'No portals at this time'; + } + // sort the rows tableRows.sort(function(a,b) { return a[0]-b[0]; @@ -291,6 +296,7 @@ window.artifact.showArtifactList = function() { // and add them to the table html += tableRows.map(function(a){return a[1];}).join(''); + html += ''; }); From 9516994fa4144e41f8dab56e2aee8ae78bb69a57 Mon Sep 17 00:00:00 2001 From: fkloft Date: Thu, 13 Feb 2014 17:49:09 +0100 Subject: [PATCH 21/58] some code formatting, adding final modifiers --- .../cradle/iitc_mobile/IITC_FileManager.java | 44 ++++++++----------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java b/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java index 0872c2d1..f841d740 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java @@ -3,7 +3,6 @@ package com.cradle.iitc_mobile; import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; -import android.content.ContentResolver; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; @@ -248,68 +247,61 @@ public class IITC_FileManager { public void installPlugin(final Uri uri, final boolean invalidateHeaders) { if (uri != null) { - AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mActivity); - - // set title - alertDialogBuilder.setTitle(mActivity.getString(R.string.install_dialog_top)); - - // set dialog message String text = mActivity.getString(R.string.install_dialog_msg); text = String.format(text, uri); - alertDialogBuilder + + // 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(DialogInterface dialog, int which) { + public void onClick(final DialogInterface dialog, final int which) { copyPlugin(uri, invalidateHeaders); } }) .setNegativeButton("No", new DialogInterface.OnClickListener() { @Override - public void onClick(DialogInterface dialog, int which) { + public void onClick(final DialogInterface dialog, final int which) { dialog.cancel(); } - }); - - // create alert dialog - AlertDialog alertDialog = alertDialogBuilder.create(); - - // show it - alertDialog.show(); + }) + .create() + .show(); } } private void copyPlugin(final Uri uri, final boolean invalidateHeaders) { - Thread thread = new Thread(new Runnable() { + final Thread thread = new Thread(new Runnable() { @Override public void run() { try { final String url = uri.toString(); - String fileName = url.substring( url.lastIndexOf('/')+1, url.length() ); // we need 2 stream 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 InputStream is, isCopy; if (uri.getScheme().contains("http")) { - URLConnection conn = new URL(url).openConnection(); - URLConnection connCopy = new URL(url).openConnection(); + final URLConnection conn = new URL(url).openConnection(); + final URLConnection connCopy = new URL(url).openConnection(); is = conn.getInputStream(); isCopy = connCopy.getInputStream(); } else { is = mActivity.getContentResolver().openInputStream(uri); isCopy = mActivity.getContentResolver().openInputStream(uri); } - fileName = getScriptInfo(isCopy).get("id") + ".user.js"; + final String 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 - File outFile = new File(pluginsDirectory + "/" + fileName); - OutputStream os = new FileOutputStream(outFile); + final File outFile = new File(pluginsDirectory, fileName); + final OutputStream os = new FileOutputStream(outFile); IITC_FileManager.copyStream(is, os, true); - } catch (IOException e) { + } catch (final IOException e) { Log.w(e); } } @@ -319,7 +311,7 @@ public class IITC_FileManager { try { thread.join(); ((IITC_PluginPreferenceActivity) mActivity).invalidateHeaders(); - } catch (InterruptedException e) { + } catch (final InterruptedException e) { Log.w(e); } } From 1c637cce109d598943e9e7396991ae12a37cf52a Mon Sep 17 00:00:00 2001 From: fkloft Date: Thu, 13 Feb 2014 21:43:25 +0100 Subject: [PATCH 22/58] [mobile] small improvement to debugging: should work with RemoteKeyboard send action --- mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java b/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java index eac3b86a..f3d1857a 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java @@ -104,7 +104,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); From 51a835ce3bc525927924a004f7f0551ca59b6b54 Mon Sep 17 00:00:00 2001 From: fkloft Date: Thu, 13 Feb 2014 21:46:03 +0100 Subject: [PATCH 23/58] [mobile] Provide a method to save a file --- .../cradle/iitc_mobile/IITC_FileManager.java | 70 ++++++++++++++++--- .../cradle/iitc_mobile/IITC_JSInterface.java | 2 +- .../iitc_mobile/IITC_JSInterfaceKitkat.java | 17 +++++ .../com/cradle/iitc_mobile/IITC_WebView.java | 8 ++- 4 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 mobile/src/com/cradle/iitc_mobile/IITC_JSInterfaceKitkat.java diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java b/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java index f841d740..14f53495 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java @@ -1,5 +1,6 @@ package com.cradle.iitc_mobile; +import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; @@ -9,6 +10,7 @@ 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; @@ -337,12 +339,12 @@ 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 { - final IITC_Mobile iitc = (IITC_Mobile) mActivity; iitc.startActivityForResult(Intent.createChooser(target, "Choose file"), this); } catch (final ActivityNotFoundException e) { Toast.makeText(mActivity, "No activity to select a file found." + @@ -367,18 +369,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('', ''); - 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); @@ -396,4 +398,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); + } + } + } } diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java index 89a3bf66..ae3babfd 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java @@ -15,7 +15,7 @@ import com.cradle.iitc_mobile.share.ShareActivity; // 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; diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_JSInterfaceKitkat.java b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterfaceKitkat.java new file mode 100644 index 00000000..4e8256b6 --- /dev/null +++ b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterfaceKitkat.java @@ -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 + public void saveFile(final String filename, final String type, final String content) { + + mIitc.getFileManager().new FileSaveRequest(filename, type, content); + } +} diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_WebView.java b/mobile/src/com/cradle/iitc_mobile/IITC_WebView.java index 9963e432..c2598738 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_WebView.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_WebView.java @@ -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(); From fc571ecf7ec963415ccd7fb34253e104ab793856 Mon Sep 17 00:00:00 2001 From: fkloft Date: Thu, 13 Feb 2014 22:31:55 +0100 Subject: [PATCH 24/58] [bookmarks-by-zaso] show only available actions; allow to save/load bookmarks to/from storage on Android --- plugins/bookmarks-by-zaso.user.js | 53 +++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/plugins/bookmarks-by-zaso.user.js b/plugins/bookmarks-by-zaso.user.js index f880efba..4340b374 100644 --- a/plugins/bookmarks-by-zaso.user.js +++ b/plugins/bookmarks-by-zaso.user.js @@ -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,10 +540,17 @@ } } + window.plugin.bookmarks.optExport = function() { + if(typeof android !== 'undefined' && android && android.saveFile) { // saveFile only exists on Kitkat+! + 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"}); @@ -561,6 +563,23 @@ } } + 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('Import failed '); + } + }); + } + window.plugin.bookmarks.optReset = function() { var promptAction = confirm('All bookmarks will be deleted. Are you sure?', ''); if(promptAction) { @@ -919,13 +938,21 @@ plugin.bookmarks.htmlStar = ''; plugin.bookmarks.htmlCalldrawBox = 'Auto draw'; plugin.bookmarks.htmlCallSetBox = 'Bookmarks Opt'; - plugin.bookmarks.htmlSetbox = ''; + + var actions = ''; + actions += 'Reset bookmarks'; + actions += 'Copy bookmarks'; + actions += 'Paste bookmarks'; + + if(plugin.bookmarks.isAndroid()) { + actions += 'Import bookmarks'; + if(typeof android !== 'undefined' && android && android.saveFile) // saveFile only exists on Kitkat+! + actions += 'Export bookmarks'; + } else { + actions += 'Save box position'; + actions += 'Reset box position'; + } + plugin.bookmarks.htmlSetbox = '
' + actions + '
'; } /***************************************************************************************************************************************************************/ From e1443e3648a3195c754cd8fe9d980e2ccc4ce6d4 Mon Sep 17 00:00:00 2001 From: fkloft Date: Thu, 13 Feb 2014 22:47:52 +0100 Subject: [PATCH 25/58] [draw-tools] fix error on mobile (Leaflet is not available until setup() is called) --- plugins/draw-tools.user.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/draw-tools.user.js b/plugins/draw-tools.user.js index d07c4ebe..702a6181 100644 --- a/plugins/draw-tools.user.js +++ b/plugins/draw-tools.user.js @@ -55,7 +55,6 @@ window.plugin.drawTools.getMarkerIcon = function(color) { window.plugin.drawTools.currentColor = '#a24ac3'; window.plugin.drawTools.markerTemplate = '@@INCLUDESTRING:images/marker-icon.svg.template@@'; -window.plugin.drawTools.currentMarker = window.plugin.drawTools.getMarkerIcon(window.plugin.drawTools.currentColor); window.plugin.drawTools.setOptions = function() { @@ -360,6 +359,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 From c0b1ad987e8c27c24d94ea8b5165f03d2b79524c Mon Sep 17 00:00:00 2001 From: fkloft Date: Thu, 13 Feb 2014 22:48:42 +0100 Subject: [PATCH 26/58] [draw-tools] implement save to/load from storage on Android --- plugins/draw-tools.user.js | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/plugins/draw-tools.user.js b/plugins/draw-tools.user.js index 702a6181..64a1aa1c 100644 --- a/plugins/draw-tools.user.js +++ b/plugins/draw-tools.user.js @@ -281,8 +281,12 @@ window.plugin.drawTools.manualOpt = function() { //TODO: add line style choosers: thickness, maybe dash styles? + '' + '
' - + 'Copy/Export Drawn Items' - + 'Paste/Import Drawn Items' + + 'Copy Drawn Items' + + 'Paste Drawn Items' + + (window.requestFile != undefined + ? 'Import Drawn Items' : '') + + ((typeof android !== 'undefined' && android && android.saveFile) // saveFile only exists on Kitkat+! + ? 'Export Drawn Items' : '') + 'Reset Drawn Items' + '
'; @@ -327,6 +331,12 @@ window.plugin.drawTools.optCopy = function() { } } +window.plugin.drawTools.optExport = function() { + if(typeof android !== 'undefined' && android && android.saveFile) { // saveFile only exists on Kitkat+! + 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 !== '') { @@ -343,10 +353,28 @@ window.plugin.drawTools.optPaste = function() { console.warn('DRAWTOOLS: failed to import data: '+e); window.plugin.drawTools.optAlert('Import failed'); } - } } +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('Import failed'); + } + }); +} + window.plugin.drawTools.optReset = function() { var promptAction = confirm('All drawn items will be deleted. Are you sure?', ''); if(promptAction) { From 2a3deb99b5ffc3823897e606b4f2b1b02b185b0f Mon Sep 17 00:00:00 2001 From: Philipp Schaefer Date: Thu, 13 Feb 2014 23:27:17 +0100 Subject: [PATCH 27/58] don't open inputstream twice on http streams...we should get a valid filename here --- .../com/cradle/iitc_mobile/IITC_FileManager.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java b/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java index 14f53495..2c8a32cf 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java @@ -283,18 +283,17 @@ public class IITC_FileManager { // we need 2 stream 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 - InputStream is, isCopy; + InputStream is; + String fileName; if (uri.getScheme().contains("http")) { - final URLConnection conn = new URL(url).openConnection(); - final URLConnection connCopy = new URL(url).openConnection(); + URLConnection conn = new URL(url).openConnection(); is = conn.getInputStream(); - isCopy = connCopy.getInputStream(); + fileName = uri.getLastPathSegment(); } else { is = mActivity.getContentResolver().openInputStream(uri); - isCopy = mActivity.getContentResolver().openInputStream(uri); + final InputStream isCopy = mActivity.getContentResolver().openInputStream(uri); + fileName = getScriptInfo(isCopy).get("id") + ".user.js"; } - final String 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(); From 903fa4f904a441f0d3582a36bb4443c391bae2e4 Mon Sep 17 00:00:00 2001 From: Philipp Schaefer Date: Thu, 13 Feb 2014 23:59:44 +0100 Subject: [PATCH 28/58] added support for draw tools file export on Android < 4.4 --- .../cradle/iitc_mobile/IITC_JSInterface.java | 20 +++++++++++++++++++ .../iitc_mobile/IITC_JSInterfaceKitkat.java | 1 + plugins/draw-tools.user.js | 4 ++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java index ae3babfd..1ea70ca3 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java @@ -6,12 +6,18 @@ 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.ByteArrayInputStream; +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 @@ -232,4 +238,18 @@ 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/drawn_items/" + filename); + outFile.getParentFile().mkdirs(); + final FileOutputStream outStream = new FileOutputStream(outFile); + IITC_FileManager.copyStream(new ByteArrayInputStream(content.getBytes("UTF-8")), outStream, true); + Toast.makeText(mIitc, "Drawn items exported to " + outFile.getPath(), Toast.LENGTH_SHORT).show(); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_JSInterfaceKitkat.java b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterfaceKitkat.java index 4e8256b6..6adf1a84 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_JSInterfaceKitkat.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterfaceKitkat.java @@ -10,6 +10,7 @@ public class IITC_JSInterfaceKitkat extends IITC_JSInterface { } @JavascriptInterface + @Override public void saveFile(final String filename, final String type, final String content) { mIitc.getFileManager().new FileSaveRequest(filename, type, content); diff --git a/plugins/draw-tools.user.js b/plugins/draw-tools.user.js index 64a1aa1c..865a71e8 100644 --- a/plugins/draw-tools.user.js +++ b/plugins/draw-tools.user.js @@ -285,7 +285,7 @@ window.plugin.drawTools.manualOpt = function() { + 'Paste Drawn Items' + (window.requestFile != undefined ? 'Import Drawn Items' : '') - + ((typeof android !== 'undefined' && android && android.saveFile) // saveFile only exists on Kitkat+! + + ((typeof android !== 'undefined' && android && android.saveFile) ? 'Export Drawn Items' : '') + 'Reset Drawn Items' + ''; @@ -332,7 +332,7 @@ window.plugin.drawTools.optCopy = function() { } window.plugin.drawTools.optExport = function() { - if(typeof android !== 'undefined' && android && android.saveFile) { // saveFile only exists on Kitkat+! + if(typeof android !== 'undefined' && android && android.saveFile) { android.saveFile('IITC-drawn-items.json', 'application/json', localStorage['plugin-draw-tools-layer']); } } From 0ba9098d802f51a872da0530892a7e8280c0a0bc Mon Sep 17 00:00:00 2001 From: fkloft Date: Fri, 14 Feb 2014 00:20:41 +0100 Subject: [PATCH 29/58] [mobile] more general save function --- .../src/com/cradle/iitc_mobile/IITC_JSInterface.java | 11 ++++++----- .../cradle/iitc_mobile/IITC_JSInterfaceKitkat.java | 1 - 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java index 1ea70ca3..d489ca6e 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterface.java @@ -13,7 +13,6 @@ import android.widget.Toast; import com.cradle.iitc_mobile.IITC_NavigationHelper.Pane; import com.cradle.iitc_mobile.share.ShareActivity; -import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -243,12 +242,14 @@ public class IITC_JSInterface { public void saveFile(final String filename, final String type, final String content) { try { final File outFile = new File(Environment.getExternalStorageDirectory().getPath() + - "/IITC_Mobile/drawn_items/" + filename); + "/IITC_Mobile/export/" + filename); outFile.getParentFile().mkdirs(); + final FileOutputStream outStream = new FileOutputStream(outFile); - IITC_FileManager.copyStream(new ByteArrayInputStream(content.getBytes("UTF-8")), outStream, true); - Toast.makeText(mIitc, "Drawn items exported to " + outFile.getPath(), Toast.LENGTH_SHORT).show(); - } catch (IOException e) { + 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(); } } diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_JSInterfaceKitkat.java b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterfaceKitkat.java index 6adf1a84..6c686734 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_JSInterfaceKitkat.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_JSInterfaceKitkat.java @@ -12,7 +12,6 @@ public class IITC_JSInterfaceKitkat extends IITC_JSInterface { @JavascriptInterface @Override public void saveFile(final String filename, final String type, final String content) { - mIitc.getFileManager().new FileSaveRequest(filename, type, content); } } From b8eb21bc0a7c6c1b029ff293fea13f1dcb1cf8d0 Mon Sep 17 00:00:00 2001 From: fkloft Date: Fri, 14 Feb 2014 00:24:02 +0100 Subject: [PATCH 30/58] [bookmarks-by-zaso] remove old comments (saveFile was backported) --- plugins/bookmarks-by-zaso.user.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/bookmarks-by-zaso.user.js b/plugins/bookmarks-by-zaso.user.js index 4340b374..f50e578c 100644 --- a/plugins/bookmarks-by-zaso.user.js +++ b/plugins/bookmarks-by-zaso.user.js @@ -541,7 +541,7 @@ } window.plugin.bookmarks.optExport = function() { - if(typeof android !== 'undefined' && android && android.saveFile) { // saveFile only exists on Kitkat+! + if(typeof android !== 'undefined' && android && android.saveFile) { android.saveFile("IITC-bookmarks.json", "application/json", localStorage[window.plugin.bookmarks.KEY_STORAGE]); } } @@ -946,8 +946,7 @@ if(plugin.bookmarks.isAndroid()) { actions += 'Import bookmarks'; - if(typeof android !== 'undefined' && android && android.saveFile) // saveFile only exists on Kitkat+! - actions += 'Export bookmarks'; + actions += 'Export bookmarks'; } else { actions += 'Save box position'; actions += 'Reset box position'; From 9b5adb9ab9d987624e06b9ef8b4a15086376925b Mon Sep 17 00:00:00 2001 From: Philipp Schaefer Date: Fri, 14 Feb 2014 11:32:26 +0100 Subject: [PATCH 31/58] don't trigger on all text mimeTypes - this breaks compatibility with file explorers throwing a wrong intent (like ES explorer)...but this is not a problem IITCm has to workaround. --- mobile/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/AndroidManifest.xml b/mobile/AndroidManifest.xml index f5a18f1b..8bdb3293 100644 --- a/mobile/AndroidManifest.xml +++ b/mobile/AndroidManifest.xml @@ -81,7 +81,7 @@ - + From c18cdc4d39964705b7cfbdf8f42cfa646dc6776d Mon Sep 17 00:00:00 2001 From: Philipp Schaefer Date: Mon, 17 Feb 2014 13:40:37 +0100 Subject: [PATCH 32/58] support gmaps v7.6.1+ labeled marker geo intent --- .../iitc_mobile/share/IntentListView.java | 18 ++++++++++++++++++ .../iitc_mobile/share/ShareActivity.java | 8 ++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/mobile/src/com/cradle/iitc_mobile/share/IntentListView.java b/mobile/src/com/cradle/iitc_mobile/share/IntentListView.java index a0af6dcc..629132f1 100644 --- a/mobile/src/com/cradle/iitc_mobile/share/IntentListView.java +++ b/mobile/src/com/cradle/iitc_mobile/share/IntentListView.java @@ -86,6 +86,14 @@ public class IntentListView extends ListView { } } + private static final HashSet GEOLABEL_WHITELIST = new HashSet(); + + static { + if (GEOLABEL_WHITELIST.isEmpty()) { + GEOLABEL_WHITELIST.add("com.google.android.apps.maps"); + } + } + private HashMap mActivities = new HashMap(); private IntentAdapter mAdapter; @@ -164,6 +172,16 @@ public class IntentListView extends ListView { ResolveInfo info = activityList.get(i); ActivityInfo activity = info.activityInfo; + // remove all apps that don't support a geo intent like geo:0,0?q=lat,lng(label) + // they'll receive a default geo intent like geo:lat,lng + if (intent.getData() != null && + "geo:0,0?q=".regionMatches(false, 0, intent.getData().toString(), 0, 10) && + !GEOLABEL_WHITELIST.contains(activity.packageName)) { + activityList.remove(i); + i--; + continue; + } + // 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); diff --git a/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java b/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java index ffae8ae7..c6bded02 100644 --- a/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java +++ b/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java @@ -66,13 +66,17 @@ public class ShareActivity extends FragmentActivity implements ActionBar.TabList 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 + // gmaps supports labeled markers via geo intent...most other navigation apps don't + // so provide two different geo intents and filter them in IntentListView ArrayList intents = new ArrayList(); String gMapsUri; try { + /* + * doesn't work anymore since gmaps v7.6.1 gMapsUri = "http://maps.google.com/?q=loc:" + mLl + "%20(" + URLEncoder.encode(mTitle, "UTF-8") + ")&z=" + mZoom; + */ + gMapsUri = "geo:0,0?q=" + mLl + "%20(" + URLEncoder.encode(mTitle, "UTF-8") + ")"; } catch (UnsupportedEncodingException e) { gMapsUri = "http://maps.google.com/?ll=" + mLl + "&z=" + mZoom; Log.w(e); From 045628b00738090156a73cf4a0fd460d2fbb2dbb Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Tue, 18 Feb 2014 08:14:27 +0000 Subject: [PATCH 33/58] when clustering portals and only displaying some, sort first by level (highest first) then guid - so when some portals are removed from screen it's the lower level ones first --- code/map_data_render.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/code/map_data_render.js b/code/map_data_render.js index 198c376b..4e7e969b 100644 --- a/code/map_data_render.js +++ b/code/map_data_render.js @@ -440,14 +440,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 (kakb) return 1; + else return 0; + }); for (var i=0; i Date: Tue, 18 Feb 2014 15:18:39 +0100 Subject: [PATCH 34/58] Remove warning, add final modifiers --- .../iitc_mobile/fragments/MainSettings.java | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/mobile/src/com/cradle/iitc_mobile/fragments/MainSettings.java b/mobile/src/com/cradle/iitc_mobile/fragments/MainSettings.java index 79a46ef6..f12e5267 100644 --- a/mobile/src/com/cradle/iitc_mobile/fragments/MainSettings.java +++ b/mobile/src/com/cradle/iitc_mobile/fragments/MainSettings.java @@ -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 From 6fadfa9b390e9b58010715e669ddb0bc675c154e Mon Sep 17 00:00:00 2001 From: fkloft Date: Tue, 18 Feb 2014 17:46:02 +0100 Subject: [PATCH 35/58] Major refactoring of ShareActivity. Google Maps now has labeled pins --- .../iitc_mobile/share/IntentComparator.java | 237 +++++++++--------- .../iitc_mobile/share/IntentFragment.java | 17 +- .../iitc_mobile/share/IntentGenerator.java | 143 +++++++++++ .../iitc_mobile/share/IntentListView.java | 204 ++++----------- .../iitc_mobile/share/ShareActivity.java | 91 +++---- 5 files changed, 344 insertions(+), 348 deletions(-) create mode 100644 mobile/src/com/cradle/iitc_mobile/share/IntentGenerator.java diff --git a/mobile/src/com/cradle/iitc_mobile/share/IntentComparator.java b/mobile/src/com/cradle/iitc_mobile/share/IntentComparator.java index 6ae1d0a8..8e0dead9 100644 --- a/mobile/src/com/cradle/iitc_mobile/share/IntentComparator.java +++ b/mobile/src/com/cradle/iitc_mobile/share/IntentComparator.java @@ -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 { +public class IntentComparator implements Comparator { + private static final String INTENT_MAP_FILE = "share_intent_map"; + + private final ShareActivity mActivity; + + private HashMap mIntentMap = new HashMap(); + + 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) 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 { 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 { : name); } } - - private static final String INTENT_MAP_FILE = "share_intent_map"; - - private ShareActivity mActivity; - private HashMap mIntentMap = new HashMap(); - 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) 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); - } } \ No newline at end of file diff --git a/mobile/src/com/cradle/iitc_mobile/share/IntentFragment.java b/mobile/src/com/cradle/iitc_mobile/share/IntentFragment.java index ef3aaaeb..9b185630 100644 --- a/mobile/src/com/cradle/iitc_mobile/share/IntentFragment.java +++ b/mobile/src/com/cradle/iitc_mobile/share/IntentFragment.java @@ -27,8 +27,8 @@ 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()); @@ -43,23 +43,22 @@ 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)); + public void onItemClick(final AdapterView parent, final View view, final int position, final long id) { + final Intent intent = mListView.getItem(position); + ((ShareActivity) getActivity()).getIntentComparator().trackIntentSelection(intent); - Intent intent = mListView.getTargetIntent(position); startActivity(intent); - getActivity().finish(); } @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) { } } \ No newline at end of file diff --git a/mobile/src/com/cradle/iitc_mobile/share/IntentGenerator.java b/mobile/src/com/cradle/iitc_mobile/share/IntentGenerator.java new file mode 100644 index 00000000..6d6d4694 --- /dev/null +++ b/mobile/src/com/cradle/iitc_mobile/share/IntentGenerator.java @@ -0,0 +1,143 @@ +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 KNOWN_COPY_HANDLERS = new HashSet(); + + 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 targets) { + for (final Intent intent : targets) { + for (final ComponentName handler : KNOWN_COPY_HANDLERS) { + if (handler.equals(intent.getComponent())) return true; + } + } + return false; + } + + private ArrayList resolveTargets(final Intent intent) { + final String packageName = mContext.getPackageName(); + final List activityList = mPackageManager.queryIntentActivities(intent, 0); + final ResolveInfo defaultTarget = mPackageManager.resolveActivity(intent, 0); + + final ArrayList list = new ArrayList(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 ArrayList 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 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 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 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 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; + } +} diff --git a/mobile/src/com/cradle/iitc_mobile/share/IntentListView.java b/mobile/src/com/cradle/iitc_mobile/share/IntentListView.java index a0af6dcc..c6e175fc 100644 --- a/mobile/src/com/cradle/iitc_mobile/share/IntentListView.java +++ b/mobile/src/com/cradle/iitc_mobile/share/IntentListView.java @@ -1,16 +1,13 @@ 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.content.pm.PackageManager.NameNotFoundException; 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; @@ -18,90 +15,27 @@ import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; +import com.cradle.iitc_mobile.Log; 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 { - 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 { - - // 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 KNOWN_COPY_HANDLERS = new HashSet(); - - 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 mActivities = new HashMap(); - private IntentAdapter mAdapter; private PackageManager mPackageManager; - public IntentListView(Context context) { + public IntentListView(final Context context) { super(context); init(); } - public IntentListView(Context context, AttributeSet attrs) { + public IntentListView(final Context context, final AttributeSet attrs) { super(context, attrs); init(); } - public IntentListView(Context context, AttributeSet attrs, int defStyle) { + public IntentListView(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); init(); } @@ -112,98 +46,54 @@ public class IntentListView extends ListView { setAdapter(mAdapter); } - public ResolveInfo getItem(int position) { + public Intent getItem(final int position) { return mAdapter.getItem(position); } - public Intent getTargetIntent(int position) { - ActivityInfo activity = mAdapter.getItem(position).activityInfo; + public void setIntents(final List intents) { + Collections.sort(intents, ((ShareActivity) getContext()).getIntentComparator()); - 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 intentList = new ArrayList(1); - intentList.add(intent); - setIntents(intentList); - } - - public void setIntents(ArrayList intents) { mAdapter.setNotifyOnChange(false); mAdapter.clear(); - - String packageName = getContext().getPackageName(); - - ArrayList allActivities = new ArrayList(); - - for (Intent intent : intents) { - List 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.addAll(intents); mAdapter.notifyDataSetChanged(); } + + private class IntentAdapter extends ArrayAdapter { + + // 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(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) 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; + } + } } diff --git a/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java b/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java index ffae8ae7..e35fbb26 100644 --- a/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java +++ b/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java @@ -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 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 intents, int label, int icon) { - IntentFragment fragment = new IntentFragment(); - Bundle args = new Bundle(); + private void addTab(final ArrayList intents, final int label, final int icon) { + final IntentFragment fragment = new IntentFragment(); + 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 intents = new ArrayList(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 intents = new ArrayList(); - 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()); 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 IntentFragment fragment = (IntentFragment) 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) { @@ -172,7 +141,7 @@ public class ShareActivity extends FragmentActivity implements ActionBar.TabList } @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 +151,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) { } } From 6d9ff2d539824d50e12c97eb660844c90f79be6a Mon Sep 17 00:00:00 2001 From: fkloft Date: Tue, 18 Feb 2014 17:55:26 +0100 Subject: [PATCH 36/58] Extract IntentAdapter from IntentListView. Delete IntentListView (only a small wrapper was left, remainders could be moved to IntentAdapter --- .../iitc_mobile/share/IntentAdapter.java | 68 +++++++++++++ .../iitc_mobile/share/IntentFragment.java | 13 ++- .../iitc_mobile/share/IntentListView.java | 99 ------------------- 3 files changed, 77 insertions(+), 103 deletions(-) create mode 100644 mobile/src/com/cradle/iitc_mobile/share/IntentAdapter.java delete mode 100644 mobile/src/com/cradle/iitc_mobile/share/IntentListView.java diff --git a/mobile/src/com/cradle/iitc_mobile/share/IntentAdapter.java b/mobile/src/com/cradle/iitc_mobile/share/IntentAdapter.java new file mode 100644 index 00000000..3c3cde4a --- /dev/null +++ b/mobile/src/com/cradle/iitc_mobile/share/IntentAdapter.java @@ -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 { + 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 intents) { + Collections.sort(intents, ((ShareActivity) getContext()).getIntentComparator()); + + setNotifyOnChange(false); + clear(); + addAll(intents); + notifyDataSetChanged(); + } +} \ No newline at end of file diff --git a/mobile/src/com/cradle/iitc_mobile/share/IntentFragment.java b/mobile/src/com/cradle/iitc_mobile/share/IntentFragment.java index 9b185630..11301938 100644 --- a/mobile/src/com/cradle/iitc_mobile/share/IntentFragment.java +++ b/mobile/src/com/cradle/iitc_mobile/share/IntentFragment.java @@ -10,13 +10,15 @@ 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 { private ArrayList mIntents; - private IntentListView mListView; + private IntentAdapter mAdapter; private int mScrollIndex, mScrollTop; + private ListView mListView; public int getIcon() { return getArguments().getInt("icon"); @@ -31,8 +33,11 @@ public class IntentFragment extends Fragment implements OnScrollListener, OnItem 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()); if (mScrollIndex != -1 && mScrollTop != -1) { mListView.setSelectionFromTop(mScrollIndex, mScrollTop); } @@ -44,7 +49,7 @@ public class IntentFragment extends Fragment implements OnScrollListener, OnItem @Override public void onItemClick(final AdapterView parent, final View view, final int position, final long id) { - final Intent intent = mListView.getItem(position); + final Intent intent = mAdapter.getItem(position); ((ShareActivity) getActivity()).getIntentComparator().trackIntentSelection(intent); startActivity(intent); diff --git a/mobile/src/com/cradle/iitc_mobile/share/IntentListView.java b/mobile/src/com/cradle/iitc_mobile/share/IntentListView.java deleted file mode 100644 index c6e175fc..00000000 --- a/mobile/src/com/cradle/iitc_mobile/share/IntentListView.java +++ /dev/null @@ -1,99 +0,0 @@ -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.AttributeSet; -import android.util.DisplayMetrics; -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.Log; -import com.cradle.iitc_mobile.R; - -import java.util.Collections; -import java.util.List; - -public class IntentListView extends ListView { - private IntentAdapter mAdapter; - private PackageManager mPackageManager; - - public IntentListView(final Context context) { - super(context); - init(); - } - - public IntentListView(final Context context, final AttributeSet attrs) { - super(context, attrs); - init(); - } - - public IntentListView(final Context context, final AttributeSet attrs, final int defStyle) { - super(context, attrs, defStyle); - init(); - } - - private void init() { - mPackageManager = getContext().getPackageManager(); - mAdapter = new IntentAdapter(); - setAdapter(mAdapter); - } - - public Intent getItem(final int position) { - return mAdapter.getItem(position); - } - - public void setIntents(final List intents) { - Collections.sort(intents, ((ShareActivity) getContext()).getIntentComparator()); - - mAdapter.setNotifyOnChange(false); - mAdapter.clear(); - mAdapter.addAll(intents); - mAdapter.notifyDataSetChanged(); - } - - private class IntentAdapter extends ArrayAdapter { - - // 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(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) 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; - } - } -} From 09b0fc7297317d86dd658a3c820667d0131c65ad Mon Sep 17 00:00:00 2001 From: fkloft Date: Tue, 18 Feb 2014 18:04:48 +0100 Subject: [PATCH 37/58] Renamed classes: IntentFragmentAdapter -> FragmentAdapter, IntentFragment -> IntentListFragment --- ...ntFragmentAdapter.java => FragmentAdapter.java} | 14 +++++++------- ...IntentFragment.java => IntentListFragment.java} | 2 +- .../cradle/iitc_mobile/share/ShareActivity.java | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) rename mobile/src/com/cradle/iitc_mobile/share/{IntentFragmentAdapter.java => FragmentAdapter.java} (56%) rename mobile/src/com/cradle/iitc_mobile/share/{IntentFragment.java => IntentListFragment.java} (95%) diff --git a/mobile/src/com/cradle/iitc_mobile/share/IntentFragmentAdapter.java b/mobile/src/com/cradle/iitc_mobile/share/FragmentAdapter.java similarity index 56% rename from mobile/src/com/cradle/iitc_mobile/share/IntentFragmentAdapter.java rename to mobile/src/com/cradle/iitc_mobile/share/FragmentAdapter.java index fd803345..fa3dfbdf 100644 --- a/mobile/src/com/cradle/iitc_mobile/share/IntentFragmentAdapter.java +++ b/mobile/src/com/cradle/iitc_mobile/share/FragmentAdapter.java @@ -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 mTabs; +public class FragmentAdapter extends FragmentPagerAdapter { + private final List mTabs; - public IntentFragmentAdapter(FragmentManager fm) { + public FragmentAdapter(final FragmentManager fm) { super(fm); - mTabs = new ArrayList(); + mTabs = new ArrayList(); } - 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(); } } \ No newline at end of file diff --git a/mobile/src/com/cradle/iitc_mobile/share/IntentFragment.java b/mobile/src/com/cradle/iitc_mobile/share/IntentListFragment.java similarity index 95% rename from mobile/src/com/cradle/iitc_mobile/share/IntentFragment.java rename to mobile/src/com/cradle/iitc_mobile/share/IntentListFragment.java index 11301938..4258e219 100644 --- a/mobile/src/com/cradle/iitc_mobile/share/IntentFragment.java +++ b/mobile/src/com/cradle/iitc_mobile/share/IntentListFragment.java @@ -14,7 +14,7 @@ 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 ArrayList mIntents; private IntentAdapter mAdapter; private int mScrollIndex, mScrollTop; diff --git a/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java b/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java index e35fbb26..e62fbbf5 100644 --- a/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java +++ b/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java @@ -17,7 +17,7 @@ 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; @@ -27,7 +27,7 @@ public class ShareActivity extends FragmentActivity implements ActionBar.TabList private int mZoom; private void addTab(final ArrayList intents, final int label, final int icon) { - final IntentFragment fragment = new IntentFragment(); + final IntentListFragment fragment = new IntentListFragment(); final Bundle args = new Bundle(); args.putParcelableArrayList("intents", intents); args.putString("title", getString(label)); @@ -62,7 +62,7 @@ public class ShareActivity extends FragmentActivity implements ActionBar.TabList mComparator = new IntentComparator(this); mGenerator = new IntentGenerator(this); - mFragmentAdapter = new IntentFragmentAdapter(getSupportFragmentManager()); + mFragmentAdapter = new FragmentAdapter(getSupportFragmentManager()); final ActionBar actionBar = getActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); @@ -107,7 +107,7 @@ public class ShareActivity extends FragmentActivity implements ActionBar.TabList }); for (int i = 0; i < mFragmentAdapter.getCount(); i++) { - final IntentFragment fragment = (IntentFragment) mFragmentAdapter.getItem(i); + final IntentListFragment fragment = (IntentListFragment) mFragmentAdapter.getItem(i); actionBar.addTab(actionBar .newTab() From c9d0c8141affebe4931b4cfc8ded1bc7d53082b6 Mon Sep 17 00:00:00 2001 From: fkloft Date: Tue, 18 Feb 2014 20:35:44 +0100 Subject: [PATCH 38/58] Improved support for geo: intents --- .../com/cradle/iitc_mobile/IITC_Mobile.java | 89 +++++++++++++------ 1 file changed, 64 insertions(+), 25 deletions(-) diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java b/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java index f3d1857a..a3a488ef 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java @@ -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 mDialogStack = new Stack(); private String mPermalink = null; + private String mSearchTerm = null; // Used for custom back stack handling private final Stack mBackStack = new Stack(); @@ -258,20 +261,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 @@ -281,21 +284,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 @@ -447,8 +476,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 @@ -458,8 +486,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); @@ -475,6 +503,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); @@ -658,10 +687,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() { From 24cf79acc758553d560b0f713aa9fa7cb5ce5c14 Mon Sep 17 00:00:00 2001 From: fkloft Date: Tue, 18 Feb 2014 21:38:50 +0100 Subject: [PATCH 39/58] Add line that must have been deleted by accident --- mobile/src/com/cradle/iitc_mobile/share/IntentListFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/mobile/src/com/cradle/iitc_mobile/share/IntentListFragment.java b/mobile/src/com/cradle/iitc_mobile/share/IntentListFragment.java index 4258e219..b3397097 100644 --- a/mobile/src/com/cradle/iitc_mobile/share/IntentListFragment.java +++ b/mobile/src/com/cradle/iitc_mobile/share/IntentListFragment.java @@ -38,6 +38,7 @@ public class IntentListFragment extends Fragment implements OnScrollListener, On mAdapter.setIntents(mIntents); mListView = new ListView(getActivity()); + mListView.setAdapter(mAdapter); if (mScrollIndex != -1 && mScrollTop != -1) { mListView.setSelectionFromTop(mScrollIndex, mScrollTop); } From 6b85e5524c567eefa48e4d0dc518b10286196615 Mon Sep 17 00:00:00 2001 From: fkloft Date: Tue, 18 Feb 2014 21:41:58 +0100 Subject: [PATCH 40/58] Remove temporary extras before launching the intent --- .../com/cradle/iitc_mobile/share/IntentGenerator.java | 5 +++++ .../cradle/iitc_mobile/share/IntentListFragment.java | 10 +++------- .../com/cradle/iitc_mobile/share/ShareActivity.java | 7 +++++++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/mobile/src/com/cradle/iitc_mobile/share/IntentGenerator.java b/mobile/src/com/cradle/iitc_mobile/share/IntentGenerator.java index 6d6d4694..1e3af18d 100644 --- a/mobile/src/com/cradle/iitc_mobile/share/IntentGenerator.java +++ b/mobile/src/com/cradle/iitc_mobile/share/IntentGenerator.java @@ -92,6 +92,11 @@ public class IntentGenerator { return list; } + public void cleanup(final Intent intent) { + intent.removeExtra(EXTRA_FLAG_IS_DEFAULT); + intent.removeExtra(EXTRA_FLAG_TITLE); + } + public ArrayList 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); diff --git a/mobile/src/com/cradle/iitc_mobile/share/IntentListFragment.java b/mobile/src/com/cradle/iitc_mobile/share/IntentListFragment.java index b3397097..f2456dd1 100644 --- a/mobile/src/com/cradle/iitc_mobile/share/IntentListFragment.java +++ b/mobile/src/com/cradle/iitc_mobile/share/IntentListFragment.java @@ -15,10 +15,10 @@ import android.widget.ListView; import java.util.ArrayList; public class IntentListFragment extends Fragment implements OnScrollListener, OnItemClickListener { - private ArrayList mIntents; private IntentAdapter mAdapter; - private int mScrollIndex, mScrollTop; + private ArrayList mIntents; private ListView mListView; + private int mScrollIndex, mScrollTop; public int getIcon() { return getArguments().getInt("icon"); @@ -50,11 +50,7 @@ public class IntentListFragment extends Fragment implements OnScrollListener, On @Override public void onItemClick(final AdapterView parent, final View view, final int position, final long id) { - final Intent intent = mAdapter.getItem(position); - ((ShareActivity) getActivity()).getIntentComparator().trackIntentSelection(intent); - - startActivity(intent); - getActivity().finish(); + ((ShareActivity) getActivity()).launch(mAdapter.getItem(position)); } @Override diff --git a/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java b/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java index e62fbbf5..0578a7dc 100644 --- a/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java +++ b/mobile/src/com/cradle/iitc_mobile/share/ShareActivity.java @@ -140,6 +140,13 @@ 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(final MenuItem item) { switch (item.getItemId()) { From e9dccc0e8fdf49f2c539ed6dfbcc0d853d8f794d Mon Sep 17 00:00:00 2001 From: Philipp Schaefer Date: Wed, 19 Feb 2014 00:15:46 +0100 Subject: [PATCH 41/58] moved comments to correct place --- mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java | 6 +++--- mobile/src/com/cradle/iitc_mobile/IITC_WebViewClient.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java b/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java index 2c8a32cf..875348e6 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_FileManager.java @@ -280,9 +280,6 @@ public class IITC_FileManager { public void run() { try { final String url = uri.toString(); - // we need 2 stream 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 InputStream is; String fileName; if (uri.getScheme().contains("http")) { @@ -290,6 +287,9 @@ public class IITC_FileManager { 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"; diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_WebViewClient.java b/mobile/src/com/cradle/iitc_mobile/IITC_WebViewClient.java index be09bb43..d9e27563 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_WebViewClient.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_WebViewClient.java @@ -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); } /** From 5a53e88a41c3e82ef3c0f76c6b2e6c79c30e2fda Mon Sep 17 00:00:00 2001 From: Philipp Schaefer Date: Wed, 19 Feb 2014 00:49:16 +0100 Subject: [PATCH 42/58] intent MIME types may be null...generate empty string in that case --- mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java b/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java index a3a488ef..a643e7f0 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java @@ -198,9 +198,12 @@ 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(); + // intent MIME type may be null + final String type = intent.getType() == null ? "" : intent.getType(); + if (Intent.ACTION_VIEW.equals(action) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) { final Uri uri = intent.getData(); Log.d("intent received url: " + uri.toString()); @@ -234,7 +237,7 @@ public class IITC_Mobile extends Activity } } - if (uri.getPath().endsWith(".user.js") || intent.getType().contains("javascript")) { + if (uri.getPath().endsWith(".user.js") || type.contains("javascript")) { final Intent prefIntent = new Intent(this, IITC_PluginPreferenceActivity.class); prefIntent.setDataAndType(uri, intent.getType()); startActivity(prefIntent); From bda40fa331466ed7a31809120be08f690f06fafc Mon Sep 17 00:00:00 2001 From: Philipp Schaefer Date: Wed, 19 Feb 2014 01:19:40 +0100 Subject: [PATCH 43/58] bugfix: use correct string format for geo intent including zoom --- mobile/src/com/cradle/iitc_mobile/share/IntentGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/src/com/cradle/iitc_mobile/share/IntentGenerator.java b/mobile/src/com/cradle/iitc_mobile/share/IntentGenerator.java index 1e3af18d..35e4793a 100644 --- a/mobile/src/com/cradle/iitc_mobile/share/IntentGenerator.java +++ b/mobile/src/com/cradle/iitc_mobile/share/IntentGenerator.java @@ -106,7 +106,7 @@ public class IntentGenerator { public ArrayList 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))) + Uri.parse(String.format("geo:%s?z=%d", mLl, mZoom))) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); final ArrayList targets = resolveTargets(intent); From ca53c381aaf673d2ad906f21805d70509b96e2e1 Mon Sep 17 00:00:00 2001 From: Philipp Schaefer Date: Wed, 19 Feb 2014 01:21:13 +0100 Subject: [PATCH 44/58] intent.getAction uri path may be null...generate empty string in that case --- mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java b/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java index a643e7f0..2f95caa5 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_Mobile.java @@ -201,9 +201,6 @@ public class IITC_Mobile extends Activity // handles ingress intel url intents, search intents, geo intents and javascript file intents private void handleIntent(final Intent intent, final boolean onCreate) { final String action = intent.getAction(); - // intent MIME type may be null - final String type = intent.getType() == null ? "" : intent.getType(); - if (Intent.ACTION_VIEW.equals(action) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) { final Uri uri = intent.getData(); Log.d("intent received url: " + uri.toString()); @@ -237,7 +234,10 @@ public class IITC_Mobile extends Activity } } - if (uri.getPath().endsWith(".user.js") || type.contains("javascript")) { + // 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); From 8de70d8ec589e5b49eefb7ad8aa9cad767ca0ee9 Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Fri, 21 Feb 2014 04:46:42 +0000 Subject: [PATCH 45/58] now we no longer can reconstruct missing short links from the portal data, and the backend servers already cull short links from the data, there's no point in skipping display of short links --- code/map_data_render.js | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/code/map_data_render.js b/code/map_data_render.js index 4e7e969b..24d5bc2c 100644 --- a/code/map_data_render.js +++ b/code/map_data_render.js @@ -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(); @@ -544,24 +536,3 @@ window.Render.prototype.getLinkPixelLength = function(link) { } -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); - } - } -} From ea45d0f447af24115275234403202fe993e0d84c Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Fri, 21 Feb 2014 05:26:52 +0000 Subject: [PATCH 46/58] remove portal clustering code, that preventing all portals we get data for being drawn to the map this was an optimisation, as leaflet can be slow when drawing a lot. however, as the backend now limits the data returned to the browser this is less important and probably wastes more time than it saves for #774 --- code/map_data_render.js | 130 ++-------------------------------------- 1 file changed, 6 insertions(+), 124 deletions(-) diff --git a/code/map_data_render.js b/code/map_data_render.js index 24d5bc2c..38f1e7ef 100644 --- a/code/map_data_render.js +++ b/code/map_data_render.js @@ -5,14 +5,6 @@ window.Render = function() { - // when there are lots of portals close together, we only add some of them to the map - // the idea is to keep the impression of the dense set of portals, without rendering them all - 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 - - - this.entityVisibilityZoom = undefined; - this.portalMarkerScale = undefined; } @@ -401,138 +393,28 @@ window.Render.prototype.createLinkEntity = function(ent,faked) { window.Render.prototype.updateEntityVisibility = function() { - if (this.entityVisibilityZoom === undefined || this.entityVisibilityZoom != map.getZoom()) { - this.entityVisibilityZoom = map.getZoom(); - this.resetPortalClusters(); + if (this.portalMarkerScale === undefined || this.portalMarkerScale != portalMarkerScale()) { + this.portalMarkerScale = portalMarkerScale(); - if (this.portalMarkerScale === undefined || this.portalMarkerScale != portalMarkerScale()) { - this.portalMarkerScale = portalMarkerScale(); + console.log('Render: map zoom '+map.getZoom()+' changes portal scale to '+portalMarkerScale()+' - redrawing all portals'); - console.log('Render: map zoom '+map.getZoom()+' changes portal scale to '+portalMarkerScale()+' - redrawing all portals'); - - //NOTE: we're not calling this because it resets highlights - we're calling it as it resets the style (inc size) of all portal markers - resetHighlightedPortals(); - } + //NOTE: we're not calling this because it resets highlights - we're calling it as it resets the style (inc size) of all portal markers + resetHighlightedPortals(); } } -// portal clustering functionality - -window.Render.prototype.resetPortalClusters = function() { - - this.portalClusters = {}; - - // first, place the portals into the clusters - for (var pguid in window.portals) { - var p = window.portals[pguid]; - var cid = this.getPortalClusterID(p); - - if (!(cid in this.portalClusters)) this.portalClusters[cid] = []; - - this.portalClusters[cid].push(pguid); - } - - // 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(function(a,b) { - var ka = (8-portals[a].options.level)+a; - var kb = (8-portals[b].options.level)+b; - if (kakb) return 1; - else return 0; - }); - - for (var i=0; i= 0) { - this.portalClusters[cid].splice(index,1); - // FIXME? if this portal was in on the screen (in the first 10), and we still have 10+ portals, add the new 10to to the screen? - } - } -} - -window.Render.prototype.getPortalClusterID = function(portal) { - // project the lat/lng into absolute map pixels - var z = map.getZoom(); - - var point = map.project(portal.getLatLng(), z); - - var clusterpoint = point.divideBy(this.CLUSTER_SIZE).round(); - - return z+":"+clusterpoint.x+":"+clusterpoint.y; -} - - -// 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; } From cdf9c838012ea32cebf0f4197a239957a3b987df Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Fri, 21 Feb 2014 07:29:42 +0000 Subject: [PATCH 47/58] simplify map data request batching - doesn't need to be so complex now we never request more than 4 tiles per request --- code/map_data_request.js | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/code/map_data_request.js b/code/map_data_request.js index 4b37b308..c0ec93ab 100644 --- a/code/map_data_request.js +++ b/code/map_data_request.js @@ -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; @@ -363,26 +358,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 0) { -// console.log('-- new request: '+tiles.length+' tiles'); this.sendTileRequest(tiles); } } + } From f57d429b42ac8ee60ff27308a9ff37a72ec0d7c7 Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Fri, 21 Feb 2014 07:30:40 +0000 Subject: [PATCH 48/58] cache expiry time was 2 mins, but default intel site uses 3 minutes - mis-read their timer value --- code/data_cache.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/code/data_cache.js b/code/data_cache.js index e6caf3b9..4002a937 100644 --- a/code/data_cache.js +++ b/code/data_cache.js @@ -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) { From 6e1008e1076bd4768684079b46eba30125a50c45 Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Fri, 21 Feb 2014 16:34:40 +0000 Subject: [PATCH 49/58] revert ea45d0f447af24115275234403202fe993e0d84c - portal clustering still has some advantages note: window.Render.prototype.getLinkPixelLength not restored - should have been deleted as part of an earlier change --- code/map_data_render.js | 105 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 6 deletions(-) diff --git a/code/map_data_render.js b/code/map_data_render.js index 38f1e7ef..212c285d 100644 --- a/code/map_data_render.js +++ b/code/map_data_render.js @@ -5,6 +5,14 @@ window.Render = function() { + // when there are lots of portals close together, we only add some of them to the map + // the idea is to keep the impression of the dense set of portals, without rendering them all + 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 + + + this.entityVisibilityZoom = undefined; + this.portalMarkerScale = undefined; } @@ -393,28 +401,113 @@ window.Render.prototype.createLinkEntity = function(ent,faked) { window.Render.prototype.updateEntityVisibility = function() { + if (this.entityVisibilityZoom === undefined || this.entityVisibilityZoom != map.getZoom()) { + this.entityVisibilityZoom = map.getZoom(); - if (this.portalMarkerScale === undefined || this.portalMarkerScale != portalMarkerScale()) { - this.portalMarkerScale = portalMarkerScale(); + this.resetPortalClusters(); - console.log('Render: map zoom '+map.getZoom()+' changes portal scale to '+portalMarkerScale()+' - redrawing all portals'); + if (this.portalMarkerScale === undefined || this.portalMarkerScale != portalMarkerScale()) { + this.portalMarkerScale = portalMarkerScale(); - //NOTE: we're not calling this because it resets highlights - we're calling it as it resets the style (inc size) of all portal markers - resetHighlightedPortals(); + console.log('Render: map zoom '+map.getZoom()+' changes portal scale to '+portalMarkerScale()+' - redrawing all portals'); + + //NOTE: we're not calling this because it resets highlights - we're calling it as it resets the style (inc size) of all portal markers + resetHighlightedPortals(); + } } } +// portal clustering functionality + +window.Render.prototype.resetPortalClusters = function() { + + this.portalClusters = {}; + + // first, place the portals into the clusters + for (var pguid in window.portals) { + var p = window.portals[pguid]; + var cid = this.getPortalClusterID(p); + + if (!(cid in this.portalClusters)) this.portalClusters[cid] = []; + + this.portalClusters[cid].push(pguid); + } + + // 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(function(a,b) { + var ka = (8-portals[a].options.level)+a; + var kb = (8-portals[b].options.level)+b; + if (kakb) return 1; + else return 0; + }); + + for (var i=0; i= 0) { + this.portalClusters[cid].splice(index,1); + // FIXME? if this portal was in on the screen (in the first 10), and we still have 10+ portals, add the new 10to to the screen? + } + } +} + +window.Render.prototype.getPortalClusterID = function(portal) { + // project the lat/lng into absolute map pixels + var z = map.getZoom(); + + var point = map.project(portal.getLatLng(), z); + + var clusterpoint = point.divideBy(this.CLUSTER_SIZE).round(); + + return z+":"+clusterpoint.x+":"+clusterpoint.y; } From fd17b4a2787c2035f76915d852b24c3efe3393c1 Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Fri, 21 Feb 2014 16:35:48 +0000 Subject: [PATCH 50/58] portal-names - use different delays for data loading updates and user events - improves performance --- plugins/portal-names.user.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/portal-names.user.js b/plugins/portal-names.user.js index d6ba283c..74322119 100644 --- a/plugins/portal-names.user.js +++ b/plugins/portal-names.user.js @@ -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 ////////////////////////////////////////////////////////// From 457f4f804983ab0bfa106efa925fe7187effd99d Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Fri, 21 Feb 2014 16:37:47 +0000 Subject: [PATCH 51/58] test: remove code that rendered stale map data tiles from cache before retrieving from the network - will try it for a while and see if it makes things noticably more responsive --- code/map_data_request.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/map_data_request.js b/code/map_data_request.js index c0ec93ab..c227492d 100644 --- a/code/map_data_request.js +++ b/code/map_data_request.js @@ -253,10 +253,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 From 7efa603be13b13ada30180b6c8253106be2d282f Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Sat, 22 Feb 2014 02:57:25 +0000 Subject: [PATCH 52/58] update munge regular expressions for changes 2014-02-22 note: request format for map data has also changed and code needs updating --- code/munge.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) mode change 100644 => 100755 code/munge.js diff --git a/code/munge.js b/code/munge.js old mode 100644 new mode 100755 index ab16e207..bf7224ce --- a/code/munge.js +++ b/code/munge.js @@ -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; From 9c3e47ec401e0262a4753e0954ef3d1e83639cc2 Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Sat, 22 Feb 2014 02:58:52 +0000 Subject: [PATCH 53/58] protocol changes 2014-02-22 - quadkeys have gone back to using map zoom, rather than portal level, in their names --- code/map_data_calc_tools.js | 28 +++++++++++++++------------- code/map_data_request.js | 25 +++++++++++++------------ code/utils_misc.js | 9 ++------- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/code/map_data_calc_tools.js b/code/map_data_calc_tools.js index ea89c959..276e38f0 100644 --- a/code/map_data_calc_tools.js +++ b/code/map_data_calc_tools.js @@ -10,32 +10,34 @@ // 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.zoomToTilesPerEdge = function(zoom) { +// var LEVEL_TO_TILES_PER_EDGE = [65536, 65536, 16384, 16384, 4096, 1536, 1024, 256, 32]; +// return LEVEL_TO_TILES_PER_EDGE[level]; + var ZOOM_TO_TILES_PER_EDGE = [32, 32, 32, 32, 256, 256, 256, 1024, 1024, 1536, 4096, 4096, 16384, 16384, 16384]; + return ZOOM_TO_TILES_PER_EDGE[zoom] || 65536; } -window.lngToTile = function(lng, level) { - return Math.floor((lng + 180) / 360 * levelToTilesPerEdge(level)); +window.lngToTile = function(lng, zoom) { + return Math.floor((lng + 180) / 360 * zoomToTilesPerEdge(zoom)); } -window.latToTile = function(lat, level) { +window.latToTile = function(lat, zoom) { 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 * zoomToTilesPerEdge(zoom)); } -window.tileToLng = function(x, level) { - return x / levelToTilesPerEdge(level) * 360 - 180; +window.tileToLng = function(x, zoom) { + return x / zoomToTilesPerEdge(zoom) * 360 - 180; } -window.tileToLat = function(y, level) { - var n = Math.PI - 2 * Math.PI * y / levelToTilesPerEdge(level); +window.tileToLat = function(y, zoom) { + var n = Math.PI - 2 * Math.PI * y / zoomToTilesPerEdge(zoom); 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(zoom, x, y) { + return zoom + "_" + x + "_" + y; } diff --git a/code/map_data_request.js b/code/map_data_request.js index c227492d..ecb35e59 100644 --- a/code/map_data_request.js +++ b/code/map_data_request.js @@ -180,6 +180,7 @@ window.MapDataRequest.prototype.refresh = function() { var bounds = clampLatLngBounds(map.getBounds()); var zoom = map.getZoom(); +//TODO: fix this: the API now takes zoom (again) rather than portal level, so we need to go back to more like the old API var minPortalLevel = getMinPortalLevelForZoom(zoom); //DEBUG: resize the bounds so we only retrieve some data @@ -188,21 +189,21 @@ 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(), zoom); + var x2 = lngToTile(bounds.getEast(), zoom); + var y1 = latToTile(bounds.getNorth(), zoom); + var y2 = latToTile(bounds.getSouth(), zoom); // 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,zoom), tileToLng(x1,zoom)], + [tileToLat(y1,zoom), tileToLng(x2+1,zoom)] ]); //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: map.getZoom(), minPortalLevel: minPortalLevel, zoom: zoom }; window.runHooks ('mapDataRefreshStart', {bounds: bounds, zoom: zoom, minPortalLevel: minPortalLevel, tileBounds: dataBounds}); @@ -235,11 +236,11 @@ window.MapDataRequest.prototype.refresh = function() { 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(zoom, x, y); + var latNorth = tileToLat(y,zoom); + var latSouth = tileToLat(y+1,zoom); + var lngWest = tileToLng(x,zoom); + var lngEast = tileToLng(x+1,zoom); this.debugTiles.create(tile_id,[[latSouth,lngWest],[latNorth,lngEast]]); diff --git a/code/utils_misc.js b/code/utils_misc.js index 9c07ac72..185cac86 100644 --- a/code/utils_misc.js +++ b/code/utils_misc.js @@ -280,14 +280,9 @@ window.androidPermalink = function() { window.getMinPortalLevelForZoom = function(z) { + var ZOOM_TO_LEVEL = [8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 4, 4, 3, 2, 2, 1, 1]; - // 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; + return ZOOM_TO_LEVEL[z] || 0; } From bfb07753609689359b0c52050969871ed560f379 Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Sat, 22 Feb 2014 02:59:32 +0000 Subject: [PATCH 54/58] temp disable show more portals and default intel detail plugins - the code changes needed for protocol changes have broken them, and i need a bit of time to study the details before fixing them --- plugins/default-intel-detail.user.js | 34 +++++++++++----------- plugins/show-more-portals.user.js | 42 ++++++++++++++-------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/plugins/default-intel-detail.user.js b/plugins/default-intel-detail.user.js index 8bcc0ef2..9e17cf32 100644 --- a/plugins/default-intel-detail.user.js +++ b/plugins/default-intel-detail.user.js @@ -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.1.1.@@DATETIMEVERSION@@ // @namespace https://github.com/jonatkins/ingress-intel-total-conversion // @updateURL @@UPDATEURL@@ // @downloadURL @@DOWNLOADURL@@ @@ -24,22 +24,22 @@ 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); - } +// 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); +// } }; diff --git a/plugins/show-more-portals.user.js b/plugins/show-more-portals.user.js index 11cc5d99..17212da7 100644 --- a/plugins/show-more-portals.user.js +++ b/plugins/show-more-portals.user.js @@ -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.1.7.@@DATETIMEVERSION@@ // @namespace https://github.com/jonatkins/ingress-intel-total-conversion // @updateURL @@UPDATEURL@@ // @downloadURL @@DOWNLOADURL@@ @@ -24,26 +24,26 @@ 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; - } +// // 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; +// } }; From ba2d90779cc5224bd8425886efbce49adcf34225 Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Sat, 22 Feb 2014 03:00:30 +0000 Subject: [PATCH 55/58] bump version number for new release --- main.js | 2 +- mobile/AndroidManifest.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main.js b/main.js index 817629fe..44b86a02 100644 --- a/main.js +++ b/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@@ diff --git a/mobile/AndroidManifest.xml b/mobile/AndroidManifest.xml index 8bdb3293..3020f113 100644 --- a/mobile/AndroidManifest.xml +++ b/mobile/AndroidManifest.xml @@ -2,8 +2,8 @@ + android:versionCode="66" + android:versionName="0.10.5"> Date: Sat, 22 Feb 2014 04:10:15 +0000 Subject: [PATCH 56/58] update plugin descriptions from "PLUGIN CURRENTLY UNAVAILABLE" to something more descriptive. Hopefully will reduce questions in the community concerning these ones --- plugins/favorite-portals.user.js | 2 +- plugins/portal-highlighter-bad-deployment-distance.user.js | 2 +- plugins/portal-highlighter-can-make-level.user.js | 2 +- plugins/portal-highlighter-imminent-decay.user.js | 2 +- plugins/portal-highlighter-mitigation.user.js | 2 +- plugins/portal-highlighter-mods.user.js | 2 +- plugins/portal-highlighter-my-8-portals.user.js | 2 +- plugins/portal-highlighter-my-portals.user.js | 2 +- plugins/portal-highlighter-portal-ap-energy-relative.user.js | 2 +- plugins/portal-highlighter-portal-ap-relative.user.js | 2 +- plugins/portal-highlighter-portal-ap.user.js | 2 +- plugins/portal-highlighter-portals-upgrade.user.js | 2 +- plugins/portal-highlighter-with-lvl8-resonators.user.js | 2 +- plugins/scoreboard.user.js | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/plugins/favorite-portals.user.js b/plugins/favorite-portals.user.js index f82526df..d3b9c888 100644 --- a/plugins/favorite-portals.user.js +++ b/plugins/favorite-portals.user.js @@ -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@@ diff --git a/plugins/portal-highlighter-bad-deployment-distance.user.js b/plugins/portal-highlighter-bad-deployment-distance.user.js index 3ddf3a66..1d8dd459 100644 --- a/plugins/portal-highlighter-bad-deployment-distance.user.js +++ b/plugins/portal-highlighter-bad-deployment-distance.user.js @@ -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* diff --git a/plugins/portal-highlighter-can-make-level.user.js b/plugins/portal-highlighter-can-make-level.user.js index 1d5acc86..ef022dd1 100644 --- a/plugins/portal-highlighter-can-make-level.user.js +++ b/plugins/portal-highlighter-can-make-level.user.js @@ -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* diff --git a/plugins/portal-highlighter-imminent-decay.user.js b/plugins/portal-highlighter-imminent-decay.user.js index 14f98297..60296401 100644 --- a/plugins/portal-highlighter-imminent-decay.user.js +++ b/plugins/portal-highlighter-imminent-decay.user.js @@ -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* diff --git a/plugins/portal-highlighter-mitigation.user.js b/plugins/portal-highlighter-mitigation.user.js index 0956e19f..f3fe1c0b 100644 --- a/plugins/portal-highlighter-mitigation.user.js +++ b/plugins/portal-highlighter-mitigation.user.js @@ -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* diff --git a/plugins/portal-highlighter-mods.user.js b/plugins/portal-highlighter-mods.user.js index 46e19f2a..4f1664cd 100644 --- a/plugins/portal-highlighter-mods.user.js +++ b/plugins/portal-highlighter-mods.user.js @@ -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* diff --git a/plugins/portal-highlighter-my-8-portals.user.js b/plugins/portal-highlighter-my-8-portals.user.js index 12cfbeb7..02e4118d 100644 --- a/plugins/portal-highlighter-my-8-portals.user.js +++ b/plugins/portal-highlighter-my-8-portals.user.js @@ -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* diff --git a/plugins/portal-highlighter-my-portals.user.js b/plugins/portal-highlighter-my-portals.user.js index 7679a824..bac86774 100644 --- a/plugins/portal-highlighter-my-portals.user.js +++ b/plugins/portal-highlighter-my-portals.user.js @@ -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* diff --git a/plugins/portal-highlighter-portal-ap-energy-relative.user.js b/plugins/portal-highlighter-portal-ap-energy-relative.user.js index be4c81eb..ddf59847 100644 --- a/plugins/portal-highlighter-portal-ap-energy-relative.user.js +++ b/plugins/portal-highlighter-portal-ap-energy-relative.user.js @@ -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* diff --git a/plugins/portal-highlighter-portal-ap-relative.user.js b/plugins/portal-highlighter-portal-ap-relative.user.js index f5865636..e119c626 100644 --- a/plugins/portal-highlighter-portal-ap-relative.user.js +++ b/plugins/portal-highlighter-portal-ap-relative.user.js @@ -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* diff --git a/plugins/portal-highlighter-portal-ap.user.js b/plugins/portal-highlighter-portal-ap.user.js index 6d8180c8..49387887 100644 --- a/plugins/portal-highlighter-portal-ap.user.js +++ b/plugins/portal-highlighter-portal-ap.user.js @@ -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* diff --git a/plugins/portal-highlighter-portals-upgrade.user.js b/plugins/portal-highlighter-portals-upgrade.user.js index 2c3f5cef..fca2ca08 100644 --- a/plugins/portal-highlighter-portals-upgrade.user.js +++ b/plugins/portal-highlighter-portals-upgrade.user.js @@ -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* diff --git a/plugins/portal-highlighter-with-lvl8-resonators.user.js b/plugins/portal-highlighter-with-lvl8-resonators.user.js index d7f09d96..01f28340 100644 --- a/plugins/portal-highlighter-with-lvl8-resonators.user.js +++ b/plugins/portal-highlighter-with-lvl8-resonators.user.js @@ -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* diff --git a/plugins/scoreboard.user.js b/plugins/scoreboard.user.js index b3237809..52ee4d9b 100644 --- a/plugins/scoreboard.user.js +++ b/plugins/scoreboard.user.js @@ -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* From 8ce28334cc4f5dd82955a3405ec789332d76da47 Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Sat, 22 Feb 2014 05:48:52 +0000 Subject: [PATCH 57/58] internal cleanups on the API used to convert map zoom levels to the parameters needed to generate quadkeys for data tiles. this should also prevent mixed release plugins/test IITC builds causing issues with it's 'show more portals' plugin further changes are planned - by lying to the backend about the map zoom to optimise the portal detail level (density) returned and make better use of the data cache. needs some practical experimentation to get good adjustments, and a cleaner API for these plugins --- code/map_data_calc_tools.js | 36 ++++++++++++++++++++++-------------- code/map_data_request.js | 36 +++++++++++++++++------------------- code/utils_misc.js | 8 +------- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/code/map_data_calc_tools.js b/code/map_data_calc_tools.js index 276e38f0..80bb5e0f 100644 --- a/code/map_data_calc_tools.js +++ b/code/map_data_calc_tools.js @@ -10,34 +10,42 @@ // http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames -window.zoomToTilesPerEdge = function(zoom) { -// 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]; - return ZOOM_TO_TILES_PER_EDGE[zoom] || 65536; + 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, zoom) { - return Math.floor((lng + 180) / 360 * zoomToTilesPerEdge(zoom)); +window.lngToTile = function(lng, params) { + return Math.floor((lng + 180) / 360 * params.tilesPerEdge); } -window.latToTile = function(lat, zoom) { +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 * zoomToTilesPerEdge(zoom)); + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * params.tilesPerEdge); } -window.tileToLng = function(x, zoom) { - return x / zoomToTilesPerEdge(zoom) * 360 - 180; +window.tileToLng = function(x, params) { + return x / params.tilesPerEdge * 360 - 180; } -window.tileToLat = function(y, zoom) { - var n = Math.PI - 2 * Math.PI * y / zoomToTilesPerEdge(zoom); +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(zoom, x, y) { - return zoom + "_" + x + "_" + y; +window.pointToTileId = function(params, x, y) { + return params.zoom + "_" + x + "_" + y; } diff --git a/code/map_data_request.js b/code/map_data_request.js index ecb35e59..56165568 100644 --- a/code/map_data_request.js +++ b/code/map_data_request.js @@ -180,8 +180,8 @@ window.MapDataRequest.prototype.refresh = function() { var bounds = clampLatLngBounds(map.getBounds()); var zoom = map.getZoom(); -//TODO: fix this: the API now takes zoom (again) rather than portal level, so we need to go back to more like the old API - var minPortalLevel = getMinPortalLevelForZoom(zoom); + + var tileParams = getMapZoomTileParameters(zoom); //DEBUG: resize the bounds so we only retrieve some data //bounds = bounds.pad(-0.4); @@ -189,36 +189,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(), zoom); - var x2 = lngToTile(bounds.getEast(), zoom); - var y1 = latToTile(bounds.getNorth(), zoom); - var y2 = latToTile(bounds.getSouth(), zoom); + 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,zoom), tileToLng(x1,zoom)], - [tileToLat(y1,zoom), tileToLng(x2+1,zoom)] + [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, zoom: zoom }; + this.fetchedDataParams = { bounds: dataBounds, mapZoom: map.getZoom(), zoom: zoom }; - window.runHooks ('mapDataRefreshStart', {bounds: bounds, zoom: zoom, minPortalLevel: minPortalLevel, tileBounds: dataBounds}); + window.runHooks ('mapDataRefreshStart', {bounds: bounds, zoom: zoom, 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 '+zoom+' (L'+tileParams.level+'+ portals, '+tileParams.tilesPerEdge+' tiles per global edge), map zoom is '+map.getZoom()); this.cachedTileCount = 0; @@ -236,11 +234,11 @@ window.MapDataRequest.prototype.refresh = function() { for (var y = y1; y <= y2; y++) { // x goes from bottom to top(?) for (var x = x1; x <= x2; x++) { - var tile_id = pointToTileId(zoom, x, y); - var latNorth = tileToLat(y,zoom); - var latSouth = tileToLat(y+1,zoom); - var lngWest = tileToLng(x,zoom); - var lngEast = tileToLng(x+1,zoom); + 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]]); diff --git a/code/utils_misc.js b/code/utils_misc.js index 185cac86..9293dc8c 100644 --- a/code/utils_misc.js +++ b/code/utils_misc.js @@ -279,16 +279,10 @@ window.androidPermalink = function() { } -window.getMinPortalLevelForZoom = function(z) { - var ZOOM_TO_LEVEL = [8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 4, 4, 3, 2, 2, 1, 1]; - - return ZOOM_TO_LEVEL[z] || 0; -} - window.getMinPortalLevel = function() { var z = map.getZoom(); - return getMinPortalLevelForZoom(z); + return getMapZoomTileParameters(z).level; } // returns number of pixels left to scroll down before reaching the From 333c1a6dafbbdf39db474bbeae952d1bd3e8658f Mon Sep 17 00:00:00 2001 From: Jon Atkins Date: Sat, 22 Feb 2014 19:30:12 +0000 Subject: [PATCH 58/58] add tweaks of data retrieval zoom level, to make better use of the cache and be friendlier to the API in some cases also, default-intel-detail and show-more-portals plugins fixed - the logic has been moved into IITC core, with the plugins just setting flags to change behaviour --- code/map_data_calc_tools.js | 77 ++++++++++++++++++++++++++++ code/map_data_request.js | 17 +++--- code/utils_misc.js | 1 + plugins/default-intel-detail.user.js | 22 ++------ plugins/show-more-portals.user.js | 26 ++-------- 5 files changed, 96 insertions(+), 47 deletions(-) mode change 100644 => 100755 code/map_data_calc_tools.js diff --git a/code/map_data_calc_tools.js b/code/map_data_calc_tools.js old mode 100644 new mode 100755 index 80bb5e0f..15bf1bd4 --- a/code/map_data_calc_tools.js +++ b/code/map_data_calc_tools.js @@ -26,6 +26,83 @@ window.getMapZoomTileParameters = function(zoom) { } +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.lngToTile = function(lng, params) { return Math.floor((lng + 180) / 360 * params.tilesPerEdge); } diff --git a/code/map_data_request.js b/code/map_data_request.js index 56165568..e2f5008c 100644 --- a/code/map_data_request.js +++ b/code/map_data_request.js @@ -179,9 +179,12 @@ window.MapDataRequest.prototype.refresh = function() { var bounds = clampLatLngBounds(map.getBounds()); - var zoom = map.getZoom(); + var mapZoom = map.getZoom(); + + var dataZoom = getDataZoomForMapZoom(mapZoom); + + var tileParams = getMapZoomTileParameters(dataZoom); - var tileParams = getMapZoomTileParameters(zoom); //DEBUG: resize the bounds so we only retrieve some data //bounds = bounds.pad(-0.4); @@ -203,10 +206,10 @@ window.MapDataRequest.prototype.refresh = function() { //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(), zoom: zoom }; + this.fetchedDataParams = { bounds: dataBounds, mapZoom: mapZoom, dataZoom: dataZoom }; - window.runHooks ('mapDataRefreshStart', {bounds: bounds, zoom: zoom, minPortalLevel: tileParams.level, tileBounds: dataBounds}); + window.runHooks ('mapDataRefreshStart', {bounds: bounds, mapZoom: mapZoom, dataZoom: dataZoom, minPortalLevel: tileParams.level, tileBounds: dataBounds}); this.render.startRenderPass(); this.render.clearPortalsBelowLevel(tileParams.level); @@ -216,7 +219,7 @@ window.MapDataRequest.prototype.refresh = function() { this.render.processGameEntities(artifact.getArtifactEntities()); - console.log('requesting data tiles at zoom '+zoom+' (L'+tileParams.level+'+ portals, '+tileParams.tilesPerEdge+' tiles per global edge), 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; @@ -228,7 +231,7 @@ 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++) { @@ -263,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; diff --git a/code/utils_misc.js b/code/utils_misc.js index 9293dc8c..2af8c837 100644 --- a/code/utils_misc.js +++ b/code/utils_misc.js @@ -282,6 +282,7 @@ window.androidPermalink = function() { window.getMinPortalLevel = function() { var z = map.getZoom(); + z = getDataZoomForMapZoom(z); return getMapZoomTileParameters(z).level; } diff --git a/plugins/default-intel-detail.user.js b/plugins/default-intel-detail.user.js index 9e17cf32..107792a9 100644 --- a/plugins/default-intel-detail.user.js +++ b/plugins/default-intel-detail.user.js @@ -2,7 +2,7 @@ // @id iitc-plugin-default-intel-detail@jonatkins // @name IITC plugin: Default intel detail level // @category Tweaks -// @version 0.1.1.@@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; }; diff --git a/plugins/show-more-portals.user.js b/plugins/show-more-portals.user.js index 17212da7..0dde84dd 100644 --- a/plugins/show-more-portals.user.js +++ b/plugins/show-more-portals.user.js @@ -2,7 +2,7 @@ // @id iitc-plugin-show-more-portals@jonatkins // @name IITC plugin: Show more portals // @category Tweaks -// @version 0.1.7.@@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; };