mirror of
https://github.com/apache/cordova-android.git
synced 2025-01-19 07:02:51 +08:00
Merge branch 'master' of github.com:phonegap/phonegap-android
This commit is contained in:
commit
941b64f6a2
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ local.properties
|
||||
framework/phonegap.jar
|
||||
framework/bin
|
||||
framework/assets/www/.DS_Store
|
||||
.DS_Store
|
||||
|
@ -5,8 +5,8 @@
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<title>PhoneGap</title>
|
||||
<link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title" charset="utf-8">
|
||||
<script type="text/javascript" charset="utf-8" src="phonegap.0.9.5.min.js"></script>
|
||||
<script type="text/javascript" charset="utf-8" src="main.js"></script>
|
||||
<script type="text/javascript" charset="utf-8" src="phonegap.0.9.6.1.min.js"></script>
|
||||
<script type="text/javascript" charset="utf-8" src="main.js"></script>
|
||||
|
||||
</head>
|
||||
<body onload="init();" id="stage" class="theme">
|
||||
@ -31,8 +31,8 @@
|
||||
<a href="#" class="btn large" onclick="show_pic();">Get a Picture</a>
|
||||
<a href="#" class="btn large" onclick="get_contacts();">Get Phone's Contacts</a>
|
||||
<a href="#" class="btn large" onclick="check_network();">Check Network</a>
|
||||
<div id="viewport" class="viewport" style="display: none;">
|
||||
<div id="viewport" class="viewport" style="display: none;">
|
||||
<img style="width:60px;height:60px" id="test_img" src="" />
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -14,15 +14,16 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_SMS" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.RECORD_VIDEO"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||
@ -36,8 +37,11 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.phonegap.DroidGap" android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<uses-sdk android:minSdkVersion="2" />
|
||||
|
||||
<uses-sdk android:minSdkVersion="2" />
|
||||
</manifest>
|
||||
|
@ -1044,7 +1044,7 @@ LocalFileSystem.prototype._castDate = function(pluginResult) {
|
||||
file.type = pluginResult.message.type;
|
||||
file.name = pluginResult.message.name;
|
||||
file.fullPath = pluginResult.message.fullPath;
|
||||
file.lastModifedDate = new Date(pluginResult.message.lastModifiedDate);
|
||||
file.lastModifiedDate = new Date(pluginResult.message.lastModifiedDate);
|
||||
pluginResult.message = file;
|
||||
}
|
||||
return pluginResult;
|
||||
|
@ -65,29 +65,49 @@ Network.prototype.isReachable = function(uri, callback, options) {
|
||||
*/
|
||||
var Connection = function() {
|
||||
this.type = null;
|
||||
this.homeNW = null;
|
||||
this.currentNW = null;
|
||||
this._firstRun = true;
|
||||
this._timer = null;
|
||||
this.timeout = 500;
|
||||
|
||||
var me = this;
|
||||
this.getInfo(
|
||||
function(info) {
|
||||
me.type = info.type;
|
||||
me.homeNW = info.homeNW;
|
||||
me.currentNW = info.currentNW;
|
||||
PhoneGap.onPhoneGapConnectionReady.fire();
|
||||
function(type) {
|
||||
// Need to send events if we are on or offline
|
||||
if (type == "none") {
|
||||
// set a timer if still offline at the end of timer send the offline event
|
||||
me._timer = setTimeout(function(){
|
||||
me.type = type;
|
||||
PhoneGap.fireEvent('offline');
|
||||
me._timer = null;
|
||||
}, me.timeout);
|
||||
} else {
|
||||
// If there is a current offline event pending clear it
|
||||
if (me._timer != null) {
|
||||
clearTimeout(me._timer);
|
||||
me._timer = null;
|
||||
}
|
||||
me.type = type;
|
||||
PhoneGap.fireEvent('online');
|
||||
}
|
||||
|
||||
// should only fire this once
|
||||
if (me._firstRun) {
|
||||
me._firstRun = false;
|
||||
PhoneGap.onPhoneGapConnectionReady.fire();
|
||||
}
|
||||
},
|
||||
function(e) {
|
||||
console.log("Error initializing Network Connection: " + e);
|
||||
});
|
||||
};
|
||||
|
||||
Connection.UNKNOWN = 0;
|
||||
Connection.ETHERNET = 1;
|
||||
Connection.WIFI = 2;
|
||||
Connection.CELL_2G = 3;
|
||||
Connection.CELL_3G = 4;
|
||||
Connection.CELL_4G = 5;
|
||||
Connection.NONE = 20;
|
||||
Connection.UNKNOWN = "unknown";
|
||||
Connection.ETHERNET = "ethernet";
|
||||
Connection.WIFI = "wifi";
|
||||
Connection.CELL_2G = "2g";
|
||||
Connection.CELL_3G = "3g";
|
||||
Connection.CELL_4G = "4g";
|
||||
Connection.NONE = "none";
|
||||
|
||||
/**
|
||||
* Get connection info
|
||||
|
@ -99,7 +99,7 @@ PhoneGap.Channel.prototype.subscribe = function(f, c, g) {
|
||||
if (f === null) { return; }
|
||||
|
||||
var func = f;
|
||||
if (typeof c === "object" && f instanceof Function) { func = PhoneGap.close(c, f); }
|
||||
if (typeof c === "object" && typeof f === "function") { func = PhoneGap.close(c, f); }
|
||||
|
||||
g = g || func.observer_guid || f.observer_guid || this.guid++;
|
||||
func.observer_guid = g;
|
||||
@ -120,7 +120,7 @@ PhoneGap.Channel.prototype.subscribeOnce = function(f, c) {
|
||||
_this.unsubscribe(g);
|
||||
};
|
||||
if (this.fired) {
|
||||
if (typeof c === "object" && f instanceof Function) { f = PhoneGap.close(c, f); }
|
||||
if (typeof c === "object" && typeof f === "function") { f = PhoneGap.close(c, f); }
|
||||
f.apply(this, this.fireArgs);
|
||||
} else {
|
||||
g = this.subscribe(m);
|
||||
@ -132,7 +132,7 @@ PhoneGap.Channel.prototype.subscribeOnce = function(f, c) {
|
||||
* Unsubscribes the function with the given guid from the channel.
|
||||
*/
|
||||
PhoneGap.Channel.prototype.unsubscribe = function(g) {
|
||||
if (g instanceof Function) { g = g.observer_guid; }
|
||||
if (typeof g === "function") { g = g.observer_guid; }
|
||||
this.handlers[g] = null;
|
||||
delete this.handlers[g];
|
||||
};
|
||||
@ -147,7 +147,7 @@ PhoneGap.Channel.prototype.fire = function(e) {
|
||||
for (item in this.handlers) {
|
||||
if (this.handlers.hasOwnProperty(item)) {
|
||||
handler = this.handlers[item];
|
||||
if (handler instanceof Function) {
|
||||
if (typeof handler === "function") {
|
||||
rv = (handler.apply(this, arguments) === false);
|
||||
fail = fail || rv;
|
||||
}
|
||||
@ -344,10 +344,13 @@ PhoneGap.Channel.join(function() {
|
||||
}
|
||||
else {
|
||||
var polling = prompt("usePolling", "gap_callbackServer:");
|
||||
PhoneGap.UsePolling = polling;
|
||||
if (polling == "true") {
|
||||
PhoneGap.UsePolling = true;
|
||||
PhoneGap.JSCallbackPolling();
|
||||
}
|
||||
else {
|
||||
PhoneGap.UsePolling = false;
|
||||
PhoneGap.JSCallback();
|
||||
}
|
||||
}
|
||||
@ -507,7 +510,7 @@ PhoneGap.clone = function(obj) {
|
||||
return retVal;
|
||||
}
|
||||
|
||||
if (obj instanceof Function) {
|
||||
if (typeof obj === "function") {
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -767,8 +770,8 @@ PhoneGap.JSCallback = function() {
|
||||
// If callback has JavaScript statement to execute
|
||||
if (xmlhttp.status === 200) {
|
||||
|
||||
// Need to url decode the response and replace %20 with a space
|
||||
var msg = decodeURIComponent(xmlhttp.responseText.replace(/\+/g, '%20'));
|
||||
// Need to url decode the response
|
||||
var msg = decodeURIComponent(xmlhttp.responseText);
|
||||
setTimeout(function() {
|
||||
try {
|
||||
var t = eval(msg);
|
||||
@ -802,13 +805,11 @@ PhoneGap.JSCallback = function() {
|
||||
console.log("JSCallback Error: Bad request. Stopping callbacks.");
|
||||
}
|
||||
|
||||
// If error, restart callback server
|
||||
// If error, revert to polling
|
||||
else {
|
||||
console.log("JSCallback Error: Request failed.");
|
||||
prompt("restartServer", "gap_callbackServer:");
|
||||
PhoneGap.JSCallbackPort = null;
|
||||
PhoneGap.JSCallbackToken = null;
|
||||
setTimeout(PhoneGap.JSCallback, 100);
|
||||
PhoneGap.UsePolling = true;
|
||||
PhoneGap.JSCallbackPolling();
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -935,7 +936,11 @@ PhoneGap.includeJavascript = function(jsfile, successCallback) {
|
||||
*/
|
||||
var PluginManager = {
|
||||
addService: function(serviceType, className) {
|
||||
navigator.app.addService(serviceType, className);
|
||||
try {
|
||||
navigator.app.addService(serviceType, className);
|
||||
} catch (e) {
|
||||
console.log("Error adding service "+serviceType+": "+e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<script src="phonegap.0.9.5.min.js"></script>
|
||||
<script src="phonegap.0.9.6.1.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
@ -10,5 +10,5 @@
|
||||
# Indicates whether an apk should be generated for each density.
|
||||
split.density=false
|
||||
# Project target.
|
||||
target=android-8
|
||||
target=android-12
|
||||
apk-configurations=
|
||||
|
@ -12,6 +12,7 @@ import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This class exposes methods in DroidGap that can be called from JavaScript.
|
||||
@ -83,8 +84,11 @@ public class App extends Plugin {
|
||||
public void loadUrl(String url, JSONObject props) throws JSONException {
|
||||
System.out.println("App.loadUrl("+url+","+props+")");
|
||||
int wait = 0;
|
||||
|
||||
boolean usePhoneGap = true;
|
||||
boolean clearPrev = false;
|
||||
|
||||
// If there are properties, then set them on the Activity
|
||||
HashMap<String, Object> params = new HashMap<String, Object>();
|
||||
if (props != null) {
|
||||
JSONArray keys = props.names();
|
||||
for (int i=0; i<keys.length(); i++) {
|
||||
@ -92,31 +96,42 @@ public class App extends Plugin {
|
||||
if (key.equals("wait")) {
|
||||
wait = props.getInt(key);
|
||||
}
|
||||
else if (key.equalsIgnoreCase("usephonegap")) {
|
||||
usePhoneGap = props.getBoolean(key);
|
||||
}
|
||||
else if (key.equalsIgnoreCase("clearprev")) {
|
||||
clearPrev = props.getBoolean(key);
|
||||
}
|
||||
else {
|
||||
Object value = props.get(key);
|
||||
if (value == null) {
|
||||
|
||||
|
||||
}
|
||||
else if (value.getClass().equals(String.class)) {
|
||||
this.ctx.getIntent().putExtra(key, (String)value);
|
||||
params.put(key, (String)value);
|
||||
}
|
||||
else if (value.getClass().equals(Boolean.class)) {
|
||||
this.ctx.getIntent().putExtra(key, (Boolean)value);
|
||||
params.put(key, (Boolean)value);
|
||||
}
|
||||
else if (value.getClass().equals(Integer.class)) {
|
||||
this.ctx.getIntent().putExtra(key, (Integer)value);
|
||||
params.put(key, (Integer)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If wait property, then delay loading
|
||||
|
||||
if (wait > 0) {
|
||||
((DroidGap)this.ctx).loadUrl(url, wait);
|
||||
}
|
||||
else {
|
||||
((DroidGap)this.ctx).loadUrl(url);
|
||||
try {
|
||||
synchronized(this) {
|
||||
this.wait(wait);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
((DroidGap)this.ctx).showWebPage(url, usePhoneGap, clearPrev, params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,12 +73,12 @@ public class AudioHandler extends Plugin {
|
||||
this.stopPlayingAudio(args.getString(0));
|
||||
}
|
||||
else if (action.equals("getCurrentPositionAudio")) {
|
||||
long l = this.getCurrentPositionAudio(args.getString(0));
|
||||
return new PluginResult(status, l);
|
||||
float f = this.getCurrentPositionAudio(args.getString(0));
|
||||
return new PluginResult(status, f);
|
||||
}
|
||||
else if (action.equals("getDurationAudio")) {
|
||||
long l = this.getDurationAudio(args.getString(0), args.getString(1));
|
||||
return new PluginResult(status, l);
|
||||
float f = this.getDurationAudio(args.getString(0), args.getString(1));
|
||||
return new PluginResult(status, f);
|
||||
}
|
||||
else if (action.equals("release")) {
|
||||
boolean b = this.release(args.getString(0));
|
||||
@ -230,10 +230,10 @@ public class AudioHandler extends Plugin {
|
||||
* @param id The id of the audio player
|
||||
* @return position in msec
|
||||
*/
|
||||
public long getCurrentPositionAudio(String id) {
|
||||
public float getCurrentPositionAudio(String id) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
return(audio.getCurrentPosition());
|
||||
return(audio.getCurrentPosition()/1000.0f);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@ -245,7 +245,7 @@ public class AudioHandler extends Plugin {
|
||||
* @param file The name of the audio file.
|
||||
* @return The duration in msec.
|
||||
*/
|
||||
public long getDurationAudio(String id, String file) {
|
||||
public float getDurationAudio(String id, String file) {
|
||||
|
||||
// Get audio file
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
|
@ -15,6 +15,7 @@ import android.media.MediaPlayer.OnErrorListener;
|
||||
import android.media.MediaRecorder;
|
||||
import android.media.MediaPlayer.OnCompletionListener;
|
||||
import android.media.MediaPlayer.OnPreparedListener;
|
||||
import android.os.Environment;
|
||||
|
||||
/**
|
||||
* This class implements the audio playback and recording capabilities used by PhoneGap.
|
||||
@ -53,7 +54,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
private String id; // The id of this player (used to identify Media object in JavaScript)
|
||||
private int state = MEDIA_NONE; // State of recording or playback
|
||||
private String audioFile = null; // File name to play or record to
|
||||
private long duration = -1; // Duration of audio
|
||||
private float duration = -1; // Duration of audio
|
||||
|
||||
private MediaRecorder recorder = null; // Audio recording object
|
||||
private String tempFile = null; // Temporary recording file name
|
||||
@ -70,10 +71,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
public AudioPlayer(AudioHandler handler, String id) {
|
||||
this.handler = handler;
|
||||
this.id = id;
|
||||
|
||||
// YES, I know this is bad, but I can't do it the right way because Google didn't have the
|
||||
// foresight to add android.os.environment.getExternalDataDirectory until Android 2.2
|
||||
this.tempFile = "/sdcard/tmprecording.mp3";
|
||||
this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.mp3";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -209,7 +207,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
this.mPlayer.prepare();
|
||||
|
||||
// Get duration
|
||||
this.duration = this.mPlayer.getDuration();
|
||||
this.duration = getDurationInSeconds();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
@ -319,7 +317,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
* -1=can't be determined
|
||||
* -2=not allowed
|
||||
*/
|
||||
public long getDuration(String file) {
|
||||
public float getDuration(String file) {
|
||||
|
||||
// Can't get duration of recording
|
||||
if (this.recorder != null) {
|
||||
@ -362,7 +360,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
}
|
||||
|
||||
// Save off duration
|
||||
this.duration = this.mPlayer.getDuration();
|
||||
this.duration = getDurationInSeconds();
|
||||
this.prepareOnly = false;
|
||||
|
||||
// Send status notification to JavaScript
|
||||
@ -370,6 +368,15 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* By default Android returns the length of audio in mills but we want seconds
|
||||
*
|
||||
* @return length of clip in seconds
|
||||
*/
|
||||
private float getDurationInSeconds() {
|
||||
return (this.mPlayer.getDuration() / 1000.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when there has been an error during an asynchronous operation
|
||||
* (other errors will throw exceptions at method call time).
|
||||
|
@ -11,11 +11,14 @@ import java.io.BufferedReader;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class provides a way for Java to run JavaScript in the web page that has loaded PhoneGap.
|
||||
* The CallbackServer class implements an XHR server and a polling server with a list of JavaScript
|
||||
@ -42,6 +45,8 @@ import java.util.LinkedList;
|
||||
*/
|
||||
public class CallbackServer implements Runnable {
|
||||
|
||||
private static final String LOG_TAG = "CallbackServer";
|
||||
|
||||
/**
|
||||
* The list of JavaScript statements to be sent to JavaScript.
|
||||
*/
|
||||
@ -224,8 +229,9 @@ public class CallbackServer implements Runnable {
|
||||
//System.out.println("CallbackServer -- sending item");
|
||||
response = "HTTP/1.1 200 OK\r\n\r\n";
|
||||
String js = this.getJavascript();
|
||||
if (js != null)
|
||||
response += URLEncoder.encode(js, "UTF-8");
|
||||
if (js != null) {
|
||||
response += encode(js, "UTF-8");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -321,4 +327,81 @@ public class CallbackServer implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
/* The Following code has been modified from original implementation of URLEncoder */
|
||||
|
||||
/* start */
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
static final String digits = "0123456789ABCDEF";
|
||||
|
||||
/**
|
||||
* This will encode the return value to JavaScript. We revert the encoding for
|
||||
* common characters that don't require encoding to reduce the size of the string
|
||||
* being passed to JavaScript.
|
||||
*
|
||||
* @param s to be encoded
|
||||
* @param enc encoding type
|
||||
* @return encoded string
|
||||
*/
|
||||
public static String encode(String s, String enc) throws UnsupportedEncodingException {
|
||||
if (s == null || enc == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
// check for UnsupportedEncodingException
|
||||
"".getBytes(enc);
|
||||
|
||||
// Guess a bit bigger for encoded form
|
||||
StringBuilder buf = new StringBuilder(s.length() + 16);
|
||||
int start = -1;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char ch = s.charAt(i);
|
||||
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
|
||||
|| (ch >= '0' && ch <= '9')
|
||||
|| " .-*_'(),<>=?@[]{}:~\"\\/;!".indexOf(ch) > -1) {
|
||||
if (start >= 0) {
|
||||
convert(s.substring(start, i), buf, enc);
|
||||
start = -1;
|
||||
}
|
||||
if (ch != ' ') {
|
||||
buf.append(ch);
|
||||
} else {
|
||||
buf.append(' ');
|
||||
}
|
||||
} else {
|
||||
if (start < 0) {
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (start >= 0) {
|
||||
convert(s.substring(start, s.length()), buf, enc);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static void convert(String s, StringBuilder buf, String enc) throws UnsupportedEncodingException {
|
||||
byte[] bytes = s.getBytes(enc);
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
buf.append('%');
|
||||
buf.append(digits.charAt((bytes[j] & 0xf0) >> 4));
|
||||
buf.append(digits.charAt(bytes[j] & 0xf));
|
||||
}
|
||||
}
|
||||
|
||||
/* end */
|
||||
}
|
||||
|
@ -166,7 +166,14 @@ public class CameraLauncher extends Plugin {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
try {
|
||||
// Read in bitmap of captured image
|
||||
Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri);
|
||||
Bitmap bitmap;
|
||||
try {
|
||||
bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri);
|
||||
} catch (FileNotFoundException e) {
|
||||
Uri uri = intent.getData();
|
||||
android.content.ContentResolver resolver = this.ctx.getContentResolver();
|
||||
bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
|
||||
}
|
||||
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
|
@ -178,11 +178,13 @@ public abstract class ContactAccessor {
|
||||
protected String getJsonString(JSONObject obj, String property) {
|
||||
String value = null;
|
||||
try {
|
||||
if (obj != null) {
|
||||
value = obj.getString(property);
|
||||
if (value.equals("null")) {
|
||||
Log.d(LOG_TAG, property + " is string called 'null'");
|
||||
value = null;
|
||||
}
|
||||
if (value.equals("null")) {
|
||||
Log.d(LOG_TAG, property + " is string called 'null'");
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Could not get = " + e.getMessage());
|
||||
|
@ -1420,7 +1420,6 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
// Add urls
|
||||
JSONArray websites = null;
|
||||
try {
|
||||
@ -1473,7 +1472,6 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
retVal = false;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -14,13 +14,11 @@ import org.json.JSONObject;
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
public class Device extends Plugin {
|
||||
|
||||
public static String phonegapVersion = "0.9.5"; // PhoneGap version
|
||||
public static String phonegapVersion = "0.9.6.1"; // PhoneGap version
|
||||
public static String platform = "Android"; // Device OS
|
||||
public static String uuid; // Device UUID
|
||||
|
||||
@ -116,34 +114,13 @@ public class Device extends Plugin {
|
||||
public String getPhonegapVersion() {
|
||||
return Device.phonegapVersion;
|
||||
}
|
||||
|
||||
public String getLine1Number(){
|
||||
TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
return operator.getLine1Number();
|
||||
}
|
||||
|
||||
public String getDeviceId(){
|
||||
TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
return operator.getDeviceId();
|
||||
}
|
||||
|
||||
public String getSimSerialNumber(){
|
||||
TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
return operator.getSimSerialNumber();
|
||||
}
|
||||
|
||||
public String getSubscriberId(){
|
||||
TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
return operator.getSubscriberId();
|
||||
}
|
||||
|
||||
public String getModel()
|
||||
{
|
||||
public String getModel() {
|
||||
String model = android.os.Build.MODEL;
|
||||
return model;
|
||||
}
|
||||
public String getProductName()
|
||||
{
|
||||
|
||||
public String getProductName() {
|
||||
String productname = android.os.Build.PRODUCT;
|
||||
return productname;
|
||||
}
|
||||
@ -158,8 +135,7 @@ public class Device extends Plugin {
|
||||
return osversion;
|
||||
}
|
||||
|
||||
public String getSDKVersion()
|
||||
{
|
||||
public String getSDKVersion() {
|
||||
String sdkversion = android.os.Build.VERSION.SDK;
|
||||
return sdkversion;
|
||||
}
|
||||
|
@ -7,38 +7,49 @@
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.widget.EditText;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.net.http.SslError;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.JsResult;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.GeolocationPermissions.Callback;
|
||||
import android.webkit.JsPromptResult;
|
||||
import android.webkit.JsResult;
|
||||
import android.webkit.SslErrorHandler;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebSettings.LayoutAlgorithm;
|
||||
import android.webkit.WebStorage;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.webkit.GeolocationPermissions.Callback;
|
||||
import android.webkit.WebSettings.LayoutAlgorithm;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginManager;
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
|
||||
/**
|
||||
* This class is the main Android activity that represents the PhoneGap
|
||||
@ -119,10 +130,15 @@ public class DroidGap extends PhonegapActivity {
|
||||
protected boolean clearHistory = false;
|
||||
|
||||
// The initial URL for our app
|
||||
// ie http://server/path/index.html#abc?query
|
||||
private String url;
|
||||
|
||||
// The initial URL for our app up to and including the file name
|
||||
// ie http://server/path/index.html
|
||||
private String urlFile;
|
||||
|
||||
// The base of the initial URL for our app
|
||||
private String baseUrl;
|
||||
private String baseUrl = null;
|
||||
|
||||
// Plugin to call when activity result is received
|
||||
protected Plugin activityResultCallback = null;
|
||||
@ -168,7 +184,11 @@ public class DroidGap extends PhonegapActivity {
|
||||
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
// This builds the view. We could probably get away with NOT having a LinearLayout, but I like having a bucket!
|
||||
|
||||
root = new LinearLayout(this);
|
||||
Display display = getWindowManager().getDefaultDisplay();
|
||||
int width = display.getWidth();
|
||||
int height = display.getHeight();
|
||||
|
||||
root = new LinearLayoutSoftKeyboardDetect(this, width, height);
|
||||
root.setOrientation(LinearLayout.VERTICAL);
|
||||
root.setBackgroundColor(Color.BLACK);
|
||||
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
@ -330,13 +350,16 @@ public class DroidGap extends PhonegapActivity {
|
||||
*/
|
||||
public void loadUrl(final String url) {
|
||||
System.out.println("loadUrl("+url+")");
|
||||
this.urlFile = this.getUrlFile(url);
|
||||
this.url = url;
|
||||
int i = url.lastIndexOf('/');
|
||||
if (i > 0) {
|
||||
this.baseUrl = url.substring(0, i);
|
||||
}
|
||||
else {
|
||||
this.baseUrl = this.url;
|
||||
if (this.baseUrl == null) {
|
||||
int i = url.lastIndexOf('/');
|
||||
if (i > 0) {
|
||||
this.baseUrl = url.substring(0, i+1);
|
||||
}
|
||||
else {
|
||||
this.baseUrl = this.url + "/";
|
||||
}
|
||||
}
|
||||
System.out.println("url="+url+" baseUrl="+baseUrl);
|
||||
|
||||
@ -608,17 +631,28 @@ public class DroidGap extends PhonegapActivity {
|
||||
// Send pause event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};");
|
||||
|
||||
// Forward to plugins
|
||||
this.pluginManager.onPause(this.keepRunning);
|
||||
|
||||
// If app doesn't want to run in background
|
||||
if (!this.keepRunning) {
|
||||
|
||||
// Forward to plugins
|
||||
this.pluginManager.onPause();
|
||||
|
||||
// Pause JavaScript timers (including setInterval)
|
||||
this.appView.pauseTimers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Called when the activity receives a new intent
|
||||
**/
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
|
||||
//Forward to plugins
|
||||
this.pluginManager.onNewIntent(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
@ -632,6 +666,9 @@ public class DroidGap extends PhonegapActivity {
|
||||
// Send resume event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onResume.fire();}catch(e){};");
|
||||
|
||||
// Forward to plugins
|
||||
this.pluginManager.onResume(this.keepRunning || this.activityResultKeepRunning);
|
||||
|
||||
// If app doesn't want to run in background
|
||||
if (!this.keepRunning || this.activityResultKeepRunning) {
|
||||
|
||||
@ -641,9 +678,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
this.activityResultKeepRunning = false;
|
||||
}
|
||||
|
||||
// Forward to plugins
|
||||
this.pluginManager.onResume();
|
||||
|
||||
// Resume JavaScript timers (including setInterval)
|
||||
this.appView.resumeTimers();
|
||||
}
|
||||
@ -658,21 +692,18 @@ public class DroidGap extends PhonegapActivity {
|
||||
|
||||
if (this.appView != null) {
|
||||
|
||||
// Make sure pause event is sent if onPause hasn't been called before onDestroy
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};");
|
||||
// Make sure pause event is sent if onPause hasn't been called before onDestroy
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};");
|
||||
|
||||
// Send destroy event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onDestroy.fire();}catch(e){};");
|
||||
// Send destroy event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onDestroy.fire();}catch(e){};");
|
||||
|
||||
// Load blank page so that JavaScript onunload is called
|
||||
this.appView.loadUrl("about:blank");
|
||||
// Load blank page so that JavaScript onunload is called
|
||||
this.appView.loadUrl("about:blank");
|
||||
|
||||
// Forward to plugins
|
||||
this.pluginManager.onDestroy();
|
||||
// Forward to plugins
|
||||
this.pluginManager.onDestroy();
|
||||
|
||||
if (this.callbackServer != null) {
|
||||
this.callbackServer.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -696,13 +727,82 @@ public class DroidGap extends PhonegapActivity {
|
||||
this.callbackServer.sendJavascript(statement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return up to file part of url.
|
||||
* If url = http://server/page.html#abc, then return http://server/page.html
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
private String getUrlFile(String url) {
|
||||
int p1 = url.indexOf("#");
|
||||
int p2 = url.indexOf("?");
|
||||
if (p1 < 0) p1 = url.length();
|
||||
if (p2 < 0) p2 = url.length();
|
||||
int p3 = (p1 < p2) ? p1 : p2;
|
||||
return url.substring(0, p3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a new browser with the specified URL.
|
||||
*
|
||||
* NOTE: If usePhoneGap is set, only trusted PhoneGap URLs should be loaded,
|
||||
* since any PhoneGap API can be called by the loaded HTML page.
|
||||
*
|
||||
* @param url The url to load.
|
||||
* @param usePhoneGap Load url in PhoneGap webview.
|
||||
* @param clearPrev Clear the activity stack, so new app becomes top of stack
|
||||
* @param params DroidGap parameters for new app
|
||||
* @throws android.content.ActivityNotFoundException
|
||||
*/
|
||||
public void showWebPage(String url, boolean usePhoneGap, boolean clearPrev, HashMap<String, Object> params) throws android.content.ActivityNotFoundException {
|
||||
Intent intent = null;
|
||||
if (usePhoneGap) {
|
||||
intent = new Intent().setClass(this, com.phonegap.DroidGap.class);
|
||||
intent.putExtra("url", url);
|
||||
|
||||
// Add parameters
|
||||
if (params != null) {
|
||||
java.util.Set<Entry<String,Object>> s = params.entrySet();
|
||||
java.util.Iterator<Entry<String,Object>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,Object> entry = it.next();
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value == null) {
|
||||
}
|
||||
else if (value.getClass().equals(String.class)) {
|
||||
intent.putExtra(key, (String)value);
|
||||
}
|
||||
else if (value.getClass().equals(Boolean.class)) {
|
||||
intent.putExtra(key, (Boolean)value);
|
||||
}
|
||||
else if (value.getClass().equals(Integer.class)) {
|
||||
intent.putExtra(key, (Integer)value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
}
|
||||
this.startActivity(intent);
|
||||
|
||||
// Finish current activity
|
||||
if (clearPrev) {
|
||||
this.finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a hook for calling "alert" from javascript. Useful for
|
||||
* debugging your javascript.
|
||||
*/
|
||||
public class GapClient extends WebChromeClient {
|
||||
|
||||
private Context ctx;
|
||||
private DroidGap ctx;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -710,7 +810,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
* @param ctx
|
||||
*/
|
||||
public GapClient(Context ctx) {
|
||||
this.ctx = ctx;
|
||||
this.ctx = (DroidGap)ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -782,10 +882,17 @@ public class DroidGap extends PhonegapActivity {
|
||||
*/
|
||||
@Override
|
||||
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
|
||||
|
||||
// Security check to make sure any requests are coming from the page initially
|
||||
// loaded in webview and not another loaded in an iframe.
|
||||
boolean reqOk = false;
|
||||
if (url.indexOf(this.ctx.baseUrl) == 0) {
|
||||
reqOk = true;
|
||||
}
|
||||
|
||||
// Calling PluginManager.exec() to call a native service using
|
||||
// prompt(this.stringify(args), "gap:"+this.stringify([service, action, callbackId, true]));
|
||||
if (defaultValue != null && defaultValue.length() > 3 && defaultValue.substring(0, 4).equals("gap:")) {
|
||||
if (reqOk && defaultValue != null && defaultValue.length() > 3 && defaultValue.substring(0, 4).equals("gap:")) {
|
||||
JSONArray array;
|
||||
try {
|
||||
array = new JSONArray(defaultValue.substring(4));
|
||||
@ -801,13 +908,13 @@ public class DroidGap extends PhonegapActivity {
|
||||
}
|
||||
|
||||
// Polling for JavaScript messages
|
||||
else if (defaultValue.equals("gap_poll:")) {
|
||||
else if (reqOk && defaultValue.equals("gap_poll:")) {
|
||||
String r = callbackServer.getJavascript();
|
||||
result.confirm(r);
|
||||
}
|
||||
|
||||
// Calling into CallbackServer
|
||||
else if (defaultValue.equals("gap_callbackServer:")) {
|
||||
else if (reqOk && defaultValue.equals("gap_callbackServer:")) {
|
||||
String r = "";
|
||||
if (message.equals("usePolling")) {
|
||||
r = ""+callbackServer.usePolling();
|
||||
@ -1022,17 +1129,18 @@ public class DroidGap extends PhonegapActivity {
|
||||
// All else
|
||||
else {
|
||||
|
||||
int i = url.lastIndexOf('/');
|
||||
String newBaseUrl = url;
|
||||
if (i > 0) {
|
||||
newBaseUrl = url.substring(0, i);
|
||||
}
|
||||
|
||||
// If our app or file:, then load into our webview
|
||||
// NOTE: This replaces our app with new URL. When BACK is pressed,
|
||||
// our app is reloaded and restarted. All state is lost.
|
||||
if (this.ctx.loadInWebView || url.startsWith("file://") || this.ctx.baseUrl.equals(newBaseUrl)) {
|
||||
this.ctx.appView.loadUrl(url);
|
||||
if (this.ctx.loadInWebView || url.startsWith("file://") || url.indexOf(this.ctx.baseUrl) == 0) {
|
||||
try {
|
||||
HashMap<String, Object> params = new HashMap<String, Object>();
|
||||
params.put("loadingDialog", "");
|
||||
params.put("hideLoadingDialogOnPageLoad", true);
|
||||
this.ctx.showWebPage(url, true, false, params);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
System.out.println("Error loading url into DroidGap - "+url+":"+ e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// If not our application, let default viewer handle
|
||||
@ -1065,7 +1173,9 @@ public class DroidGap extends PhonegapActivity {
|
||||
// Try firing the onNativeReady event in JS. If it fails because the JS is
|
||||
// not loaded yet then just set a flag so that the onNativeReady can be fired
|
||||
// from the JS side when the JS gets to that code.
|
||||
appView.loadUrl("javascript:try{ PhoneGap.onNativeReady.fire();}catch(e){_nativeReady = true;}");
|
||||
if (!url.equals("about:blank")) {
|
||||
appView.loadUrl("javascript:try{ PhoneGap.onNativeReady.fire();}catch(e){_nativeReady = true;}");
|
||||
}
|
||||
|
||||
// Make app view visible
|
||||
appView.setVisibility(View.VISIBLE);
|
||||
@ -1081,6 +1191,13 @@ public class DroidGap extends PhonegapActivity {
|
||||
this.ctx.clearHistory = false;
|
||||
this.ctx.appView.clearHistory();
|
||||
}
|
||||
|
||||
// Shutdown if blank loaded
|
||||
if (url.equals("about:blank")) {
|
||||
if (this.ctx.callbackServer != null) {
|
||||
this.ctx.callbackServer.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1105,6 +1222,27 @@ public class DroidGap extends PhonegapActivity {
|
||||
// Handle error
|
||||
this.ctx.onReceivedError(errorCode, description, failingUrl);
|
||||
}
|
||||
|
||||
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
|
||||
|
||||
final String packageName = this.ctx.getPackageName();
|
||||
final PackageManager pm = this.ctx.getPackageManager();
|
||||
ApplicationInfo appInfo;
|
||||
try {
|
||||
appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
|
||||
if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
|
||||
// debug = true
|
||||
handler.proceed();
|
||||
return;
|
||||
} else {
|
||||
// debug = false
|
||||
super.onReceivedSslError(view, handler, error);
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
// When it doubt, lock it out!
|
||||
super.onReceivedSslError(view, handler, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1125,6 +1263,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
// If back key is bound, then send event to JavaScript
|
||||
if (this.bound) {
|
||||
this.appView.loadUrl("javascript:PhoneGap.fireEvent('backbutton');");
|
||||
return true;
|
||||
}
|
||||
|
||||
// If not bound
|
||||
@ -1133,6 +1272,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
// Go to previous page in webview if it is possible to go back
|
||||
if (this.appView.canGoBack()) {
|
||||
this.appView.goBack();
|
||||
return true;
|
||||
}
|
||||
|
||||
// If not, then invoke behavior of super class
|
||||
@ -1145,11 +1285,13 @@ public class DroidGap extends PhonegapActivity {
|
||||
// If menu key
|
||||
else if (keyCode == KeyEvent.KEYCODE_MENU) {
|
||||
this.appView.loadUrl("javascript:PhoneGap.fireEvent('menubutton');");
|
||||
return true;
|
||||
}
|
||||
|
||||
// If search key
|
||||
else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
|
||||
this.appView.loadUrl("javascript:PhoneGap.fireEvent('searchbutton');");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -1276,4 +1418,84 @@ public class DroidGap extends PhonegapActivity {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* We are providing this class to detect when the soft keyboard is shown
|
||||
* and hidden in the web view.
|
||||
*/
|
||||
class LinearLayoutSoftKeyboardDetect extends LinearLayout {
|
||||
|
||||
private static final String LOG_TAG = "SoftKeyboardDetect";
|
||||
|
||||
private int oldHeight = 0; // Need to save the old height as not to send redundant events
|
||||
private int oldWidth = 0; // Need to save old width for orientation change
|
||||
private int screenWidth = 0;
|
||||
private int screenHeight = 0;
|
||||
|
||||
public LinearLayoutSoftKeyboardDetect(Context context, int width, int height) {
|
||||
super(context);
|
||||
screenWidth = width;
|
||||
screenHeight = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Start listening to new measurement events. Fire events when the height
|
||||
* gets smaller fire a show keyboard event and when height gets bigger fire
|
||||
* a hide keyboard event.
|
||||
*
|
||||
* Note: We are using callbackServer.sendJavascript() instead of
|
||||
* this.appView.loadUrl() as changing the URL of the app would cause the
|
||||
* soft keyboard to go away.
|
||||
*
|
||||
* @param widthMeasureSpec
|
||||
* @param heightMeasureSpec
|
||||
*/
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
Log.d(LOG_TAG, "We are in our onMeasure method");
|
||||
|
||||
// Get the current height of the visible part of the screen.
|
||||
// This height will not included the status bar.
|
||||
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
|
||||
Log.d(LOG_TAG, "Old Height = " + oldHeight);
|
||||
Log.d(LOG_TAG, "Height = " + height);
|
||||
Log.d(LOG_TAG, "Old Width = " + oldWidth);
|
||||
Log.d(LOG_TAG, "Width = " + width);
|
||||
|
||||
|
||||
// If the oldHeight = 0 then this is the first measure event as the app starts up.
|
||||
// If oldHeight == height then we got a measurement change that doesn't affect us.
|
||||
if (oldHeight == 0 || oldHeight == height) {
|
||||
Log.d(LOG_TAG, "Ignore this event");
|
||||
}
|
||||
// Account for orientation change and ignore this event/Fire orientation change
|
||||
else if(screenHeight == width)
|
||||
{
|
||||
int tmp_var = screenHeight;
|
||||
screenHeight = screenWidth;
|
||||
screenWidth = tmp_var;
|
||||
Log.d(LOG_TAG, "Orientation Change");
|
||||
}
|
||||
// If the height as gotten bigger then we will assume the soft keyboard has
|
||||
// gone away.
|
||||
else if (height > oldHeight) {
|
||||
Log.d(LOG_TAG, "Throw hide keyboard event");
|
||||
callbackServer.sendJavascript("PhoneGap.fireEvent('hidekeyboard');");
|
||||
}
|
||||
// If the height as gotten smaller then we will assume the soft keyboard has
|
||||
// been displayed.
|
||||
else if (height < oldHeight) {
|
||||
Log.d(LOG_TAG, "Throw show keyboard event");
|
||||
callbackServer.sendJavascript("PhoneGap.fireEvent('showkeyboard');");
|
||||
}
|
||||
|
||||
// Update the old height for the next event
|
||||
oldHeight = height;
|
||||
oldWidth = width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ package com.phonegap;
|
||||
import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
@ -229,15 +230,16 @@ public class FileUtils extends Plugin {
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject resolveLocalFileSystemURI(String url) throws IOException, JSONException {
|
||||
String decoded = URLDecoder.decode(url, "UTF-8");
|
||||
// Test to see if this is a valid URL first
|
||||
@SuppressWarnings("unused")
|
||||
URL testUrl = new URL(url);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
URL testUrl = new URL(decoded);
|
||||
|
||||
File fp = null;
|
||||
if (url.startsWith("file://")) {
|
||||
fp = new File(url.substring(7, url.length()));
|
||||
if (decoded.startsWith("file://")) {
|
||||
fp = new File(decoded.substring(7, decoded.length()));
|
||||
} else {
|
||||
fp = new File(url);
|
||||
fp = new File(decoded);
|
||||
}
|
||||
if (!fp.exists()) {
|
||||
throw new FileNotFoundException();
|
||||
@ -411,7 +413,7 @@ public class FileUtils extends Plugin {
|
||||
}
|
||||
|
||||
// Check to make sure we are not copying the directory into itself
|
||||
if (destinationDir.getAbsolutePath().startsWith(srcDir.getAbsolutePath())) {
|
||||
if (isCopyOnItself(srcDir.getAbsolutePath(), destinationDir.getAbsolutePath())) {
|
||||
throw new InvalidModificationException("Can't copy itself into itself");
|
||||
}
|
||||
|
||||
@ -435,6 +437,26 @@ public class FileUtils extends Plugin {
|
||||
return getEntry(destinationDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the user attempted to copy an entry into its parent without changing its name,
|
||||
* or attempted to copy a directory into a directory that it contains directly or indirectly.
|
||||
*
|
||||
* @param srcDir
|
||||
* @param destinationDir
|
||||
* @return
|
||||
*/
|
||||
private boolean isCopyOnItself(String src, String dest) {
|
||||
|
||||
// This weird test is to determine if we are copying or moving a directory into itself.
|
||||
// Copy /sdcard/myDir to /sdcard/myDir-backup is okay but
|
||||
// Copy /sdcard/myDir to /sdcard/myDir/backup should thow an INVALID_MODIFICATION_ERR
|
||||
if (dest.startsWith(src) && dest.indexOf(File.separator, src.length()-1) != -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a file
|
||||
*
|
||||
@ -480,8 +502,8 @@ public class FileUtils extends Plugin {
|
||||
}
|
||||
|
||||
// Check to make sure we are not copying the directory into itself
|
||||
if (destinationDir.getAbsolutePath().startsWith(srcDir.getAbsolutePath())) {
|
||||
throw new InvalidModificationException("Can't copy itself into itself");
|
||||
if (isCopyOnItself(srcDir.getAbsolutePath(), destinationDir.getAbsolutePath())) {
|
||||
throw new InvalidModificationException("Can't move itself into itself");
|
||||
}
|
||||
|
||||
// If the destination directory already exists and is empty then delete it. This is according to spec.
|
||||
|
@ -11,7 +11,6 @@ import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
import com.phonegap.api.Plugin;
|
||||
@ -21,13 +20,13 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.*;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.util.Log;
|
||||
|
||||
public class NetworkManager extends Plugin {
|
||||
|
||||
public static int NOT_REACHABLE = 0;
|
||||
public static int NOT_REACHABLE = 0;
|
||||
public static int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
|
||||
public static int REACHABLE_VIA_WIFI_NETWORK = 2;
|
||||
|
||||
@ -46,24 +45,26 @@ public class NetworkManager extends Plugin {
|
||||
public static final String LTE = "lte";
|
||||
public static final String UMB = "umb";
|
||||
// return types
|
||||
public static final int TYPE_UNKNOWN = 0;
|
||||
public static final int TYPE_ETHERNET = 1;
|
||||
public static final int TYPE_WIFI = 2;
|
||||
public static final int TYPE_2G = 3;
|
||||
public static final int TYPE_3G = 4;
|
||||
public static final int TYPE_4G = 5;
|
||||
public static final int TYPE_NONE = 20;
|
||||
public static final String TYPE_UNKNOWN = "unknown";
|
||||
public static final String TYPE_ETHERNET = "ethernet";
|
||||
public static final String TYPE_WIFI = "wifi";
|
||||
public static final String TYPE_2G = "2g";
|
||||
public static final String TYPE_3G = "3g";
|
||||
public static final String TYPE_4G = "4g";
|
||||
public static final String TYPE_NONE = "none";
|
||||
|
||||
private static final String LOG_TAG = "NetworkManager";
|
||||
|
||||
private String connectionCallbackId;
|
||||
|
||||
ConnectivityManager sockMan;
|
||||
TelephonyManager telephonyManager;
|
||||
ConnectivityManager sockMan;
|
||||
BroadcastReceiver receiver;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public NetworkManager() {
|
||||
this.receiver = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,19 +76,23 @@ public class NetworkManager extends Plugin {
|
||||
public void setContext(PhonegapActivity ctx) {
|
||||
super.setContext(ctx);
|
||||
this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
this.telephonyManager = ((TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE));
|
||||
this.connectionCallbackId = null;
|
||||
|
||||
// We need to listen to connectivity events to update navigator.connection
|
||||
IntentFilter intentFilter = new IntentFilter() ;
|
||||
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
ctx.registerReceiver(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));
|
||||
}
|
||||
}, intentFilter);
|
||||
}
|
||||
if (this.receiver == null) {
|
||||
this.receiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));
|
||||
}
|
||||
};
|
||||
ctx.registerReceiver(this.receiver, intentFilter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
@ -135,6 +140,19 @@ public class NetworkManager extends Plugin {
|
||||
// All methods take a while, so always use async
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop network receiver.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
if (this.receiver != null) {
|
||||
try {
|
||||
this.ctx.unregisterReceiver(this.receiver);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
@ -147,11 +165,9 @@ public class NetworkManager extends Plugin {
|
||||
* @param info the current active network info
|
||||
* @return
|
||||
*/
|
||||
private void updateConnectionInfo(NetworkInfo info) {
|
||||
JSONObject connection = this.getConnectionInfo(info);
|
||||
|
||||
// send update to javascript "navigator.connection"
|
||||
sendUpdate(connection);
|
||||
private void updateConnectionInfo(NetworkInfo info) {
|
||||
// send update to javascript "navigator.network.connection"
|
||||
sendUpdate(this.getConnectionInfo(info));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -160,40 +176,18 @@ public class NetworkManager extends Plugin {
|
||||
* @param info the current active network info
|
||||
* @return a JSONObject that represents the network info
|
||||
*/
|
||||
private JSONObject getConnectionInfo(NetworkInfo info) {
|
||||
JSONObject connection = new JSONObject();
|
||||
|
||||
try {
|
||||
if (info != null) {
|
||||
// If we are not connected to any network set type to none
|
||||
if (!info.isConnected()) {
|
||||
connection.put("type", TYPE_NONE);
|
||||
connection.put("homeNW", null);
|
||||
connection.put("currentNW", null);
|
||||
}
|
||||
else {
|
||||
// If we are connected check which type
|
||||
// First off is wifi
|
||||
if (info.getTypeName().toLowerCase().equals(WIFI)) {
|
||||
connection.put("type", TYPE_WIFI);
|
||||
connection.put("homeNW", null);
|
||||
connection.put("currentNW", null);
|
||||
}
|
||||
// Otherwise it must be one of the mobile network protocols
|
||||
else {
|
||||
// Determine the correct type, 2G, 3G, 4G
|
||||
connection.put("type", getType(info));
|
||||
connection.put("homeNW", telephonyManager.getSimOperatorName());
|
||||
connection.put("currentNW", telephonyManager.getNetworkOperatorName());
|
||||
}
|
||||
}
|
||||
private String getConnectionInfo(NetworkInfo info) {
|
||||
String type = TYPE_NONE;
|
||||
if (info != null) {
|
||||
// If we are not connected to any network set type to none
|
||||
if (!info.isConnected()) {
|
||||
type = TYPE_NONE;
|
||||
}
|
||||
else {
|
||||
type = getType(info);
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
// this should never happen
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return connection;
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -201,8 +195,8 @@ public class NetworkManager extends Plugin {
|
||||
*
|
||||
* @param connection the network info to set as navigator.connection
|
||||
*/
|
||||
private void sendUpdate(JSONObject connection) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, connection);
|
||||
private void sendUpdate(String type) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, type);
|
||||
result.setKeepCallback(true);
|
||||
this.success(result, this.connectionCallbackId);
|
||||
}
|
||||
@ -213,11 +207,14 @@ public class NetworkManager extends Plugin {
|
||||
* @param info the network info so we can determine connection type.
|
||||
* @return the type of mobile network we are on
|
||||
*/
|
||||
private int getType(NetworkInfo info) {
|
||||
private String getType(NetworkInfo info) {
|
||||
if (info != null) {
|
||||
String type = info.getTypeName();
|
||||
|
||||
if (type.toLowerCase().equals(MOBILE)) {
|
||||
if (type.toLowerCase().equals(WIFI)) {
|
||||
return TYPE_WIFI;
|
||||
}
|
||||
else if (type.toLowerCase().equals(MOBILE)) {
|
||||
type = info.getSubtypeName();
|
||||
if (type.toLowerCase().equals(GSM) ||
|
||||
type.toLowerCase().equals(GPRS) ||
|
||||
@ -278,7 +275,7 @@ public class NetworkManager extends Plugin {
|
||||
public int isReachable(String uri, boolean isIpAddress) {
|
||||
int reachable = NOT_REACHABLE;
|
||||
|
||||
if (uri.indexOf("http://") == -1) {
|
||||
if (uri.indexOf("http://") == -1 && uri.indexOf("https://") == -1) {
|
||||
uri = "http://" + uri;
|
||||
}
|
||||
|
||||
@ -301,4 +298,4 @@ public class NetworkManager extends Plugin {
|
||||
|
||||
return reachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,13 +54,17 @@ public interface IPlugin {
|
||||
|
||||
/**
|
||||
* Called when the system is about to start resuming a previous activity.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
void onPause();
|
||||
void onPause(boolean multitasking);
|
||||
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
void onResume();
|
||||
void onResume(boolean multitasking);
|
||||
|
||||
/**
|
||||
* The final call you receive before your activity is destroyed.
|
||||
|
@ -64,14 +64,24 @@ public abstract class Plugin implements IPlugin {
|
||||
|
||||
/**
|
||||
* Called when the system is about to start resuming a previous activity.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
public void onPause() {
|
||||
public void onPause(boolean multitasking) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
public void onResume() {
|
||||
public void onResume(boolean multitasking) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity receives a new intent.
|
||||
*/
|
||||
public void onNewIntent(Intent intent) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,6 +13,7 @@ import java.util.Map.Entry;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
@ -229,27 +230,31 @@ public final class PluginManager {
|
||||
|
||||
/**
|
||||
* Called when the system is about to start resuming a previous activity.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
public void onPause() {
|
||||
public void onPause(boolean multitasking) {
|
||||
java.util.Set<Entry<String,Plugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,Plugin>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,Plugin> entry = it.next();
|
||||
Plugin plugin = entry.getValue();
|
||||
plugin.onPause();
|
||||
plugin.onPause(multitasking);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
public void onResume() {
|
||||
public void onResume(boolean multitasking) {
|
||||
java.util.Set<Entry<String,Plugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,Plugin>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,Plugin> entry = it.next();
|
||||
Plugin plugin = entry.getValue();
|
||||
plugin.onResume();
|
||||
plugin.onResume(multitasking);
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,4 +270,17 @@ public final class PluginManager {
|
||||
plugin.onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity receives a new intent.
|
||||
*/
|
||||
public void onNewIntent(Intent intent) {
|
||||
java.util.Set<Entry<String,Plugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,Plugin>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,Plugin> entry = it.next();
|
||||
Plugin plugin = entry.getValue();
|
||||
plugin.onNewIntent(intent);
|
||||
}
|
||||
}
|
||||
}
|
@ -88,7 +88,7 @@ class Classic
|
||||
|
||||
# copies stuff from src directory into the android project directory (@path)
|
||||
def copy_libs
|
||||
version = IO.read(File.join(@framework_dir, '../VERSION'))
|
||||
version = IO.read(File.join(@framework_dir, '../VERSION')).lstrip.rstrip
|
||||
framework_res_dir = File.join(@framework_dir, "res")
|
||||
app_res_dir = File.join(@path, "res")
|
||||
# copies in the jar
|
||||
|
@ -33,12 +33,19 @@ class Update
|
||||
# TODO need to allow for www import inc icon
|
||||
def copy_libs
|
||||
puts "Copying over libraries and assets..."
|
||||
|
||||
FileUtils.mkdir_p File.join(@path, "libs")
|
||||
FileUtils.cp File.join(@framework_dir, "phonegap.jar"), File.join(@path, "libs")
|
||||
version = IO.read(File.join(@framework_dir, '../VERSION'))
|
||||
|
||||
FileUtils.mkdir_p File.join(@path, "assets", "www")
|
||||
FileUtils.cp File.join(@framework_dir, "assets", "www", "phonegap.js"), File.join(@path, "assets", "www")
|
||||
FileUtils.cp File.join(@framework_dir, "phonegap.#{ version }.jar"), File.join(@path, "libs")
|
||||
|
||||
# concat JS and put into www folder. this can be overridden in the config.xml via @app_js_dir
|
||||
js_dir = File.join(@framework_dir, "assets", "js")
|
||||
phonegapjs = IO.read(File.join(js_dir, 'phonegap.js.base'))
|
||||
Dir.new(js_dir).entries.each do |script|
|
||||
next if script[0].chr == "." or script == "phonegap.js.base"
|
||||
phonegapjs << IO.read(File.join(js_dir, script))
|
||||
phonegapjs << "\n\n"
|
||||
end
|
||||
File.open(File.join(@path, "assets", "www", "phonegap.#{ version }.js"), 'w') {|f| f.write(phonegapjs) }
|
||||
end
|
||||
#
|
||||
end
|
Loading…
Reference in New Issue
Block a user