Merge "HIDD: Address API Review concerns"
diff --git a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothA2dpFacade.java b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothA2dpFacade.java
index 932bcbd..bc5f54c 100644
--- a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothA2dpFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothA2dpFacade.java
@@ -140,8 +140,8 @@
if (sA2dpProfile == null) {
return false;
}
- BluetoothDevice mDevice = BluetoothFacade.getDevice(
- BluetoothFacade.DiscoveredDevices, deviceID);
+ BluetoothDevice mDevice =
+ BluetoothFacade.getDevice(mBluetoothAdapter.getBondedDevices(), deviceID);
Log.d("Connecting to device " + mDevice.getAliasName());
return a2dpConnect(mDevice);
}
diff --git a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHspFacade.java b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHspFacade.java
index 0af2b9c..5870674 100644
--- a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHspFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHspFacade.java
@@ -100,7 +100,8 @@
throws Exception {
if (sHspProfile == null)
return false;
- BluetoothDevice mDevice = BluetoothFacade.getDevice(BluetoothFacade.DiscoveredDevices, device);
+ BluetoothDevice mDevice =
+ BluetoothFacade.getDevice(mBluetoothAdapter.getBondedDevices(), device);
Log.d("Connecting to device " + mDevice.getAliasName());
return hspConnect(mDevice);
}
@@ -244,7 +245,71 @@
return sHspProfile.isAudioConnected(device);
}
+ /**
+ * Start voice recognition. Send BVRA command.
+ *
+ * @param deviceAddress the Bluetooth MAC address of remote device
+ * @return True if started successfully, False otherwise.
+ */
+ @Rpc(description = "Start Voice Recognition.")
+ public Boolean bluetoothHspStartVoiceRecognition(
+ @RpcParameter(name = "deviceAddress", description = "MAC address of a bluetooth device.")
+ String deviceAddress) throws Exception {
+ BluetoothDevice device = BluetoothFacade.getDevice(sHspProfile.getConnectedDevices(), deviceAddress);
+ return sHspProfile.startVoiceRecognition(device);
+ }
+
+ /**
+ * Stop voice recognition. Send BVRA command.
+ *
+ * @param deviceAddress the Bluetooth MAC address of remote device
+ * @return True if stopped successfully, False otherwise.
+ */
+ @Rpc(description = "Stop Voice Recognition.")
+ public Boolean bluetoothHspStopVoiceRecognition(
+ @RpcParameter(name = "deviceAddress", description = "MAC address of a bluetooth device.")
+ String deviceAddress) throws Exception {
+ BluetoothDevice device = BluetoothFacade.getDevice(sHspProfile.getConnectedDevices(), deviceAddress);
+ return sHspProfile.stopVoiceRecognition(device);
+ }
+
+ /**
+ * Determine whether in-band ringtone is enabled or not.
+ *
+ * @return True if enabled, False otherwise.
+ */
+ @Rpc(description = "In-band ringtone enabled check.")
+ public Boolean bluetoothHspIsInbandRingingEnabled() {
+ return sHspProfile.isInbandRingingEnabled();
+ }
+
+ /**
+ * Send a CLCC response from Sl4a (experimental).
+ *
+ * @param index the index of the call
+ * @param direction the direction of the call
+ * @param status the status of the call
+ * @param mode the mode
+ * @param mpty the mpty value
+ * @param number the phone number
+ * @param type the type
+ * @return True if stopped successfully, False otherwise.
+ */
+ @Rpc(description = "Send generic clcc response.")
+ public void bluetoothHspClccResponse(
+ @RpcParameter(name = "index", description = "") Integer index,
+ @RpcParameter(name = "direction", description = "") Integer direction,
+ @RpcParameter(name = "status", description = "") Integer status,
+ @RpcParameter(name = "mode", description = "") Integer mode,
+ @RpcParameter(name = "mpty", description = "") Boolean mpty,
+ @RpcParameter(name = "number", description = "") String number,
+ @RpcParameter(name = "type", description = "") Integer type
+ ) {
+ sHspProfile.clccResponse(index, direction, status, mode, mpty, number, type);
+ }
+
@Override
public void shutdown() {
}
}
+
diff --git a/Common/src/com/googlecode/android_scripting/facade/telephony/SmsFacade.java b/Common/src/com/googlecode/android_scripting/facade/telephony/SmsFacade.java
index 178f597..08c5cf0 100644
--- a/Common/src/com/googlecode/android_scripting/facade/telephony/SmsFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/telephony/SmsFacade.java
@@ -27,7 +27,6 @@
import android.net.Uri;
import android.os.Bundle;
import android.provider.Telephony.Sms.Intents;
-import android.provider.Telephony.Mms;
import android.telephony.SmsCbCmasInfo;
import android.telephony.SmsCbEtwsInfo;
import android.telephony.SmsCbMessage;
@@ -582,7 +581,7 @@
event.putString("Type", "NewWapPushReceived");
mEventFacade.postEvent(TelephonyConstants.EventWapPushReceived, event);
}
- if (Intents.DATA_SMS_RECEIVED_ACTION.equals(action)) {
+ else if (Intents.DATA_SMS_RECEIVED_ACTION.equals(action)) {
Log.d("New Data SMS Received");
Bundle event = new Bundle();
event.putString("Type", "NewDataSMSReceived");
diff --git a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyConstants.java b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyConstants.java
index 4efe857..88f6294 100644
--- a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyConstants.java
+++ b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyConstants.java
@@ -347,6 +347,7 @@
public static final String EventSignalStrengthChanged = "SignalStrengthChanged";
public static final String EventVolteServiceStateChanged = "VolteServiceStateChanged";
public static final String EventMessageWaitingIndicatorChanged = "MessageWaitingIndicatorChanged";
+ public static final String EventPhysicalChannelConfigChanged = "PhysicalChannelConfigChanged";
/**
* Constants for OnStartTetheringCallback
@@ -420,6 +421,9 @@
public static final String SYSTEM_ID = "systemId";
public static final String SUBSCRIPTION_ID = "subscriptionId";
public static final String SERVICE_STATE = "serviceState";
+ public static final String CHANNEL_NUMBER = "channelNumber";
+ public static final String CELL_BANDWIDTHS = "cellBandwidths";
+ public static final String DUPLEX_MODE = "duplexMode";
}
public static class MessageWaitingIndicatorContainer {
@@ -429,4 +433,10 @@
public static class VoLteServiceStateContainer {
public static final String SRVCC_STATE = "srvccState";
}
+
+ public static class PhysicalChannelConfigContainer {
+ public static final String CONFIGS = "configs";
+ public static final String CELL_BANDWIDTH_DOWNLINK = "cellBandwidthDownlink";
+ public static final String CONNECTION_STATUS = "cellConnectionStatus";
+ }
}
diff --git a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyEvents.java b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyEvents.java
index d4e234e..982d9ef 100644
--- a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyEvents.java
+++ b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyEvents.java
@@ -16,13 +16,19 @@
package com.googlecode.android_scripting.facade.telephony;
-import org.json.JSONException;
-import org.json.JSONObject;
import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseCallState;
import android.telephony.ServiceState;
+
import com.googlecode.android_scripting.jsonrpc.JsonSerializable;
+import java.util.List;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
public class TelephonyEvents {
public static class CallStateEvent implements JsonSerializable {
@@ -266,13 +272,24 @@
mServiceState.isEmergencyOnly());
serviceState.put(
TelephonyConstants.ServiceStateContainer.NETWORK_ID,
- mServiceState.getNetworkId());
+ mServiceState.getCdmaNetworkId());
serviceState.put(
TelephonyConstants.ServiceStateContainer.SYSTEM_ID,
- mServiceState.getSystemId());
+ mServiceState.getCdmaSystemId());
serviceState.put(
TelephonyConstants.ServiceStateContainer.SERVICE_STATE,
mServiceStateString);
+ serviceState.put(
+ TelephonyConstants.ServiceStateContainer.CHANNEL_NUMBER,
+ mServiceState.getChannelNumber());
+ serviceState.put(
+ TelephonyConstants.ServiceStateContainer.CELL_BANDWIDTHS,
+ mServiceState.getCellBandwidths() != null
+ ? new JSONArray(mServiceState.getCellBandwidths())
+ : JSONObject.NULL);
+ serviceState.put(
+ TelephonyConstants.ServiceStateContainer.DUPLEX_MODE,
+ mServiceState.getDuplexMode());
return serviceState;
}
@@ -299,4 +316,32 @@
return messageWaitingIndicator;
}
}
+
+ public static class PhysicalChannelConfigChangedEvent implements JsonSerializable {
+ private final List<PhysicalChannelConfig> mConfigs;
+
+ PhysicalChannelConfigChangedEvent(List<PhysicalChannelConfig> configs) {
+ mConfigs = configs;
+ }
+
+ List<PhysicalChannelConfig> getConfigs() {
+ return mConfigs;
+ }
+
+ public JSONObject toJSON() throws JSONException {
+ JSONArray jsonConfigs = new JSONArray();
+ for(PhysicalChannelConfig c : mConfigs) {
+ JSONObject cfg = new JSONObject();
+ cfg.put(
+ TelephonyConstants.PhysicalChannelConfigContainer.CELL_BANDWIDTH_DOWNLINK,
+ c.getCellBandwidthDownlink());
+ cfg.put(
+ TelephonyConstants.PhysicalChannelConfigContainer.CONNECTION_STATUS,
+ c.getConnectionStatus());
+ jsonConfigs.put(cfg);
+ }
+ return new JSONObject().put(
+ TelephonyConstants.PhysicalChannelConfigContainer.CONFIGS, jsonConfigs);
+ }
+ }
}
diff --git a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyManagerFacade.java b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyManagerFacade.java
index 325a354..0f7d5b1 100644
--- a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyManagerFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyManagerFacade.java
@@ -582,6 +582,24 @@
return mTelephonyManager.getCellLocation();
}
+ /**
+ * Returns carrier id of the current subscription.
+ * @return Carrier id of the current subscription.
+ */
+ @Rpc(description = "Returns the numeric CarrierId for current subscription")
+ public int telephonyGetSimCarrierId() {
+ return mTelephonyManager.getSimCarrierId();
+ }
+
+ /**
+ * Returns carrier id name of the current subscription.
+ * @return Carrier id name of the current subscription
+ */
+ @Rpc(description = "Returns Carrier Name for current subscription")
+ public CharSequence telephonyGetSimCarrierIdName() {
+ return mTelephonyManager.getSimCarrierIdName();
+ }
+
@Rpc(description = "Returns the numeric name (MCC+MNC) of registered operator." +
"for default subscription ID")
public String telephonyGetNetworkOperator() {
diff --git a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyStateListeners.java b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyStateListeners.java
index f4d692a..b9fe705 100644
--- a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyStateListeners.java
+++ b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyStateListeners.java
@@ -21,6 +21,7 @@
import android.telephony.CellInfo;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.PhoneStateListener;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseCallState;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -328,4 +329,33 @@
}
}
+ public static class PhysicalChannelConfigurationChangeListener extends PhoneStateListener {
+
+ private final EventFacade mEventFacade;
+ public List<PhysicalChannelConfig> mConfigs;
+ public static final int sListeningStates =
+ PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION;
+ public PhysicalChannelConfigurationChangeListener(EventFacade ef) {
+ super();
+ mEventFacade = ef;
+ }
+
+ public PhysicalChannelConfigurationChangeListener(EventFacade ef, int subId) {
+ super(subId);
+ mEventFacade = ef;
+ }
+
+ public PhysicalChannelConfigurationChangeListener(
+ EventFacade ef, int subId, Looper looper) {
+ super(subId, looper);
+ mEventFacade = ef;
+ }
+
+ @Override
+ public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> config) {
+ mEventFacade.postEvent(
+ TelephonyConstants.EventPhysicalChannelConfigChanged, config);
+ }
+ }
+
}
diff --git a/Common/src/com/googlecode/android_scripting/webcam/JpegProvider.java b/Common/src/com/googlecode/android_scripting/facade/webcam/JpegProvider.java
similarity index 88%
rename from Common/src/com/googlecode/android_scripting/webcam/JpegProvider.java
rename to Common/src/com/googlecode/android_scripting/facade/webcam/JpegProvider.java
index 883af81..6f73371 100644
--- a/Common/src/com/googlecode/android_scripting/webcam/JpegProvider.java
+++ b/Common/src/com/googlecode/android_scripting/facade/webcam/JpegProvider.java
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-package com.googlecode.android_scripting.webcam;
+package com.googlecode.android_scripting.facade.webcam;
interface JpegProvider {
- public byte[] getJpeg();
-}
\ No newline at end of file
+ byte[] getJpeg();
+}
diff --git a/Common/src/com/googlecode/android_scripting/facade/webcam/MjpegServer.java b/Common/src/com/googlecode/android_scripting/facade/webcam/MjpegServer.java
new file mode 100644
index 0000000..af3d9e6
--- /dev/null
+++ b/Common/src/com/googlecode/android_scripting/facade/webcam/MjpegServer.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+
+package com.googlecode.android_scripting.facade.webcam;
+
+import com.googlecode.android_scripting.Log;
+import com.googlecode.android_scripting.SimpleServer;
+
+import java.io.BufferedReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.Socket;
+
+class MjpegServer extends SimpleServer {
+
+ private final JpegProvider mProvider;
+
+ MjpegServer(JpegProvider provider) {
+ mProvider = provider;
+ }
+
+ @Override
+ protected void handleConnection(Socket socket) throws Exception {
+ Log.d("handle Mjpeg connection");
+ byte[] data = mProvider.getJpeg();
+ if (data == null) {
+ return;
+ }
+ OutputStream outputStream = socket.getOutputStream();
+ outputStream.write((
+ "HTTP/1.0 200 OK\r\n"
+ + "Server: SL4A\r\n"
+ + "Connection: close\r\n"
+ + "Max-Age: 0\r\n"
+ + "Expires: 0\r\n"
+ + "Cache-Control: no-cache, private\r\n"
+ + "Pragma: no-cache\r\n"
+ + "Content-Type: multipart/x-mixed-replace; "
+ + "boundary=--BoundaryString\r\n\r\n").getBytes());
+ while (true) {
+ data = mProvider.getJpeg();
+ if (data == null) {
+ return;
+ }
+ outputStream.write("--BoundaryString\r\n".getBytes());
+ outputStream.write("Content-type: image/jpg\r\n".getBytes());
+ outputStream.write(("Content-Length: " + data.length + "\r\n\r\n").getBytes());
+ outputStream.write(data);
+ outputStream.write("\r\n\r\n".getBytes());
+ outputStream.flush();
+ }
+ }
+
+ @Override
+ protected void handleRPCConnection(Socket sock,
+ Integer uid,
+ BufferedReader reader,
+ PrintWriter writer)
+ throws Exception {
+ }
+}
diff --git a/Common/src/com/googlecode/android_scripting/facade/webcam/WebCamFacade.java b/Common/src/com/googlecode/android_scripting/facade/webcam/WebCamFacade.java
new file mode 100644
index 0000000..bc7bbbf
--- /dev/null
+++ b/Common/src/com/googlecode/android_scripting/facade/webcam/WebCamFacade.java
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+
+package com.googlecode.android_scripting.facade.webcam;
+
+import android.app.Service;
+import android.graphics.ImageFormat;
+import android.graphics.Rect;
+import android.graphics.YuvImage;
+import android.hardware.Camera;
+import android.hardware.Camera.Parameters;
+import android.hardware.Camera.PreviewCallback;
+import android.hardware.Camera.Size;
+import android.util.Base64;
+import android.view.SurfaceHolder;
+import android.view.SurfaceHolder.Callback;
+import android.view.SurfaceView;
+import android.view.WindowManager;
+
+import com.googlecode.android_scripting.BaseApplication;
+import com.googlecode.android_scripting.FutureActivityTaskExecutor;
+import com.googlecode.android_scripting.Log;
+import com.googlecode.android_scripting.SimpleServer.SimpleServerObserver;
+import com.googlecode.android_scripting.SingleThreadExecutor;
+import com.googlecode.android_scripting.facade.EventFacade;
+import com.googlecode.android_scripting.facade.FacadeManager;
+import com.googlecode.android_scripting.future.FutureActivityTask;
+import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
+import com.googlecode.android_scripting.rpc.Rpc;
+import com.googlecode.android_scripting.rpc.RpcDefault;
+import com.googlecode.android_scripting.rpc.RpcOptional;
+import com.googlecode.android_scripting.rpc.RpcParameter;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+
+/**
+ * Manages access to camera streaming.
+ * <br>
+ * <h3>Usage Notes</h3>
+ * <br><b>webCamStart</b> and <b>webCamStop</b> are used to start and stop an Mpeg stream on a
+ * given port. <b>webcamAdjustQuality</b> is used to ajust the quality of the streaming video.
+ * <br><b>cameraStartPreview</b> is used to get access to the camera preview screen. It will
+ * generate "preview" events as images become available.
+ * <br>The preview has two modes: data or file. If you pass a non-blank, writable file path to
+ * the <b>cameraStartPreview</b> it will store jpg images in that folder.
+ * It is up to the caller to clean up these files after the fact. If no file element is provided,
+ * the event will include the image data as a base64 encoded string.
+ * <h3>Event details</h3>
+ * <br>The data element of the preview event will be a map, with the following elements defined.
+ * <ul>
+ * <li><b>format</b> - currently always "jpeg"
+ * <li><b>width</b> - image width (in pixels)
+ * <li><b>height</b> - image height (in pixels)
+ * <li><b>quality</b> - JPEG quality. Number from 1-100
+ * <li><b>filename</b> - Name of file where image was saved. Only relevant if filepath defined.
+ * <li><b>error</b> - included if there was an IOException saving file, ie, disk full or path write
+ * protected.
+ * <li><b>encoding</b> - Data encoding. If filepath defined, will be "file" otherwise "base64"
+ * <li><b>data</b> - Base64 encoded image data.
+ * </ul>
+ * <br>Note that "filename", "error" and "data" are mutual exclusive.
+ * <br>
+ * <br>The webcam and preview modes use the same resources, so you can't use them both at the same
+ * time. Stop one mode before starting the other.
+ */
+public class WebCamFacade extends RpcReceiver {
+
+ private final Service mService;
+ private final Executor mJpegCompressionExecutor = new SingleThreadExecutor();
+ private final ByteArrayOutputStream mJpegCompressionBuffer = new ByteArrayOutputStream();
+
+ private volatile byte[] mJpegData;
+
+ private CountDownLatch mJpegDataReady;
+ private boolean mStreaming;
+ private int mPreviewHeight;
+ private int mPreviewWidth;
+ private int mJpegQuality;
+
+ private MjpegServer mJpegServer;
+ private FutureActivityTask<SurfaceHolder> mPreviewTask;
+ private Camera mCamera;
+ private Parameters mParameters;
+ private final EventFacade mEventFacade;
+ private boolean mPreview;
+ private File mDest;
+
+ private final PreviewCallback mPreviewCallback = new PreviewCallback() {
+ @Override
+ public void onPreviewFrame(final byte[] data, final Camera camera) {
+ mJpegCompressionExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mJpegData = compressYuvToJpeg(data);
+ mJpegDataReady.countDown();
+ if (mStreaming) {
+ camera.setOneShotPreviewCallback(mPreviewCallback);
+ }
+ }
+ });
+ }
+ };
+
+ private final PreviewCallback mPreviewEvent = new PreviewCallback() {
+ @Override
+ public void onPreviewFrame(final byte[] data, final Camera camera) {
+ mJpegCompressionExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mJpegData = compressYuvToJpeg(data);
+ Map<String, Object> map = new HashMap<String, Object>();
+ map.put("format", "jpeg");
+ map.put("width", mPreviewWidth);
+ map.put("height", mPreviewHeight);
+ map.put("quality", mJpegQuality);
+ if (mDest != null) {
+ try {
+ File dest = File.createTempFile("prv", ".jpg", mDest);
+ OutputStream output = new FileOutputStream(dest);
+ output.write(mJpegData);
+ output.close();
+ map.put("encoding", "file");
+ map.put("filename", dest.toString());
+ } catch (IOException e) {
+ map.put("error", e.toString());
+ }
+ } else {
+ map.put("encoding", "Base64");
+ map.put("data", Base64.encodeToString(mJpegData, Base64.DEFAULT));
+ }
+ mEventFacade.postEvent("preview", map);
+ if (mPreview) {
+ camera.setOneShotPreviewCallback(mPreviewEvent);
+ }
+ }
+ });
+ }
+ };
+
+ public WebCamFacade(FacadeManager manager) {
+ super(manager);
+ mService = manager.getService();
+ mJpegDataReady = new CountDownLatch(1);
+ mEventFacade = manager.getReceiver(EventFacade.class);
+ }
+
+ private byte[] compressYuvToJpeg(final byte[] yuvData) {
+ mJpegCompressionBuffer.reset();
+ YuvImage yuvImage =
+ new YuvImage(yuvData, ImageFormat.NV21, mPreviewWidth, mPreviewHeight, null);
+ yuvImage.compressToJpeg(new Rect(0, 0, mPreviewWidth, mPreviewHeight), mJpegQuality,
+ mJpegCompressionBuffer);
+ return mJpegCompressionBuffer.toByteArray();
+ }
+
+ /**
+ * Starts an MJPEG stream and returns a Tuple of address and port for the stream.
+ * @param resolutionLevel increasing this number provides higher resolution
+ * @param jpegQuality a number from 0-100
+ * @param port If port is specified, the webcam service will bind to port, otherwise it will
+ * pick any available port.
+ * @return a Tuple of address and port for the stream
+ * @throws Exception upon failure to open the webcam or start the server
+ */
+ @Rpc(description = "Starts an MJPEG stream and returns a Tuple of address and port "
+ + "for the stream.")
+ public InetSocketAddress webcamStart(
+ @RpcParameter(name = "resolutionLevel",
+ description = "increasing this number provides higher resolution")
+ @RpcDefault("0")
+ Integer resolutionLevel,
+ @RpcParameter(name = "jpegQuality", description = "a number from 0-100")
+ @RpcDefault("20")
+ Integer jpegQuality,
+ @RpcParameter(name = "port",
+ description = "If port is specified, the webcam service will bind to "
+ + "port, otherwise it will pick any available port.")
+ @RpcDefault("0")
+ Integer port)
+ throws Exception {
+ try {
+ openCamera(resolutionLevel, jpegQuality);
+ return startServer(port);
+ } catch (Exception e) {
+ webcamStop();
+ throw e;
+ }
+ }
+
+ private InetSocketAddress startServer(Integer port) {
+ mJpegServer = new MjpegServer(new JpegProvider() {
+ @Override
+ public byte[] getJpeg() {
+ try {
+ mJpegDataReady.await();
+ } catch (InterruptedException e) {
+ Log.e(e);
+ }
+ return mJpegData;
+ }
+ });
+ mJpegServer.addObserver(new SimpleServerObserver() {
+ @Override
+ public void onDisconnect() {
+ if (mJpegServer.getNumberOfConnections() == 0 && mStreaming) {
+ stopStream();
+ }
+ }
+
+ @Override
+ public void onConnect() {
+ if (!mStreaming) {
+ startStream();
+ }
+ }
+ });
+ return mJpegServer.startPublic(port);
+ }
+
+ private void stopServer() {
+ if (mJpegServer != null) {
+ mJpegServer.shutdown();
+ mJpegServer = null;
+ }
+ }
+
+ /**
+ * Adjusts the quality of the webcam stream while it is running.
+ * @param resolutionLevel increasing this number provides higher resolution
+ * @param jpegQuality a number from 0-100
+ * @throws Exception if the webcam is not streaming or the camera is unable to open
+ */
+ @Rpc(description = "Adjusts the quality of the webcam stream while it is running.")
+ public void webcamAdjustQuality(
+ @RpcParameter(name = "resolutionLevel",
+ description = "increasing this number provides higher resolution")
+ @RpcDefault("0")
+ Integer resolutionLevel,
+ @RpcParameter(name = "jpegQuality", description = "a number from 0-100")
+ @RpcDefault("20")
+ Integer jpegQuality)
+ throws Exception {
+ if (!mStreaming) {
+ throw new IllegalStateException("Webcam not streaming.");
+ }
+ stopStream();
+ releaseCamera();
+ openCamera(resolutionLevel, jpegQuality);
+ startStream();
+ }
+
+ private void openCamera(Integer resolutionLevel, Integer jpegQuality) throws IOException,
+ InterruptedException {
+ mCamera = Camera.open();
+ mParameters = mCamera.getParameters();
+ mParameters.setPictureFormat(ImageFormat.JPEG);
+ mParameters.setPreviewFormat(ImageFormat.JPEG);
+ List<Size> supportedPreviewSizes = mParameters.getSupportedPreviewSizes();
+ Collections.sort(supportedPreviewSizes, new Comparator<Size>() {
+ @Override
+ public int compare(Size o1, Size o2) {
+ return o1.width - o2.width;
+ }
+ });
+ Size previewSize =
+ supportedPreviewSizes.get(
+ Math.min(resolutionLevel, supportedPreviewSizes.size() - 1));
+ mPreviewHeight = previewSize.height;
+ mPreviewWidth = previewSize.width;
+ mParameters.setPreviewSize(mPreviewWidth, mPreviewHeight);
+ mJpegQuality = Math.min(Math.max(jpegQuality, 0), 100);
+ mCamera.setParameters(mParameters);
+ // TODO(damonkohler): Rotate image based on orientation.
+ mPreviewTask = createPreviewTask();
+ mCamera.startPreview();
+ }
+
+ private void startStream() {
+ mStreaming = true;
+ mCamera.setOneShotPreviewCallback(mPreviewCallback);
+ }
+
+ private void stopStream() {
+ mJpegDataReady = new CountDownLatch(1);
+ mStreaming = false;
+ if (mPreviewTask != null) {
+ mPreviewTask.finish();
+ mPreviewTask = null;
+ }
+ }
+
+ private void releaseCamera() {
+ if (mCamera != null) {
+ mCamera.release();
+ mCamera = null;
+ }
+ mParameters = null;
+ }
+
+ /** Stops the webcam stream. */
+ @Rpc(description = "Stops the webcam stream.")
+ public void webcamStop() {
+ stopServer();
+ stopStream();
+ releaseCamera();
+ }
+
+ private FutureActivityTask<SurfaceHolder> createPreviewTask() throws IOException,
+ InterruptedException {
+ FutureActivityTask<SurfaceHolder> task = new FutureActivityTask<SurfaceHolder>() {
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ final SurfaceView view = new SurfaceView(getActivity());
+ getActivity().setContentView(view);
+ getActivity().getWindow().setSoftInputMode(
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
+ //view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+ view.getHolder().addCallback(new Callback() {
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ setResult(view.getHolder());
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder,
+ int format,
+ int width,
+ int height) {
+ }
+ });
+ }
+ };
+ FutureActivityTaskExecutor taskExecutor =
+ ((BaseApplication) mService.getApplication()).getTaskExecutor();
+ taskExecutor.execute(task);
+ mCamera.setPreviewDisplay(task.getResult());
+ return task;
+ }
+
+ /**
+ * Start Preview Mode. Throws 'preview' events.
+ * @param resolutionLevel increasing this number provides higher resolution
+ * @param jpegQuality a number from 0-100
+ * @param filepath the path to store jpeg files
+ * @return a Tuple of address and port for the stream
+ * @throws InterruptedException if interrupted while trying to open the camera
+ */
+ @Rpc(description = "Start Preview Mode. Throws 'preview' events.",
+ returns = "True if successful")
+ public boolean cameraStartPreview(
+ @RpcParameter(name = "resolutionLevel",
+ description = "increasing this number provides higher resolution")
+ @RpcDefault("0")
+ Integer resolutionLevel,
+ @RpcParameter(name = "jpegQuality", description = "a number from 0-100")
+ @RpcDefault("20")
+ Integer jpegQuality,
+ @RpcParameter(name = "filepath", description = "Path to store jpeg files.")
+ @RpcOptional
+ String filepath)
+ throws InterruptedException {
+ mDest = null;
+ if (filepath != null && (filepath.length() > 0)) {
+ mDest = new File(filepath);
+ if (!mDest.exists()) mDest.mkdirs();
+ if (!(mDest.isDirectory() && mDest.canWrite())) {
+ return false;
+ }
+ }
+
+ try {
+ openCamera(resolutionLevel, jpegQuality);
+ } catch (IOException e) {
+ Log.e(e);
+ return false;
+ }
+ startPreview();
+ return true;
+ }
+
+ /** Stops the preview mode. */
+ @Rpc(description = "Stop the preview mode.")
+ public void cameraStopPreview() {
+ stopPreview();
+ }
+
+ private void startPreview() {
+ mPreview = true;
+ mCamera.setOneShotPreviewCallback(mPreviewEvent);
+ }
+
+ private void stopPreview() {
+ mPreview = false;
+ if (mPreviewTask != null) {
+ mPreviewTask.finish();
+ mPreviewTask = null;
+ }
+ releaseCamera();
+ }
+
+ @Override
+ public void shutdown() {
+ mPreview = false;
+ webcamStop();
+ }
+}
diff --git a/Common/src/com/googlecode/android_scripting/facade/wifi/HttpFacade.java b/Common/src/com/googlecode/android_scripting/facade/wifi/HttpFacade.java
index f8fbd20..6f173b5 100644
--- a/Common/src/com/googlecode/android_scripting/facade/wifi/HttpFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/wifi/HttpFacade.java
@@ -100,7 +100,7 @@
*/
private HttpURLConnection httpRequest(String url) throws IOException {
URL targetURL = new URL(url);
- HttpURLConnection urlConnection = null;
+ HttpURLConnection urlConnection;
try {
urlConnection = (HttpURLConnection) targetURL.openConnection();
urlConnection.connect();
@@ -110,6 +110,7 @@
} catch (IOException e) {
Log.e("Failed to open a connection to " + url);
Log.e(e.toString());
+ throw e;
}
return urlConnection;
}
diff --git a/Common/src/com/googlecode/android_scripting/webcam/MjpegServer.java b/Common/src/com/googlecode/android_scripting/webcam/MjpegServer.java
deleted file mode 100644
index 690f0dd..0000000
--- a/Common/src/com/googlecode/android_scripting/webcam/MjpegServer.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed 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.
- */
-
-package com.googlecode.android_scripting.webcam;
-
-import java.io.BufferedReader;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.net.Socket;
-import com.googlecode.android_scripting.Log;
-import com.googlecode.android_scripting.SimpleServer;
-
-class MjpegServer extends SimpleServer {
-
- private final JpegProvider mProvider;
-
- public MjpegServer(JpegProvider provider) {
- mProvider = provider;
- }
-
- @Override
- protected void handleConnection(Socket socket) throws Exception {
- Log.d("handle Mjpeg connection");
- byte[] data = mProvider.getJpeg();
- if (data == null) {
- return;
- }
- OutputStream outputStream = socket.getOutputStream();
- outputStream.write((
- "HTTP/1.0 200 OK\r\n" +
- "Server: SL4A\r\n" +
- "Connection: close\r\n" +
- "Max-Age: 0\r\n" +
- "Expires: 0\r\n" +
- "Cache-Control: no-cache, private\r\n" +
- "Pragma: no-cache\r\n" +
- "Content-Type: multipart/x-mixed-replace; boundary=--BoundaryString\r\n\r\n").getBytes());
- while (true) {
- data = mProvider.getJpeg();
- if (data == null) {
- return;
- }
- outputStream.write("--BoundaryString\r\n".getBytes());
- outputStream.write("Content-type: image/jpg\r\n".getBytes());
- outputStream.write(("Content-Length: " + data.length + "\r\n\r\n").getBytes());
- outputStream.write(data);
- outputStream.write("\r\n\r\n".getBytes());
- outputStream.flush();
- }
- }
-
- @Override
- protected void handleRPCConnection(Socket sock, Integer UID, BufferedReader reader, PrintWriter writer)
- throws Exception {
- }
-}
diff --git a/Common/src/com/googlecode/android_scripting/webcam/WebCamFacade.java b/Common/src/com/googlecode/android_scripting/webcam/WebCamFacade.java
deleted file mode 100644
index a61681a..0000000
--- a/Common/src/com/googlecode/android_scripting/webcam/WebCamFacade.java
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed 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.
- */
-
-package com.googlecode.android_scripting.webcam;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-
-import android.app.Service;
-import android.graphics.ImageFormat;
-import android.graphics.Rect;
-import android.graphics.YuvImage;
-import android.hardware.Camera;
-import android.hardware.Camera.Parameters;
-import android.hardware.Camera.PreviewCallback;
-import android.hardware.Camera.Size;
-import android.util.Base64;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.WindowManager;
-import android.view.SurfaceHolder.Callback;
-
-import com.googlecode.android_scripting.BaseApplication;
-import com.googlecode.android_scripting.FutureActivityTaskExecutor;
-import com.googlecode.android_scripting.Log;
-import com.googlecode.android_scripting.SingleThreadExecutor;
-import com.googlecode.android_scripting.SimpleServer.SimpleServerObserver;
-import com.googlecode.android_scripting.facade.EventFacade;
-import com.googlecode.android_scripting.facade.FacadeManager;
-import com.googlecode.android_scripting.future.FutureActivityTask;
-import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
-import com.googlecode.android_scripting.rpc.Rpc;
-import com.googlecode.android_scripting.rpc.RpcDefault;
-import com.googlecode.android_scripting.rpc.RpcOptional;
-import com.googlecode.android_scripting.rpc.RpcParameter;
-
-/**
- * Manages access to camera streaming.
- * <br>
- * <h3>Usage Notes</h3>
- * <br><b>webCamStart</b> and <b>webCamStop</b> are used to start and stop an Mpeg stream on a given port. <b>webcamAdjustQuality</b> is used to ajust the quality of the streaming video.
- * <br><b>cameraStartPreview</b> is used to get access to the camera preview screen. It will generate "preview" events as images become available.
- * <br>The preview has two modes: data or file. If you pass a non-blank, writable file path to the <b>cameraStartPreview</b> it will store jpg images in that folder.
- * It is up to the caller to clean up these files after the fact. If no file element is provided,
- * the event will include the image data as a base64 encoded string.
- * <h3>Event details</h3>
- * <br>The data element of the preview event will be a map, with the following elements defined.
- * <ul>
- * <li><b>format</b> - currently always "jpeg"
- * <li><b>width</b> - image width (in pixels)
- * <li><b>height</b> - image height (in pixels)
- * <li><b>quality</b> - JPEG quality. Number from 1-100
- * <li><b>filename</b> - Name of file where image was saved. Only relevant if filepath defined.
- * <li><b>error</b> - included if there was an IOException saving file, ie, disk full or path write protected.
- * <li><b>encoding</b> - Data encoding. If filepath defined, will be "file" otherwise "base64"
- * <li><b>data</b> - Base64 encoded image data.
- * </ul>
- *<br>Note that "filename", "error" and "data" are mutual exclusive.
- *<br>
- *<br>The webcam and preview modes use the same resources, so you can't use them both at the same time. Stop one mode before starting the other.
- *
- *
- */
-public class WebCamFacade extends RpcReceiver {
-
- private final Service mService;
- private final Executor mJpegCompressionExecutor = new SingleThreadExecutor();
- private final ByteArrayOutputStream mJpegCompressionBuffer = new ByteArrayOutputStream();
-
- private volatile byte[] mJpegData;
-
- private CountDownLatch mJpegDataReady;
- private boolean mStreaming;
- private int mPreviewHeight;
- private int mPreviewWidth;
- private int mJpegQuality;
-
- private MjpegServer mJpegServer;
- private FutureActivityTask<SurfaceHolder> mPreviewTask;
- private Camera mCamera;
- private Parameters mParameters;
- private final EventFacade mEventFacade;
- private boolean mPreview;
- private File mDest;
-
- private final PreviewCallback mPreviewCallback = new PreviewCallback() {
- @Override
- public void onPreviewFrame(final byte[] data, final Camera camera) {
- mJpegCompressionExecutor.execute(new Runnable() {
- @Override
- public void run() {
- mJpegData = compressYuvToJpeg(data);
- mJpegDataReady.countDown();
- if (mStreaming) {
- camera.setOneShotPreviewCallback(mPreviewCallback);
- }
- }
- });
- }
- };
-
- private final PreviewCallback mPreviewEvent = new PreviewCallback() {
- @Override
- public void onPreviewFrame(final byte[] data, final Camera camera) {
- mJpegCompressionExecutor.execute(new Runnable() {
- @Override
- public void run() {
- mJpegData = compressYuvToJpeg(data);
- Map<String,Object> map = new HashMap<String, Object>();
- map.put("format", "jpeg");
- map.put("width", mPreviewWidth);
- map.put("height", mPreviewHeight);
- map.put("quality", mJpegQuality);
- if (mDest!=null) {
- try {
- File dest=File.createTempFile("prv",".jpg",mDest);
- OutputStream output = new FileOutputStream(dest);
- output.write(mJpegData);
- output.close();
- map.put("encoding","file");
- map.put("filename",dest.toString());
- } catch (IOException e) {
- map.put("error", e.toString());
- }
- }
- else {
- map.put("encoding","Base64");
- map.put("data", Base64.encodeToString(mJpegData, Base64.DEFAULT));
- }
- mEventFacade.postEvent("preview", map);
- if (mPreview) {
- camera.setOneShotPreviewCallback(mPreviewEvent);
- }
- }
- });
- }
- };
-
- public WebCamFacade(FacadeManager manager) {
- super(manager);
- mService = manager.getService();
- mJpegDataReady = new CountDownLatch(1);
- mEventFacade = manager.getReceiver(EventFacade.class);
- }
-
- private byte[] compressYuvToJpeg(final byte[] yuvData) {
- mJpegCompressionBuffer.reset();
- YuvImage yuvImage =
- new YuvImage(yuvData, ImageFormat.NV21, mPreviewWidth, mPreviewHeight, null);
- yuvImage.compressToJpeg(new Rect(0, 0, mPreviewWidth, mPreviewHeight), mJpegQuality,
- mJpegCompressionBuffer);
- return mJpegCompressionBuffer.toByteArray();
- }
-
- @Rpc(description = "Starts an MJPEG stream and returns a Tuple of address and port for the stream.")
- public InetSocketAddress webcamStart(
- @RpcParameter(name = "resolutionLevel", description = "increasing this number provides higher resolution") @RpcDefault("0") Integer resolutionLevel,
- @RpcParameter(name = "jpegQuality", description = "a number from 0-100") @RpcDefault("20") Integer jpegQuality,
- @RpcParameter(name = "port", description = "If port is specified, the webcam service will bind to port, otherwise it will pick any available port.") @RpcDefault("0") Integer port)
- throws Exception {
- try {
- openCamera(resolutionLevel, jpegQuality);
- return startServer(port);
- } catch (Exception e) {
- webcamStop();
- throw e;
- }
- }
-
- private InetSocketAddress startServer(Integer port) {
- mJpegServer = new MjpegServer(new JpegProvider() {
- @Override
- public byte[] getJpeg() {
- try {
- mJpegDataReady.await();
- } catch (InterruptedException e) {
- Log.e(e);
- }
- return mJpegData;
- }
- });
- mJpegServer.addObserver(new SimpleServerObserver() {
- @Override
- public void onDisconnect() {
- if (mJpegServer.getNumberOfConnections() == 0 && mStreaming) {
- stopStream();
- }
- }
-
- @Override
- public void onConnect() {
- if (!mStreaming) {
- startStream();
- }
- }
- });
- return mJpegServer.startPublic(port);
- }
-
- private void stopServer() {
- if (mJpegServer != null) {
- mJpegServer.shutdown();
- mJpegServer = null;
- }
- }
-
- @Rpc(description = "Adjusts the quality of the webcam stream while it is running.")
- public void webcamAdjustQuality(
- @RpcParameter(name = "resolutionLevel", description = "increasing this number provides higher resolution") @RpcDefault("0") Integer resolutionLevel,
- @RpcParameter(name = "jpegQuality", description = "a number from 0-100") @RpcDefault("20") Integer jpegQuality)
- throws Exception {
- if (mStreaming == false) {
- throw new IllegalStateException("Webcam not streaming.");
- }
- stopStream();
- releaseCamera();
- openCamera(resolutionLevel, jpegQuality);
- startStream();
- }
-
- private void openCamera(Integer resolutionLevel, Integer jpegQuality) throws IOException,
- InterruptedException {
- mCamera = Camera.open();
- mParameters = mCamera.getParameters();
- mParameters.setPictureFormat(ImageFormat.JPEG);
- mParameters.setPreviewFormat(ImageFormat.JPEG);
- List<Size> supportedPreviewSizes = mParameters.getSupportedPreviewSizes();
- Collections.sort(supportedPreviewSizes, new Comparator<Size>() {
- @Override
- public int compare(Size o1, Size o2) {
- return o1.width - o2.width;
- }
- });
- Size previewSize =
- supportedPreviewSizes.get(Math.min(resolutionLevel, supportedPreviewSizes.size() - 1));
- mPreviewHeight = previewSize.height;
- mPreviewWidth = previewSize.width;
- mParameters.setPreviewSize(mPreviewWidth, mPreviewHeight);
- mJpegQuality = Math.min(Math.max(jpegQuality, 0), 100);
- mCamera.setParameters(mParameters);
- // TODO(damonkohler): Rotate image based on orientation.
- mPreviewTask = createPreviewTask();
- mCamera.startPreview();
- }
-
- private void startStream() {
- mStreaming = true;
- mCamera.setOneShotPreviewCallback(mPreviewCallback);
- }
-
- private void stopStream() {
- mJpegDataReady = new CountDownLatch(1);
- mStreaming = false;
- if (mPreviewTask != null) {
- mPreviewTask.finish();
- mPreviewTask = null;
- }
- }
-
- private void releaseCamera() {
- if (mCamera != null) {
- mCamera.release();
- mCamera = null;
- }
- mParameters = null;
- }
-
- @Rpc(description = "Stops the webcam stream.")
- public void webcamStop() {
- stopServer();
- stopStream();
- releaseCamera();
- }
-
- private FutureActivityTask<SurfaceHolder> createPreviewTask() throws IOException,
- InterruptedException {
- FutureActivityTask<SurfaceHolder> task = new FutureActivityTask<SurfaceHolder>() {
- @Override
- public void onCreate() {
- super.onCreate();
- final SurfaceView view = new SurfaceView(getActivity());
- getActivity().setContentView(view);
- getActivity().getWindow().setSoftInputMode(
- WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
- //view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
- view.getHolder().addCallback(new Callback() {
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- setResult(view.getHolder());
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- }
- });
- }
- };
- FutureActivityTaskExecutor taskExecutor =
- ((BaseApplication) mService.getApplication()).getTaskExecutor();
- taskExecutor.execute(task);
- mCamera.setPreviewDisplay(task.getResult());
- return task;
- }
-
- @Rpc(description = "Start Preview Mode. Throws 'preview' events.",returns="True if successful")
- public boolean cameraStartPreview(
- @RpcParameter(name = "resolutionLevel", description = "increasing this number provides higher resolution") @RpcDefault("0") Integer resolutionLevel,
- @RpcParameter(name = "jpegQuality", description = "a number from 0-100") @RpcDefault("20") Integer jpegQuality,
- @RpcParameter(name = "filepath", description = "Path to store jpeg files.") @RpcOptional String filepath)
- throws InterruptedException {
- mDest=null;
- if (filepath!=null && (filepath.length()>0)) {
- mDest = new File(filepath);
- if (!mDest.exists()) mDest.mkdirs();
- if (!(mDest.isDirectory() && mDest.canWrite())) {
- return false;
- }
- }
-
- try {
- openCamera(resolutionLevel, jpegQuality);
- } catch (IOException e) {
- Log.e(e);
- return false;
- }
- startPreview();
- return true;
- }
-
- @Rpc(description = "Stop the preview mode.")
- public void cameraStopPreview() {
- stopPreview();
- }
-
- private void startPreview() {
- mPreview = true;
- mCamera.setOneShotPreviewCallback(mPreviewEvent);
- }
-
- private void stopPreview() {
- mPreview = false;
- if (mPreviewTask!=null)
- {
- mPreviewTask.finish();
- mPreviewTask=null;
- }
- releaseCamera();
- }
-
- @Override
- public void shutdown() {
- mPreview=false;
- webcamStop();
- }
-}
diff --git a/OWNERS b/OWNERS
index 599f5dd..8c3141f 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,10 +1,12 @@
+ashutoshrsingh@google.com
bettyzhou@google.com
bmahadev@google.com
+bpeake@google.com
etancohen@google.com
gmoturu@google.com
+jaineelm@google.com
jpawlowski@google.com
krisr@google.com
-tturney@google.com
markdr@google.com
sutherlandsean@google.com
-bpeake@google.com
+tturney@google.com
diff --git a/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java b/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java
index a2dfb70..68d91b0 100644
--- a/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java
+++ b/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java
@@ -16,16 +16,6 @@
package com.googlecode.android_scripting.facade;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
import com.google.common.collect.Maps;
import com.googlecode.android_scripting.Log;
import com.googlecode.android_scripting.facade.bluetooth.BluetoothA2dpFacade;
@@ -35,8 +25,8 @@
import com.googlecode.android_scripting.facade.bluetooth.BluetoothFacade;
import com.googlecode.android_scripting.facade.bluetooth.BluetoothHealthFacade;
import com.googlecode.android_scripting.facade.bluetooth.BluetoothHfpClientFacade;
-import com.googlecode.android_scripting.facade.bluetooth.BluetoothHidFacade;
import com.googlecode.android_scripting.facade.bluetooth.BluetoothHidDeviceFacade;
+import com.googlecode.android_scripting.facade.bluetooth.BluetoothHidFacade;
import com.googlecode.android_scripting.facade.bluetooth.BluetoothHspFacade;
import com.googlecode.android_scripting.facade.bluetooth.BluetoothLeAdvertiseFacade;
import com.googlecode.android_scripting.facade.bluetooth.BluetoothLeAdvertisingSetFacade;
@@ -44,8 +34,8 @@
import com.googlecode.android_scripting.facade.bluetooth.BluetoothMapClientFacade;
import com.googlecode.android_scripting.facade.bluetooth.BluetoothMapFacade;
import com.googlecode.android_scripting.facade.bluetooth.BluetoothMediaFacade;
-import com.googlecode.android_scripting.facade.bluetooth.BluetoothPbapClientFacade;
import com.googlecode.android_scripting.facade.bluetooth.BluetoothPanFacade;
+import com.googlecode.android_scripting.facade.bluetooth.BluetoothPbapClientFacade;
import com.googlecode.android_scripting.facade.bluetooth.BluetoothRfcommFacade;
import com.googlecode.android_scripting.facade.bluetooth.GattClientFacade;
import com.googlecode.android_scripting.facade.bluetooth.GattServerFacade;
@@ -54,6 +44,7 @@
import com.googlecode.android_scripting.facade.media.MediaRecorderFacade;
import com.googlecode.android_scripting.facade.media.MediaScannerFacade;
import com.googlecode.android_scripting.facade.media.MediaSessionFacade;
+import com.googlecode.android_scripting.facade.net.IpSecManagerFacade;
import com.googlecode.android_scripting.facade.net.nsd.NsdManagerFacade;
import com.googlecode.android_scripting.facade.telephony.CarrierConfigFacade;
import com.googlecode.android_scripting.facade.telephony.ImsManagerFacade;
@@ -63,9 +54,10 @@
import com.googlecode.android_scripting.facade.telephony.TelecomManagerFacade;
import com.googlecode.android_scripting.facade.telephony.TelephonyManagerFacade;
import com.googlecode.android_scripting.facade.ui.UiFacade;
+import com.googlecode.android_scripting.facade.webcam.WebCamFacade;
import com.googlecode.android_scripting.facade.wifi.HttpFacade;
-import com.googlecode.android_scripting.facade.wifi.WifiManagerFacade;
import com.googlecode.android_scripting.facade.wifi.WifiAwareManagerFacade;
+import com.googlecode.android_scripting.facade.wifi.WifiManagerFacade;
import com.googlecode.android_scripting.facade.wifi.WifiP2pManagerFacade;
import com.googlecode.android_scripting.facade.wifi.WifiRttManagerFacade;
import com.googlecode.android_scripting.facade.wifi.WifiScannerFacade;
@@ -75,8 +67,16 @@
import com.googlecode.android_scripting.rpc.RpcMinSdk;
import com.googlecode.android_scripting.rpc.RpcStartEvent;
import com.googlecode.android_scripting.rpc.RpcStopEvent;
-import com.googlecode.android_scripting.webcam.WebCamFacade;
-import com.googlecode.android_scripting.facade.net.IpSecManagerFacade;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
/**
* Encapsulates the list of supported facades and their construction.
diff --git a/ScriptingLayerForAndroid/Android.mk b/ScriptingLayerForAndroid/Android.mk
index 8bbbe77..93a2edb 100644
--- a/ScriptingLayerForAndroid/Android.mk
+++ b/ScriptingLayerForAndroid/Android.mk
@@ -22,6 +22,7 @@
LOCAL_MODULE_TAGS := optional
LOCAL_PACKAGE_NAME := sl4a
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_MODULE_OWNER := google
LOCAL_DEX_PREOPT := false
diff --git a/ScriptingLayerForAndroid/AndroidManifest.xml b/ScriptingLayerForAndroid/AndroidManifest.xml
index 0d80394..69d1e6e 100644
--- a/ScriptingLayerForAndroid/AndroidManifest.xml
+++ b/ScriptingLayerForAndroid/AndroidManifest.xml
@@ -120,8 +120,12 @@
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
<uses-permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST"/>
- <application android:icon="@drawable/sl4a_logo_48" android:label="@string/application_title" android:name=".Sl4aApplication"
- android:theme="@android:style/Theme.DeviceDefault">
+ <application
+ android:icon="@drawable/sl4a_logo_48"
+ android:label="@string/application_title"
+ android:name=".Sl4aApplication"
+ android:theme="@android:style/Theme.DeviceDefault"
+ android:usesCleartextTraffic="true">
<activity android:name=".activity.ScriptManager" android:configChanges="keyboardHidden|orientation" android:windowSoftInputMode="adjustResize" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />