PhoneGap
-> Callback
-> Cordova
-> PhoneGap?
Github -> Apache JIRA
https://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&jqlQuery=project+%3D+CB+AND+fixVersion+%3D+%221.6.0%22+AND+resolution+%3D+Unresolved+AND+component+%3D+CordovaJS+ORDER+BY+priority+DESC&mode=hide
Phonegap is unusable as an app platform for Android if the camera is involved.
var imageQuality = (ED.util.isAndroid() ? 50 : 70);
var destinationType = navigator.camera.DestinationType.FILE_URI;
// Check if this is a stupid Android that doesnt support canvas.toDataURL properly
if (!ED.util.supportsDataURL()) {
destinationType = navigator.camera.DestinationType.DATA_URL;
targetWidth = 400;
targetHeight = 400;
}
/**
* A safer decodeStream method
* rather than the one of {@link BitmapFactory}
* which will be easy to get OutOfMemory Exception
* while loading a big image file.
*/
protected Bitmap safeDecodeStream(Uri uri, int width, int height) {
}
☞ gist: PhoneGap Camera.java
String hackUri = uri.toString() + "?orientation=" + exif.orientation;
this.success(new PluginResult(PluginResult.Status.OK, hackUri), this.callbackId);
var degree = 0;
var canvasWidth = imageWidth, canvasHeight = imageHeight;
var canvasX = 0, canvasY = 0;
var orientation = ED.util.getUrlParam('orientation', imageUri);
if (orientation == 6) degree = 90;
if (orientation == 3) degree = 180;
if (orientation == 8) degree = 270;
switch(degree){
case 90:
canvasWidth = imageHeight;
canvasHeight = imageWidth;
canvasY = imageHeight * (-1);
break;
case 180:
canvasX = imageWidth * (-1);
canvasY = imageHeight * (-1);
break;
case 270:
canvasWidth = imageHeight;
canvasHeight = imageWidth;
canvasX = imageWidth * (-1);
break;
}
canvas.setAttribute('width', canvasWidth);
canvas.setAttribute('height', canvasHeight);
if (degree > 0) {
canvasContext.rotate(degree * Math.PI / 180);
}
canvasContext.drawImage($img[0], canvasX, canvasY);
☞ gist: Rotating photos with PhoneGap
(AKA $!?%! Android)
function setupFixedFix() {
if (isFixedBroken()) {
$('.container-fluid').css({'position': 'static'});
$('.actionsbar, .navbar').css({'position': 'absolute'});
fixFixed();
$(window).bind('scroll', fixFixed);
$('.mobile-page').bind('touchend', fixFixed);
}
}
function isFixedBroken(){
// Feature detection didnt seem to work so we check the user agent instead
return (ED.util.isIOS() && navigator.userAgent.match(/[5-9]_[0-9]/) === null);
}
function fixFixed() {
if (isFixedBroken()) {
$('.actionsbar').css({'top': (window.pageYOffset) + 'px'});
$('.navbar').css({'top': (window.pageYOffset + window.innerHeight - 30) + 'px'});
}
}
ED.util.addTouchOrClickHandler($('.mobile-log-a'), openPageLink);
ED.util.addClickHandler($('.mobile-footer-a'), openPageLink);
ED.util.addClickHandler($('.mobile-stream-a'), openStreamLink);
function addTouchOrClickHandler(dom, callback, logThis) {
if (useTouchEvents()) {
dom.each(function() {
$(this).unbind('tap', callback);
$(this).bind('tap', callback);
$(this).bind('touchstart', function(e) {
e.preventDefault();
var item = e.currentTarget;
if (ISTOUCHING) return;
item.moved = false;
ISTOUCHING = true;
item.startX = e.touches[0].pageX;
item.startY = e.touches[0].pageY;
$(item).addClass('active');
});
$(this).bind('touchmove', function(e) {
var item = e.currentTarget;
if (Math.abs(e.touches[0].pageX - item.startX) > 10 ||
Math.abs(e.touches[0].pageY - item.startY) > 10) {
item.moved = true;
$(item).removeClass('active');
}
});
$(this).bind('touchend', function(e) {
var item = e.currentTarget;
ISTOUCHING = false;
if(!item.moved) {
$(item).trigger('tap');
}
setTimeout(function() {
$(item).removeClass('active');
}, 300);
delete item.moved;
});
});
} else {
dom.unbind('click', callback).bind('click', callback);
}
}
def click_button(self, selector):
if self.driver.name == 'iPhone':
self.driver.execute_script('$("%s").trigger("tap")' % (selector))
else:
try:
self.get_el(selector).click()
function resetScroll(top) {
top = top || 0;
$(document).scrollTop(top);
window.setTimeout(function() {
$(document).scrollTop(top);
}, 10);
}
function show() {
ED.util.resetScroll();
ED.util.makeAutoResize($(DOM.notesInput));
ED.util.putCursorAtEnd($(DOM.notesInput));
}
<input type="number" step="0.01">
function hideKeyboard() {
$('input, textarea').blur();
if (ED.util.isIOS()) {
document.activeElement.blur();
}
if (ED.util.isAndroid()) {
var field = $('<input type="text">');
$('#mobile-logs-page').append(field);
setTimeout(function() {
field.focus();
setTimeout(function() {
field.hide();
}, 50);
}, 50);
}
}
var MSG_LOG_OFFLINE = 'We can\'t connect to the network now, so your log may be out of date.';
runIfOnline(function() {
fetchNewData();
}, MSG_LOG_OFFLINE);
}
function runIfOnline(onlineFunc, message, offlineFunc) {
message = message || 'Sorry, we can\'t connect to the network right now.';
if (isOnline()) {
try {
onlineFunc();
} catch(e) {
logError(e);
}
} else {
alert(message);
if (offlineFunc) {
try {
offlineFunc();
} catch(ee) {
logError(ee);
}
}
}
}
function isOnline() {
// Presume online unless PhoneGap or HTML5 tell us otherwise.
if (navigator.network && navigator.network.connection.type == Connection.NONE && 0) {
ED.util.log('Found Connection.NONE');
return false;
}
if (navigator.onLine === false) {
ED.util.log('Found navigator.onLine is false');
return false;
}
return true;
}

