am a72a8063: (-s ours) Merge "DO NOT MERGE AdapterView does not set setCurrentItemIndex in accessibility event." into ics-mr0
* commit 'a72a80632b07dd7faf0ca2a4a0eae2770b14caa6':
DO NOT MERGE AdapterView does not set setCurrentItemIndex in accessibility event.
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index fe0106d..33310df 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -768,61 +768,6 @@
}
/**
- * TODO: Remove this once pre-built apk's (Maps, Youtube etc) are updated
- * @deprecated use {@link CreateNdefMessageCallback} or {@link OnNdefPushCompleteCallback}
- * @hide
- */
- @Deprecated
- public interface NdefPushCallback {
- /**
- * @deprecated use {@link CreateNdefMessageCallback} instead
- */
- @Deprecated
- NdefMessage createMessage();
- /**
- * @deprecated use{@link OnNdefPushCompleteCallback} instead
- */
- @Deprecated
- void onMessagePushed();
- }
-
- /**
- * TODO: Remove this
- * Converts new callbacks to old callbacks.
- */
- static final class LegacyCallbackWrapper implements CreateNdefMessageCallback,
- OnNdefPushCompleteCallback {
- final NdefPushCallback mLegacyCallback;
- LegacyCallbackWrapper(NdefPushCallback legacyCallback) {
- mLegacyCallback = legacyCallback;
- }
- @Override
- public void onNdefPushComplete(NfcEvent event) {
- mLegacyCallback.onMessagePushed();
- }
- @Override
- public NdefMessage createNdefMessage(NfcEvent event) {
- return mLegacyCallback.createMessage();
- }
- }
-
- /**
- * TODO: Remove this once pre-built apk's (Maps, Youtube etc) are updated
- * @deprecated use {@link #setNdefPushMessageCallback} instead
- * @hide
- */
- @Deprecated
- public void enableForegroundNdefPush(Activity activity, final NdefPushCallback callback) {
- if (activity == null || callback == null) {
- throw new NullPointerException();
- }
- enforceResumed(activity);
- LegacyCallbackWrapper callbackWrapper = new LegacyCallbackWrapper(callback);
- mNfcActivityManager.setNdefPushMessageCallback(activity, callbackWrapper);
- mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callbackWrapper);
- }
-
- /**
* Enable NDEF Push feature.
* <p>This API is for the Settings application.
* @hide
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 9dea4c4..70ec0af 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -32,12 +32,13 @@
import java.util.concurrent.atomic.AtomicInteger;
/**
- * <p>AsyncTask enables proper and easy use of the UI thread. This class allows to
- * perform background operations and publish results on the UI thread without
- * having to manipulate threads and/or handlers.</p>
+ * <p>AsyncTask enables proper and easy use of the UI thread (also called main thread) or
+ * any other looper thread. AsyncTask is most commonly used to interact with the UI thread.
+ * This class allows to perform background operations and publish results on a looper
+ * thread without having to manipulate threads and/or handlers.</p>
*
* <p>An asynchronous task is defined by a computation that runs on a background thread and
- * whose result is published on the UI thread. An asynchronous task is defined by 3 generic
+ * whose result is published on a looper thread. An asynchronous task is defined by 3 generic
* types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
* and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>,
* <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p>
@@ -101,7 +102,7 @@
* <h2>The 4 steps</h2>
* <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
* <ol>
- * <li>{@link #onPreExecute()}, invoked on the UI thread immediately after the task
+ * <li>{@link #onPreExecute()}, invoked on the looper thread immediately after the task
* is executed. This step is normally used to setup the task, for instance by
* showing a progress bar in the user interface.</li>
* <li>{@link #doInBackground}, invoked on the background thread
@@ -110,14 +111,14 @@
* of the asynchronous task are passed to this step. The result of the computation must
* be returned by this step and will be passed back to the last step. This step
* can also use {@link #publishProgress} to publish one or more units
- * of progress. These values are published on the UI thread, in the
+ * of progress. These values are published on the looper thread, in the
* {@link #onProgressUpdate} step.</li>
- * <li>{@link #onProgressUpdate}, invoked on the UI thread after a
+ * <li>{@link #onProgressUpdate}, invoked on the looper thread after a
* call to {@link #publishProgress}. The timing of the execution is
* undefined. This method is used to display any form of progress in the user
* interface while the background computation is still executing. For instance,
* it can be used to animate a progress bar or show logs in a text field.</li>
- * <li>{@link #onPostExecute}, invoked on the UI thread after the background
+ * <li>{@link #onPostExecute}, invoked on the looper thread after the background
* computation finishes. The result of the background computation is passed to
* this step as a parameter.</li>
* </ol>
@@ -135,8 +136,8 @@
* <p>There are a few threading rules that must be followed for this class to
* work properly:</p>
* <ul>
- * <li>The task instance must be created on the UI thread.</li>
- * <li>{@link #execute} must be invoked on the UI thread.</li>
+ * <li>The task instance must be created on the looper thread.</li>
+ * <li>{@link #execute} must be invoked on the looper thread.</li>
* <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
* {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
* <li>The task can be executed only once (an exception will be thrown if
@@ -152,6 +153,9 @@
* <li>Set member fields in {@link #doInBackground}, and refer to them in
* {@link #onProgressUpdate} and {@link #onPostExecute}.
* </ul>
+ *
+ * @see Looper
+ * @see Handler
*/
public abstract class AsyncTask<Params, Progress, Result> {
private static final String LOG_TAG = "AsyncTask";
@@ -187,7 +191,13 @@
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
- private static final InternalHandler sHandler = new InternalHandler();
+ private static final ThreadLocal<InternalHandler> sHandler =
+ new ThreadLocal<InternalHandler>() {
+ @Override
+ protected InternalHandler initialValue() {
+ return new InternalHandler();
+ }
+ };
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private final WorkerRunnable<Params, Result> mWorker;
@@ -196,6 +206,7 @@
private volatile Status mStatus = Status.PENDING;
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
+ private final InternalHandler mHandler;
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
@@ -242,9 +253,8 @@
FINISHED,
}
- /** @hide Used to force static handler to be created. */
+ /** @hide */
public static void init() {
- sHandler.getLooper();
}
/** @hide */
@@ -253,14 +263,26 @@
}
/**
- * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
+ * Creates a new asynchronous task. This constructor must be invoked on the looper thread.
+ *
+ * @throws IllegalStateException if this constructor is invoked on a non-looper thread
+ *
+ * @see Looper
*/
public AsyncTask() {
+ if (Looper.myLooper() == null) {
+ throw new IllegalStateException("AsyncTask can be only instanciated on a "
+ + "looper thread. The current thread is " + Thread.currentThread());
+ }
+
+ mHandler = sHandler.get();
+
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ //noinspection unchecked
return postResult(doInBackground(mParams));
}
};
@@ -295,7 +317,8 @@
}
private Result postResult(Result result) {
- Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
+ @SuppressWarnings({"unchecked"})
+ Message message = mHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
@@ -316,7 +339,7 @@
* by the caller of this task.
*
* This method can call {@link #publishProgress} to publish updates
- * on the UI thread.
+ * on the looper thread.
*
* @param params The parameters of the task.
*
@@ -329,7 +352,7 @@
protected abstract Result doInBackground(Params... params);
/**
- * Runs on the UI thread before {@link #doInBackground}.
+ * Runs on the looper thread before {@link #doInBackground}.
*
* @see #onPostExecute
* @see #doInBackground
@@ -338,7 +361,7 @@
}
/**
- * <p>Runs on the UI thread after {@link #doInBackground}. The
+ * <p>Runs on the looper thread after {@link #doInBackground}. The
* specified result is the value returned by {@link #doInBackground}.</p>
*
* <p>This method won't be invoked if the task was cancelled.</p>
@@ -354,7 +377,7 @@
}
/**
- * Runs on the UI thread after {@link #publishProgress} is invoked.
+ * Runs on the looper thread after {@link #publishProgress} is invoked.
* The specified values are the values passed to {@link #publishProgress}.
*
* @param values The values indicating progress.
@@ -367,7 +390,7 @@
}
/**
- * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
+ * <p>Runs on the looper thread after {@link #cancel(boolean)} is invoked and
* {@link #doInBackground(Object[])} has finished.</p>
*
* <p>The default implementation simply invokes {@link #onCancelled()} and
@@ -390,7 +413,7 @@
* This method is invoked by the default implementation of
* {@link #onCancelled(Object)}.</p>
*
- * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
+ * <p>Runs on the looper thread after {@link #cancel(boolean)} is invoked and
* {@link #doInBackground(Object[])} has finished.</p>
*
* @see #onCancelled(Object)
@@ -425,7 +448,7 @@
* an attempt to stop the task.</p>
*
* <p>Calling this method will result in {@link #onCancelled(Object)} being
- * invoked on the UI thread after {@link #doInBackground(Object[])}
+ * invoked on the looper thread after {@link #doInBackground(Object[])}
* returns. Calling this method guarantees that {@link #onPostExecute(Object)}
* is never invoked. After invoking this method, you should check the
* value returned by {@link #isCancelled()} periodically from
@@ -498,14 +521,15 @@
* with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings on
* its use.
*
- * <p>This method must be invoked on the UI thread.
+ * <p>This method must be invoked on the looper thread.
*
* @param params The parameters of the task.
*
* @return This instance of AsyncTask.
*
* @throws IllegalStateException If {@link #getStatus()} returns either
- * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
+ * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED} or
+ * the current thread is not a looper thread.
*/
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
@@ -531,7 +555,7 @@
* executed in serial; to guarantee such work is serialized regardless of
* platform version you can use this function with {@link #SERIAL_EXECUTOR}.
*
- * <p>This method must be invoked on the UI thread.
+ * <p>This method must be invoked on the looper thread.
*
* @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a
* convenient process-wide thread pool for tasks that are loosely coupled.
@@ -540,10 +564,16 @@
* @return This instance of AsyncTask.
*
* @throws IllegalStateException If {@link #getStatus()} returns either
- * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
+ * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED} or
+ * the current thread is not a looper thread.
*/
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
+ if (Looper.myLooper() == null) {
+ throw new IllegalStateException("AsyncTask can be only instanciated on a "
+ + "looper thread. The current thread is " + Thread.currentThread());
+ }
+
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
@@ -576,9 +606,9 @@
/**
* This method can be invoked from {@link #doInBackground} to
- * publish updates on the UI thread while the background computation is
+ * publish updates on the looper thread while the background computation is
* still running. Each call to this method will trigger the execution of
- * {@link #onProgressUpdate} on the UI thread.
+ * {@link #onProgressUpdate} on the looper thread.
*
* {@link #onProgressUpdate} will note be called if the task has been
* canceled.
@@ -590,7 +620,7 @@
*/
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
- sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
+ mHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 4d7a9bb..cc2fa85 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -35,7 +35,6 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
@@ -902,15 +901,13 @@
return false;
}
+ // Thread policy controls BlockGuard.
int threadPolicyMask = StrictMode.DETECT_DISK_WRITE |
StrictMode.DETECT_DISK_READ |
StrictMode.DETECT_NETWORK;
if (!IS_USER_BUILD) {
threadPolicyMask |= StrictMode.PENALTY_DROPBOX;
- if (IS_ENG_BUILD) {
- threadPolicyMask |= StrictMode.PENALTY_LOG;
- }
}
if (doFlashes) {
threadPolicyMask |= StrictMode.PENALTY_FLASH;
@@ -918,6 +915,8 @@
StrictMode.setThreadPolicyMask(threadPolicyMask);
+ // VM Policy controls CloseGuard, detection of Activity leaks,
+ // and instance counting.
if (IS_USER_BUILD) {
setCloseGuardEnabled(false);
} else {
diff --git a/data/keyboards/keyboards.mk b/data/keyboards/keyboards.mk
index 564f41c..c964961 100644
--- a/data/keyboards/keyboards.mk
+++ b/data/keyboards/keyboards.mk
@@ -24,6 +24,3 @@
PRODUCT_COPY_FILES += $(foreach file,$(keyconfigs),\
frameworks/base/data/keyboards/$(file):system/usr/idc/$(file))
-
-PRODUCT_PACKAGES := $(keylayouts) $(keycharmaps) $(keyconfigs)
-
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 349b9e3..07995085 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -264,7 +264,7 @@
layer->setFbo(0);
layer->setAlpha(255, SkXfermode::kSrcOver_Mode);
layer->layer.set(0.0f, 0.0f, 0.0f, 0.0f);
- layer->texCoords.set(0.0f, 1.0f, 0.0f, 1.0f);
+ layer->texCoords.set(0.0f, 1.0f, 1.0f, 0.0f);
layer->region.clear();
layer->setRenderTarget(GL_NONE); // see ::updateTextureLayer()
@@ -400,6 +400,18 @@
renderer.setViewport(bitmap->width(), bitmap->height());
renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
bitmap->width(), bitmap->height(), !layer->isBlend());
+
+ glDisable(GL_SCISSOR_TEST);
+ renderer.translate(0.0f, bitmap->height());
+ renderer.scale(1.0f, -1.0f);
+
+ mat4 texTransform(layer->getTexTransform());
+
+ mat4 invert;
+ invert.translate(0.0f, 1.0f, 0.0f);
+ invert.scale(1.0f, -1.0f, 1.0f);
+ layer->getTexTransform().multiply(invert);
+
if ((error = glGetError()) != GL_NO_ERROR) goto error;
{
@@ -413,6 +425,7 @@
if ((error = glGetError()) != GL_NO_ERROR) goto error;
}
+ layer->getTexTransform().load(texTransform);
status = true;
}
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 198ae4c..77acfe6 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -35,14 +35,27 @@
import java.lang.IllegalArgumentException;
/**
- * TODO javadoc update for ComponentName - PendingIntent change
* RemoteControlClient enables exposing information meant to be consumed by remote controls
* capable of displaying metadata, artwork and media transport control buttons.
- * A remote control client object is associated with a media button event receiver. This
+ *
+ * <p>A remote control client object is associated with a media button event receiver. This
* event receiver must have been previously registered with
* {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)} before the
* RemoteControlClient can be registered through
* {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.
+ *
+ * <p>Here is an example of creating a RemoteControlClient instance after registering a media
+ * button event receiver:
+ * <pre>ComponentName myEventReceiver = new ComponentName(getPackageName(), MyRemoteControlEventReceiver.class.getName());
+ * AudioManager myAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ * myAudioManager.registerMediaButtonEventReceiver(myEventReceiver);
+ * // build the PendingIntent for the remote control client
+ * Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ * mediaButtonIntent.setComponent(myEventReceiver);
+ * PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0);
+ * // create and register the remote control client
+ * RemoteControlClient myRemoteControlClient = new RemoteControlClient(mediaPendingIntent);
+ * myAudioManager.registerRemoteControlClient(myRemoteControlClient);</pre>
*/
public class RemoteControlClient
{
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 8569143..11b6c15 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -18,7 +18,6 @@
import android.app.Activity;
import android.app.AlertDialog;
-import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -57,8 +56,6 @@
private static final String TAG = "GlobalActions";
- private StatusBarManager mStatusBar;
-
private final Context mContext;
private final AudioManager mAudioManager;
@@ -103,13 +100,12 @@
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = isDeviceProvisioned;
if (mDialog == null) {
- mStatusBar = (StatusBarManager)mContext.getSystemService(Context.STATUS_BAR_SERVICE);
mDialog = createDialog();
}
prepareDialog();
- mStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
mDialog.show();
+ mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
}
/**
@@ -249,7 +245,6 @@
/** {@inheritDoc} */
public void onDismiss(DialogInterface dialog) {
- mStatusBar.disable(StatusBarManager.DISABLE_NONE);
}
/** {@inheritDoc} */
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index b05705e..bcb1aa2 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -238,6 +238,11 @@
* Notify our observers of an interface removal.
*/
private void notifyInterfaceRemoved(String iface) {
+ // netd already clears out quota and alerts for removed ifaces; update
+ // our sanity-checking state.
+ mActiveAlertIfaces.remove(iface);
+ mActiveQuotaIfaces.remove(iface);
+
for (INetworkManagementEventObserver obs : mObservers) {
try {
obs.interfaceRemoved(iface);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
index 3232eedc..414ae0d 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
@@ -22,9 +22,11 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
import android.graphics.SurfaceTexture;
import android.opengl.GLUtils;
import android.os.Bundle;
+import android.os.Environment;
import android.util.Log;
import android.view.Gravity;
import android.view.TextureView;
@@ -39,6 +41,7 @@
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL;
import java.io.BufferedOutputStream;
+import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -65,7 +68,8 @@
Bitmap b = mTextureView.getBitmap(800, 800);
BufferedOutputStream out = null;
try {
- out = new BufferedOutputStream(new FileOutputStream("/sdcard/out.png"));
+ File dump = new File(Environment.getExternalStorageDirectory(), "out.png");
+ out = new BufferedOutputStream(new FileOutputStream(dump));
b.compress(Bitmap.CompressFormat.PNG, 100, out);
} catch (FileNotFoundException e) {
e.printStackTrace();
@@ -168,10 +172,10 @@
private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
private final float[] mTriangleVerticesData = {
// X, Y, Z, U, V
- -1.0f, -1.0f, 0, 0.f, 0.f,
- 1.0f, -1.0f, 0, 1.f, 0.f,
- -1.0f, 1.0f, 0, 0.f, 1.f,
- 1.0f, 1.0f, 0, 1.f, 1.f,
+ -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
+ 1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
+ -1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
+ 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
};
@Override
@@ -212,8 +216,6 @@
while (!mFinished) {
checkCurrent();
- Log.d(LOG_TAG, "Rendering frame");
-
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
checkGlError();
@@ -237,7 +239,7 @@
checkEglError();
try {
- Thread.sleep(20);
+ Thread.sleep(2000);
} catch (InterruptedException e) {
// Ignore
}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
index fcb57d9..0f4c668 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
@@ -17,16 +17,23 @@
package com.android.test.hwui;
import android.app.Activity;
+import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.Bundle;
+import android.os.Environment;
import android.view.Gravity;
+import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
@SuppressWarnings({"UnusedDeclaration"})
@@ -44,6 +51,26 @@
mTextureView = new TextureView(this);
mTextureView.setSurfaceTextureListener(this);
+ mTextureView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Bitmap b = mTextureView.getBitmap(800, 800);
+ BufferedOutputStream out = null;
+ try {
+ File dump = new File(Environment.getExternalStorageDirectory(), "out.png");
+ out = new BufferedOutputStream(new FileOutputStream(dump));
+ b.compress(Bitmap.CompressFormat.PNG, 100, out);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } finally {
+ if (out != null) try {
+ out.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ });
Button button = new Button(this);
button.setText("Remove/Add");
@@ -73,6 +100,8 @@
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mCamera = Camera.open();
+ mCamera.setDisplayOrientation(getCameraOrientation());
+
Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
mTextureView.setLayoutParams(new FrameLayout.LayoutParams(
previewSize.width, previewSize.height, Gravity.CENTER));
@@ -86,6 +115,34 @@
mCamera.startPreview();
}
+ private int getCameraOrientation() {
+ Camera.CameraInfo info = new Camera.CameraInfo();
+ for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
+ Camera.getCameraInfo(i, info);
+ if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) break;
+ }
+
+ int rotation = getWindowManager().getDefaultDisplay().getRotation();
+ int degrees = 0;
+
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ degrees = 0;
+ break;
+ case Surface.ROTATION_90:
+ degrees = 90;
+ break;
+ case Surface.ROTATION_180:
+ degrees = 180;
+ break;
+ case Surface.ROTATION_270:
+ degrees = 270;
+ break;
+ }
+
+ return (info.orientation - degrees + 360) % 360;
+ }
+
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Ignored, the Camera does all the work for us