<html>
<head>
<script>
var timedEvents = [];
function timeEvent(name) {
timedEvents.push({'name': name || 'unnamed', time: Date.now()});
}
function showTimedEvents() {
var timeText = '';
var timeHtml = '<table>';
for (var i = 0; i < timedEvents.length; i++) {
var timedEvent = timedEvents[i];
timeText += timedEvent.name + ': ' + timedEvent.time;
var diff = '';
if (i > 0) {
diff = (timedEvent.time - timedEvents[i-1].time);
timeText += ' (' + diff + 'ms elapsed)';
}
timeHtml += '<tr><td>' + timedEvent.name + '<td>' + timedEvent.time + '<td>' + diff;
timeText += '\n';
}
console.log(timeText);
document.body.innerHTML += timeHtml;
}
</script>
<script>timeEvent('Before CSS');</script>
<link rel="stylesheet" href="css/all-phonegap-min.css?v=10201550"/>
<script>timeEvent('After CSS');</script>
</head>
<body>
<div>HTML here (more than this)</div>
<script>timeEvent('After HTML');</script>
<script src="js/libs/jquery-1.6.2.min.js"></script>
<script>timeEvent('After jQuery script tag');</script>
<script src="js/all-phonegap-min.js?v=10201550"></script>
<script>timeEvent('After other script tag');</script>
<script>
$(document).ready(function() {
timeEvent('After document ready');
myCustomMobileFunction();
timeEvent('After ready JS called');
showTimedEvents();
});
</script>
</body>
</html>
jQuery -> Zepto
Shaved: 22%
onload -> deviceready
Shaved: 67%
document.addEventListener("DOMContentLoaded", function() {
ED.mobile.setupMobile();
});
document.addEventListener('deviceready', function() {
ED.mobile.setupPhonegap();
}, false);
function setupMobile() {
// Sets up the page CSS
// Binds events to DOM
}
function setupPhonegap() {
// Shows splash page
// Fetches data
// Binds PhoneGap events
}
☞ Get to deviceready faster on Android
.android {
.modal {
@include box-shadow(none);
@include background-clip(border-box);
@include border-radius(0px);
border: 1px solid black;
}
}
$(window).on('scroll', $.throttle(500, loadVisibleImages));
☞ Delayed Image Loading on Long Pages
$textAreas.autoResizer({resizeOnChange: !isAndroid()});
window.setTimeout(function() {
ED.shared.setupStream();
ED.shared.setupStreamTimer();
}, 2000);
window.setTimeout(function() {
ED.shared.maybeFetchStream();
}, 100);
window.setTimeout(function() {
fetchNewData();
}, 1000);
☞ Zakas: Responsive Interfaces