Merge "Implement DayNight theme for DocumentsUI"
diff --git a/api/current.txt b/api/current.txt
index 85813f0..e509a00 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7774,6 +7774,7 @@
field public static final java.lang.String MEDIA_PROJECTION_SERVICE = "media_projection";
field public static final java.lang.String MEDIA_ROUTER_SERVICE = "media_router";
field public static final java.lang.String MEDIA_SESSION_SERVICE = "media_session";
+ field public static final java.lang.String MIDI_SERVICE = "midi";
field public static final int MODE_APPEND = 32768; // 0x8000
field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
field public static final int MODE_MULTI_PROCESS = 4; // 0x4
@@ -9311,6 +9312,7 @@
field public static final java.lang.String FEATURE_APP_WIDGETS = "android.software.app_widgets";
field public static final java.lang.String FEATURE_AUDIO_LOW_LATENCY = "android.hardware.audio.low_latency";
field public static final java.lang.String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
+ field public static final java.lang.String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
field public static final java.lang.String FEATURE_BACKUP = "android.software.backup";
field public static final java.lang.String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
field public static final java.lang.String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
@@ -12412,7 +12414,9 @@
method public void draw(android.graphics.Canvas);
method public android.graphics.drawable.Drawable findDrawableByLayerId(int);
method public int findIndexByLayerId(int);
+ method public int getBottomPadding();
method public android.graphics.drawable.Drawable getDrawable(int);
+ method public int getEndPadding();
method public int getId(int);
method public int getLayerGravity(int);
method public int getLayerHeight(int);
@@ -12423,9 +12427,13 @@
method public int getLayerInsetStart(int);
method public int getLayerInsetTop(int);
method public int getLayerWidth(int);
+ method public int getLeftPadding();
method public int getNumberOfLayers();
method public int getOpacity();
method public int getPaddingMode();
+ method public int getRightPadding();
+ method public int getStartPadding();
+ method public int getTopPadding();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
method public void setAlpha(int);
@@ -12446,7 +12454,9 @@
method public void setLayerSize(int, int, int);
method public void setLayerWidth(int, int);
method public void setOpacity(int);
+ method public void setPadding(int, int, int, int);
method public void setPaddingMode(int);
+ method public void setPaddingRelative(int, int, int, int);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
field public static final int PADDING_MODE_NEST = 0; // 0x0
field public static final int PADDING_MODE_STACK = 1; // 0x1
@@ -14987,6 +14997,7 @@
method public int getPlayState();
method public int getPlaybackHeadPosition();
method public int getPlaybackRate();
+ method public android.media.PlaybackSettings getPlaybackSettings();
method public int getPositionNotificationPeriod();
method public android.media.AudioDeviceInfo getPreferredOutputDevice();
method public int getSampleRate();
@@ -15004,6 +15015,7 @@
method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener);
method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener, android.os.Handler);
method public int setPlaybackRate(int);
+ method public void setPlaybackSettings(android.media.PlaybackSettings);
method public int setPositionNotificationPeriod(int);
method public boolean setPreferredOutputDevice(android.media.AudioDeviceInfo);
method protected deprecated void setState(int);
@@ -15733,7 +15745,7 @@
method public final void release();
method public void seekTo(long, int);
method public void selectTrack(int);
- method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException, java.lang.IllegalArgumentException;
+ method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException;
method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
method public final void setDataSource(java.lang.String) throws java.io.IOException;
@@ -16367,6 +16379,24 @@
method public abstract void onAudioDeviceConnection();
}
+ public final class PlaybackSettings {
+ ctor public PlaybackSettings();
+ method public android.media.PlaybackSettings allowDefaults();
+ method public int getAudioFallbackMode();
+ method public int getAudioStretchMode();
+ method public float getPitch();
+ method public float getSpeed();
+ method public android.media.PlaybackSettings setAudioFallbackMode(int);
+ method public android.media.PlaybackSettings setAudioStretchMode(int);
+ method public android.media.PlaybackSettings setPitch(float);
+ method public android.media.PlaybackSettings setSpeed(float);
+ field public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0; // 0x0
+ field public static final int AUDIO_FALLBACK_MODE_FAIL = 2; // 0x2
+ field public static final int AUDIO_FALLBACK_MODE_MUTE = 1; // 0x1
+ field public static final int AUDIO_STRETCH_MODE_DEFAULT = 0; // 0x0
+ field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
+ }
+
public final class Rating implements android.os.Parcelable {
method public int describeContents();
method public float getPercentRating();
@@ -30888,6 +30918,7 @@
method public int getEvdoSnr();
method public int getGsmBitErrorRate();
method public int getGsmSignalStrength();
+ method public int getLevel();
method public boolean isGsm();
method public void writeToParcel(android.os.Parcel, int);
}
@@ -35568,10 +35599,10 @@
method public long getTimeDelta();
method public boolean isInProgress();
method public boolean isQuickScaleEnabled();
- method public boolean isSecondaryButtonScaleEnabled();
+ method public boolean isStylusScaleEnabled();
method public boolean onTouchEvent(android.view.MotionEvent);
method public void setQuickScaleEnabled(boolean);
- method public void setSecondaryButtonScaleEnabled(boolean);
+ method public void setStylusScaleEnabled(boolean);
}
public static abstract interface ScaleGestureDetector.OnScaleGestureListener {
diff --git a/api/system-current.txt b/api/system-current.txt
index 043a55b..8b4052b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7991,6 +7991,7 @@
field public static final java.lang.String MEDIA_PROJECTION_SERVICE = "media_projection";
field public static final java.lang.String MEDIA_ROUTER_SERVICE = "media_router";
field public static final java.lang.String MEDIA_SESSION_SERVICE = "media_session";
+ field public static final java.lang.String MIDI_SERVICE = "midi";
field public static final int MODE_APPEND = 32768; // 0x8000
field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
field public static final int MODE_MULTI_PROCESS = 4; // 0x4
@@ -9570,6 +9571,7 @@
field public static final java.lang.String FEATURE_APP_WIDGETS = "android.software.app_widgets";
field public static final java.lang.String FEATURE_AUDIO_LOW_LATENCY = "android.hardware.audio.low_latency";
field public static final java.lang.String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
+ field public static final java.lang.String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
field public static final java.lang.String FEATURE_BACKUP = "android.software.backup";
field public static final java.lang.String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
field public static final java.lang.String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
@@ -12706,7 +12708,9 @@
method public void draw(android.graphics.Canvas);
method public android.graphics.drawable.Drawable findDrawableByLayerId(int);
method public int findIndexByLayerId(int);
+ method public int getBottomPadding();
method public android.graphics.drawable.Drawable getDrawable(int);
+ method public int getEndPadding();
method public int getId(int);
method public int getLayerGravity(int);
method public int getLayerHeight(int);
@@ -12717,9 +12721,13 @@
method public int getLayerInsetStart(int);
method public int getLayerInsetTop(int);
method public int getLayerWidth(int);
+ method public int getLeftPadding();
method public int getNumberOfLayers();
method public int getOpacity();
method public int getPaddingMode();
+ method public int getRightPadding();
+ method public int getStartPadding();
+ method public int getTopPadding();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
method public void setAlpha(int);
@@ -12740,7 +12748,9 @@
method public void setLayerSize(int, int, int);
method public void setLayerWidth(int, int);
method public void setOpacity(int);
+ method public void setPadding(int, int, int, int);
method public void setPaddingMode(int);
+ method public void setPaddingRelative(int, int, int, int);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
field public static final int PADDING_MODE_NEST = 0; // 0x0
field public static final int PADDING_MODE_STACK = 1; // 0x1
@@ -16199,6 +16209,7 @@
method public int getPlayState();
method public int getPlaybackHeadPosition();
method public int getPlaybackRate();
+ method public android.media.PlaybackSettings getPlaybackSettings();
method public int getPositionNotificationPeriod();
method public android.media.AudioDeviceInfo getPreferredOutputDevice();
method public int getSampleRate();
@@ -16216,6 +16227,7 @@
method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener);
method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener, android.os.Handler);
method public int setPlaybackRate(int);
+ method public void setPlaybackSettings(android.media.PlaybackSettings);
method public int setPositionNotificationPeriod(int);
method public boolean setPreferredOutputDevice(android.media.AudioDeviceInfo);
method protected deprecated void setState(int);
@@ -16946,7 +16958,7 @@
method public final void release();
method public void seekTo(long, int);
method public void selectTrack(int);
- method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException, java.lang.IllegalArgumentException;
+ method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException;
method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
method public final void setDataSource(java.lang.String) throws java.io.IOException;
@@ -17582,6 +17594,24 @@
method public abstract void onAudioDeviceConnection();
}
+ public final class PlaybackSettings {
+ ctor public PlaybackSettings();
+ method public android.media.PlaybackSettings allowDefaults();
+ method public int getAudioFallbackMode();
+ method public int getAudioStretchMode();
+ method public float getPitch();
+ method public float getSpeed();
+ method public android.media.PlaybackSettings setAudioFallbackMode(int);
+ method public android.media.PlaybackSettings setAudioStretchMode(int);
+ method public android.media.PlaybackSettings setPitch(float);
+ method public android.media.PlaybackSettings setSpeed(float);
+ field public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0; // 0x0
+ field public static final int AUDIO_FALLBACK_MODE_FAIL = 2; // 0x2
+ field public static final int AUDIO_FALLBACK_MODE_MUTE = 1; // 0x1
+ field public static final int AUDIO_STRETCH_MODE_DEFAULT = 0; // 0x0
+ field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
+ }
+
public final class Rating implements android.os.Parcelable {
method public int describeContents();
method public float getPercentRating();
@@ -33041,6 +33071,7 @@
method public int getEvdoSnr();
method public int getGsmBitErrorRate();
method public int getGsmSignalStrength();
+ method public int getLevel();
method public boolean isGsm();
method public void writeToParcel(android.os.Parcel, int);
}
@@ -37767,10 +37798,10 @@
method public long getTimeDelta();
method public boolean isInProgress();
method public boolean isQuickScaleEnabled();
- method public boolean isSecondaryButtonScaleEnabled();
+ method public boolean isStylusScaleEnabled();
method public boolean onTouchEvent(android.view.MotionEvent);
method public void setQuickScaleEnabled(boolean);
- method public void setSecondaryButtonScaleEnabled(boolean);
+ method public void setStylusScaleEnabled(boolean);
}
public static abstract interface ScaleGestureDetector.OnScaleGestureListener {
diff --git a/core/java/android/annotation/BinderThread.java b/core/java/android/annotation/BinderThread.java
new file mode 100644
index 0000000..c69ba10
--- /dev/null
+++ b/core/java/android/annotation/BinderThread.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 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 android.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated method should only be called on the binder thread.
+ * If the annotated element is a class, then all methods in the class should be called
+ * on the binder thread.
+ * <p>
+ * Example:
+ * <pre>{@code
+ * (@BinderThread
+ * public BeamShareData createBeamShareData() { ... }
+ * }</pre>
+ *
+ * {@hide}
+ */
+@Retention(SOURCE)
+@Target({METHOD,CONSTRUCTOR,TYPE})
+public @interface BinderThread {
+}
\ No newline at end of file
diff --git a/core/java/android/annotation/MainThread.java b/core/java/android/annotation/MainThread.java
new file mode 100644
index 0000000..18a4283
--- /dev/null
+++ b/core/java/android/annotation/MainThread.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 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 android.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated method should only be called on the main thread.
+ * If the annotated element is a class, then all methods in the class should be called
+ * on the main thread.
+ * <p>
+ * Example:
+ * <pre>{@code
+ * @MainThread
+ * public void deliverResult(D data) { ... }
+ * }</pre>
+ *
+ * {@hide}
+ */
+@Retention(SOURCE)
+@Target({METHOD,CONSTRUCTOR,TYPE})
+public @interface MainThread {
+}
\ No newline at end of file
diff --git a/core/java/android/annotation/UiThread.java b/core/java/android/annotation/UiThread.java
new file mode 100644
index 0000000..b814600
--- /dev/null
+++ b/core/java/android/annotation/UiThread.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 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 android.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated method or constructor should only be called on the UI thread.
+ * If the annotated element is a class, then all methods in the class should be called
+ * on the UI thread.
+ * <p>
+ * Example:
+ * <pre>{@code
+ * @UiThread
+ * public abstract void setText(@NonNull String text) { ... }
+ * }</pre>
+ *
+ * {@hide}
+ */
+@Retention(SOURCE)
+@Target({METHOD,CONSTRUCTOR,TYPE})
+public @interface UiThread {
+}
\ No newline at end of file
diff --git a/core/java/android/annotation/WorkerThread.java b/core/java/android/annotation/WorkerThread.java
new file mode 100644
index 0000000..dd12e05
--- /dev/null
+++ b/core/java/android/annotation/WorkerThread.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 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 android.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated method should only be called on a worker thread.
+ * If the annotated element is a class, then all methods in the class should be called
+ * on a worker thread.
+ * <p>
+ * Example:
+ * <pre>{@code
+ * (@WorkerThread
+ * protected abstract FilterResults performFiltering(CharSequence constraint);
+ * }</pre>
+ *
+ * {@hide}
+ */
+@Retention(SOURCE)
+@Target({METHOD,CONSTRUCTOR,TYPE})
+public @interface WorkerThread {
+}
\ No newline at end of file
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 10d6d01..ab5f811 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2541,11 +2541,16 @@
if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL) {
data.putParcelable(AssistStructure.ASSIST_KEY, new AssistStructure(r.activity));
AssistContent content = new AssistContent();
- Intent intent = new Intent(r.activity.getIntent());
- intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
- intent.removeUnsafeExtras();
- content.setIntent(intent);
+ Intent activityIntent = r.activity.getIntent();
+ if (activityIntent != null) {
+ Intent intent = new Intent(activityIntent);
+ intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
+ intent.removeUnsafeExtras();
+ content.setIntent(intent);
+ } else {
+ content.setIntent(new Intent());
+ }
r.activity.onProvideAssistContent(content);
data.putParcelable(AssistContent.ASSIST_KEY, content);
}
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 179957d..9d1d312 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -597,6 +597,15 @@
}
}
+ /** @hide */
+ public long getNextWakeFromIdleTime() {
+ try {
+ return mService.getNextWakeFromIdleTime();
+ } catch (RemoteException ex) {
+ return Long.MAX_VALUE;
+ }
+ }
+
/**
* Gets information about the next alarm clock currently scheduled.
*
diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl
index d5719f5..327c00b 100644
--- a/core/java/android/app/IAlarmManager.aidl
+++ b/core/java/android/app/IAlarmManager.aidl
@@ -33,6 +33,7 @@
boolean setTime(long millis);
void setTimeZone(String zone);
void remove(in PendingIntent operation);
+ long getNextWakeFromIdleTime();
AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3bf3f85..5eacce3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3098,7 +3098,6 @@
* {@link android.media.midi.MidiManager} for accessing the MIDI service.
*
* @see #getSystemService
- * @hide
*/
public static final String MIDI_SERVICE = "midi";
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a0cec50..e4108b1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1574,6 +1574,21 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: This is a device dedicated to showing UI
+ * on a vehicle headunit. A headunit here is defined to be inside a
+ * vehicle that may or may not be moving. A headunit uses either a
+ * primary display in the center console and/or additional displays in
+ * the instrument cluster or elsewhere in the vehicle. Headunit display(s)
+ * have limited size and resolution. The user will likely be focused on
+ * driving so limiting driver distraction is a primary concern. User input
+ * can be a variety of hard buttons, touch, rotary controllers and even mouse-
+ * like interfaces.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: This is a device dedicated to showing UI
* on a television. Television here is defined to be a typical living
* room television experience: displayed on a big screen, where the user
* is sitting far away from it, and the dominant form of input will be
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 0d7b261..2257b0a 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -587,6 +587,25 @@
return false;
}
+ /**
+ * Retrieves the authenticator token for binding keys to the lifecycle
+ * of the current set of fingerprints. Used only by internal clients.
+ *
+ * @hide
+ */
+ public long getAuthenticatorId() {
+ if (mService != null) {
+ try {
+ return mService.getAuthenticatorId();
+ } catch (RemoteException e) {
+ Log.v(TAG, "Remote exception in getAuthenticatorId(): ", e);
+ }
+ } else {
+ Log.w(TAG, "getAuthenticatorId(): Service not connected!");
+ }
+ return 0;
+ }
+
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch(msg.what) {
@@ -792,4 +811,5 @@
}
};
-}
\ No newline at end of file
+}
+
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 51a0e4c..c5ec08c 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -63,4 +63,6 @@
// Gets the unique device id for hardware enumerated at i
// long getHardwareDevice(int i);
+ // Gets the authenticator ID for fingerprint
+ long getAuthenticatorId();
}
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 5cf2c5c..b055efe 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -130,7 +130,7 @@
private float mFocusY;
private boolean mQuickScaleEnabled;
- private boolean mButtonScaleEnabled;
+ private boolean mStylusScaleEnabled;
private float mCurrSpan;
private float mPrevSpan;
@@ -162,7 +162,7 @@
private static final float SCALE_FACTOR = .5f;
private static final int ANCHORED_SCALE_MODE_NONE = 0;
private static final int ANCHORED_SCALE_MODE_DOUBLE_TAP = 1;
- private static final int ANCHORED_SCALE_MODE_BUTTON = 2;
+ private static final int ANCHORED_SCALE_MODE_STYLUS = 2;
/**
@@ -212,9 +212,14 @@
mMinSpan = res.getDimensionPixelSize(com.android.internal.R.dimen.config_minScalingSpan);
mHandler = handler;
// Quick scale is enabled by default after JB_MR2
- if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
+ if (targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN_MR2) {
setQuickScaleEnabled(true);
}
+ // Stylus scale is enabled by default after LOLLIPOP_MR1
+ if (targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
+ setStylusScaleEnabled(true);
+ }
}
/**
@@ -315,14 +320,11 @@
}
final int count = event.getPointerCount();
- final int toolType = event.getToolType(0);
- final boolean isButtonTool = toolType == MotionEvent.TOOL_TYPE_STYLUS
- || toolType == MotionEvent.TOOL_TYPE_MOUSE;
- final boolean isAnchoredScaleButtonDown = isButtonTool && (count == 1)
+ final boolean isStylusButtonDown = (event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS)
&& (event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0;
final boolean anchoredScaleCancelled =
- mAnchoredScaleMode == ANCHORED_SCALE_MODE_BUTTON && !isAnchoredScaleButtonDown;
+ mAnchoredScaleMode == ANCHORED_SCALE_MODE_STYLUS && !isStylusButtonDown;
final boolean streamComplete = action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_CANCEL || anchoredScaleCancelled;
@@ -347,12 +349,12 @@
}
}
- if (!mInProgress && mButtonScaleEnabled && !inAnchoredScaleMode()
- && !streamComplete && isAnchoredScaleButtonDown) {
+ if (!mInProgress && mStylusScaleEnabled && !inAnchoredScaleMode()
+ && !streamComplete && isStylusButtonDown) {
// Start of a button scale gesture
mAnchoredScaleStartX = event.getX();
mAnchoredScaleStartY = event.getY();
- mAnchoredScaleMode = ANCHORED_SCALE_MODE_BUTTON;
+ mAnchoredScaleMode = ANCHORED_SCALE_MODE_STYLUS;
mInitialSpan = 0;
}
@@ -503,24 +505,22 @@
}
/**
- * Sets whether the associates {@link OnScaleGestureListener} should receive onScale callbacks
- * when the user presses a {@value MotionEvent#BUTTON_SECONDARY} (right mouse button, stylus
- * first button) and drags the pointer on the screen. Note that this is enabled by default if
- * the app targets API 23 and newer.
+ * Sets whether the associates {@link OnScaleGestureListener} should receive
+ * onScale callbacks when the user uses a stylus and presses the button.
+ * Note that this is enabled by default if the app targets API 23 and newer.
*
- * @param scales true to enable stylus or mouse scaling, false to disable.
+ * @param scales true to enable stylus scaling, false to disable.
*/
- public void setSecondaryButtonScaleEnabled(boolean scales) {
- mButtonScaleEnabled = scales;
+ public void setStylusScaleEnabled(boolean scales) {
+ mStylusScaleEnabled = scales;
}
/**
- * Return whether the button scale gesture, in which the user presses a
- * {@value MotionEvent#BUTTON_SECONDARY} (right mouse button, stylus first button) and drags the
- * pointer on the screen, should perform scaling. {@see #setButtonScaleEnabled(boolean)}.
+ * Return whether the stylus scale gesture, in which the user uses a stylus
+ * and presses the button, should preform scaling. {@see #setButtonScaleEnabled(boolean)}.
*/
- public boolean isSecondaryButtonScaleEnabled() {
- return mButtonScaleEnabled;
+ public boolean isStylusScaleEnabled() {
+ return mStylusScaleEnabled;
}
/**
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 0747969..44037dd 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -358,7 +358,7 @@
return legacyBitmapConfigToColorType(c);
}
-SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
+android::Canvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
SkASSERT(env);
SkASSERT(canvas);
SkASSERT(env->IsInstanceOf(canvas, gCanvas_class));
@@ -366,9 +366,7 @@
if (!canvasHandle) {
return NULL;
}
- SkCanvas* c = reinterpret_cast<android::Canvas*>(canvasHandle)->asSkCanvas();
- SkASSERT(c);
- return c;
+ return reinterpret_cast<android::Canvas*>(canvasHandle);
}
SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region)
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 422d3f1..d73507e 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -8,6 +8,7 @@
#include "SkPoint.h"
#include "SkRect.h"
#include "SkImageDecoder.h"
+#include <Canvas.h>
#include <jni.h>
class SkBitmapRegionDecoder;
@@ -47,7 +48,7 @@
static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point);
static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf);
- static SkCanvas* getNativeCanvas(JNIEnv*, jobject canvas);
+ static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas);
static SkBitmap* getSkBitmap(JNIEnv*, jobject bitmap);
static SkRegion* getNativeRegion(JNIEnv*, jobject region);
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index a2c1609..3ae829b 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -39,17 +39,23 @@
}
// Native wrapper constructor used by Canvas(Bitmap)
-static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap));
+static jlong initRaster(JNIEnv* env, jobject, jobject jbitmap) {
+ SkBitmap* bitmap = nullptr;
+ if (jbitmap != NULL) {
+ bitmap = GraphicsJNI::getSkBitmap(env, jbitmap);
+ }
+ return reinterpret_cast<jlong>(Canvas::create_canvas(
+ bitmap ? *bitmap : SkBitmap()));
}
// Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
// optionally copying canvas matrix & clip state.
-static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
- jboolean copyState) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- get_canvas(canvasHandle)->setBitmap(bitmap, copyState);
+static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap) {
+ SkBitmap* bitmap = nullptr;
+ if (jbitmap != NULL) {
+ bitmap = GraphicsJNI::getSkBitmap(env, jbitmap);
+ }
+ get_canvas(canvasHandle)->setBitmap(bitmap ? *bitmap : SkBitmap());
}
static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
@@ -658,8 +664,8 @@
static JNINativeMethod gMethods[] = {
{"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
- {"initRaster", "(J)J", (void*) CanvasJNI::initRaster},
- {"native_setBitmap", "(JJZ)V", (void*) CanvasJNI::setBitmap},
+ {"initRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster},
+ {"native_setBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
{"native_isOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
{"native_getWidth","(J)I", (void*) CanvasJNI::getWidth},
{"native_getHeight","(J)I", (void*) CanvasJNI::getHeight},
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 8d3a9aa..8b2c269 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -677,6 +677,63 @@
// ----------------------------------------------------------------------------
+static void android_media_AudioTrack_set_playback_settings(JNIEnv *env, jobject thiz,
+ jfloatArray floatArray, jintArray intArray) {
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "AudioTrack not initialized");
+ return;
+ }
+
+ // NOTE: Get<Primitive>ArrayRegion throws ArrayIndexOutOfBoundsException if not valid.
+ // TODO: consider the actual occupancy.
+ float farray[2];
+ int iarray[2];
+ if ((env->GetFloatArrayRegion(floatArray, 0, 2, farray), env->ExceptionCheck()) == JNI_FALSE
+ &&
+ (env->GetIntArrayRegion(intArray, 0, 2, iarray), env->ExceptionCheck()) == JNI_FALSE) {
+ // arrays retrieved OK
+ AudioPlaybackRate playbackRate;
+ playbackRate.mSpeed = farray[0];
+ playbackRate.mPitch = farray[1];
+ playbackRate.mFallbackMode = (AudioTimestretchFallbackMode)iarray[0];
+ playbackRate.mStretchMode = (AudioTimestretchStretchMode)iarray[1];
+ if (lpTrack->setPlaybackRate(playbackRate) != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "arguments out of range");
+ }
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+static void android_media_AudioTrack_get_playback_settings(JNIEnv *env, jobject thiz,
+ jfloatArray floatArray, jintArray intArray) {
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "AudioTrack not initialized");
+ return;
+ }
+
+ AudioPlaybackRate playbackRate = lpTrack->getPlaybackRate();
+
+ float farray[2] = {
+ playbackRate.mSpeed,
+ playbackRate.mPitch,
+ };
+ int iarray[2] = {
+ playbackRate.mFallbackMode,
+ playbackRate.mStretchMode,
+ };
+ // NOTE: Set<Primitive>ArrayRegion throws ArrayIndexOutOfBoundsException if not valid.
+ env->SetFloatArrayRegion(floatArray, 0, 2, farray);
+ env->SetIntArrayRegion(intArray, 0, 2, iarray);
+}
+
+
+// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env, jobject thiz,
jint markerPos) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
@@ -942,6 +999,10 @@
"(I)I", (void *)android_media_AudioTrack_set_playback_rate},
{"native_get_playback_rate",
"()I", (void *)android_media_AudioTrack_get_playback_rate},
+ {"native_set_playback_settings",
+ "([F[I)V", (void *)android_media_AudioTrack_set_playback_settings},
+ {"native_get_playback_settings",
+ "([F[I)V", (void *)android_media_AudioTrack_get_playback_settings},
{"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos},
{"native_get_marker_pos","()I", (void *)android_media_AudioTrack_get_marker_pos},
{"native_set_pos_update_period",
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index a12629f..aa79d70 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -67,11 +67,6 @@
jfieldID bottom;
} gRectClassInfo;
-static struct {
- jfieldID mSurfaceFormat;
- jmethodID setNativeBitmap;
-} gCanvasClassInfo;
-
#define GET_INT(object, field) \
env->GetIntField(object, field)
@@ -196,13 +191,9 @@
bitmap.setPixels(NULL);
}
- SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer->getPixelFormat());
- INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(&bitmap));
-
- SkRect clipRect;
- clipRect.set(rect.left, rect.top, rect.right, rect.bottom);
- SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
- nativeCanvas->clipRect(clipRect);
+ Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
+ nativeCanvas->setBitmap(bitmap);
+ nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom);
if (dirtyRect) {
INVOKEV(dirtyRect, gRectClassInfo.set,
@@ -217,7 +208,8 @@
GraphicBufferWrapper* wrapper =
reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
- INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0);
+ Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
+ nativeCanvas->setBitmap(SkBitmap());
if (wrapper) {
status_t status = wrapper->buffer->unlock();
@@ -302,10 +294,6 @@
gRectClassInfo.right = GetFieldIDOrDie(env, clazz, "right", "I");
gRectClassInfo.bottom = GetFieldIDOrDie(env, clazz, "bottom", "I");
- clazz = FindClassOrDie(env, "android/graphics/Canvas");
- gCanvasClassInfo.mSurfaceFormat = GetFieldIDOrDie(env, clazz, "mSurfaceFormat", "I");
- gCanvasClassInfo.setNativeBitmap = GetMethodIDOrDie(env, clazz, "setNativeBitmap", "(J)V");
-
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 4fcb361..c4cd7ff 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -73,11 +73,6 @@
jfieldID bottom;
} gRectClassInfo;
-static struct {
- jfieldID mSurfaceFormat;
- jmethodID setNativeBitmap;
-} gCanvasClassInfo;
-
// ----------------------------------------------------------------------------
// this is just a pointer we use to pass to inc/decStrong
@@ -318,9 +313,6 @@
return 0;
}
- // Associate a SkCanvas object to this surface
- env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, outBuffer.format);
-
SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
convertPixelFormat(outBuffer.format),
kPremul_SkAlphaType);
@@ -338,12 +330,12 @@
bitmap.setPixels(NULL);
}
- env->CallVoidMethod(canvasObj, gCanvasClassInfo.setNativeBitmap,
- reinterpret_cast<jlong>(&bitmap));
+ Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
+ nativeCanvas->setBitmap(bitmap);
if (dirtyRectPtr) {
- SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
- nativeCanvas->clipRect( SkRect::Make(reinterpret_cast<const SkIRect&>(dirtyRect)) );
+ nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top,
+ dirtyRect.right, dirtyRect.bottom);
}
if (dirtyRectObj) {
@@ -369,7 +361,8 @@
}
// detach the canvas from the surface
- env->CallVoidMethod(canvasObj, gCanvasClassInfo.setNativeBitmap, (jlong)0);
+ Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
+ nativeCanvas->setBitmap(SkBitmap());
// unlock surface
status_t err = surface->unlockAndPost();
@@ -564,10 +557,6 @@
gSurfaceClassInfo.clazz, "mLock", "Ljava/lang/Object;");
gSurfaceClassInfo.ctor = GetMethodIDOrDie(env, gSurfaceClassInfo.clazz, "<init>", "(J)V");
- clazz = FindClassOrDie(env, "android/graphics/Canvas");
- gCanvasClassInfo.mSurfaceFormat = GetFieldIDOrDie(env, clazz, "mSurfaceFormat", "I");
- gCanvasClassInfo.setNativeBitmap = GetMethodIDOrDie(env, clazz, "setNativeBitmap", "(J)V");
-
clazz = FindClassOrDie(env, "android/graphics/Rect");
gRectClassInfo.left = GetFieldIDOrDie(env, clazz, "left", "I");
gRectClassInfo.top = GetFieldIDOrDie(env, clazz, "top", "I");
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index c2bd0b3c4..7e05793 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -48,11 +48,6 @@
} gRectClassInfo;
static struct {
- jfieldID mSurfaceFormat;
- jmethodID setNativeBitmap;
-} gCanvasClassInfo;
-
-static struct {
jfieldID nativeWindow;
} gTextureViewClassInfo;
@@ -172,13 +167,9 @@
bitmap.setPixels(NULL);
}
- SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer.format);
- INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(&bitmap));
-
- SkRect clipRect;
- clipRect.set(rect.left, rect.top, rect.right, rect.bottom);
- SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
- nativeCanvas->clipRect(clipRect);
+ Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
+ nativeCanvas->setBitmap(bitmap);
+ nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom);
if (dirtyRect) {
INVOKEV(dirtyRect, gRectClassInfo.set,
@@ -191,7 +182,8 @@
static void android_view_TextureView_unlockCanvasAndPost(JNIEnv* env, jobject,
jlong nativeWindow, jobject canvas) {
- INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0);
+ Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
+ nativeCanvas->setBitmap(SkBitmap());
if (nativeWindow) {
sp<ANativeWindow> window((ANativeWindow*) nativeWindow);
@@ -225,10 +217,6 @@
gRectClassInfo.right = GetFieldIDOrDie(env, clazz, "right", "I");
gRectClassInfo.bottom = GetFieldIDOrDie(env, clazz, "bottom", "I");
- clazz = FindClassOrDie(env, "android/graphics/Canvas");
- gCanvasClassInfo.mSurfaceFormat = GetFieldIDOrDie(env, clazz, "mSurfaceFormat", "I");
- gCanvasClassInfo.setNativeBitmap = GetMethodIDOrDie(env, clazz, "setNativeBitmap", "(J)V");
-
clazz = FindClassOrDie(env, "android/view/TextureView");
gTextureViewClassInfo.nativeWindow = GetFieldIDOrDie(env, clazz, "mNativeWindow", "J");
diff --git a/core/res/res/drawable/ic_spinner_caret.xml b/core/res/res/drawable/ic_spinner_caret.xml
new file mode 100644
index 0000000..6a18f89
--- /dev/null
+++ b/core/res/res/drawable/ic_spinner_caret.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2015 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:pathData="M7,10l5,5,5-5z"
+ android:fillColor="@color/white"/>
+</vector>
diff --git a/core/res/res/drawable/spinner_background_material.xml b/core/res/res/drawable/spinner_background_material.xml
index d5b509f..892dbc5 100644
--- a/core/res/res/drawable/spinner_background_material.xml
+++ b/core/res/res/drawable/spinner_background_material.xml
@@ -15,21 +15,24 @@
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
- android:paddingMode="stack">
- <item android:drawable="@drawable/item_background_borderless_material"
- android:gravity="end|center_vertical"
- android:width="24dp"
- android:height="24dp" />
- <item android:gravity="end|center_vertical">
- <vector android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"
- android:tint="?attr/colorControlNormal">
- <path android:pathData="M7,10l5,5,5-5z"
- android:fillColor="@color/white"/>
- </vector>
+ android:paddingMode="stack"
+ android:paddingStart="0dp"
+ android:paddingEnd="48dp"
+ android:paddingLeft="0dp"
+ android:paddingRight="0dp">
+ <item
+ android:gravity="end|center_vertical"
+ android:width="48dp"
+ android:height="48dp">
+ <ripple
+ android:color="?attr/colorControlHighlight"
+ android:radius="24dp" />
</item>
- <item android:end="48dp"
- android:drawable="@color/transparent" />
+
+ <item
+ android:drawable="@drawable/ic_spinner_caret"
+ android:gravity="end|center_vertical"
+ android:width="24dp"
+ android:height="24dp"
+ android:end="12dp" />
</layer-list>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 551c083..a5c6f84 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5183,6 +5183,20 @@
<!-- Stack each layer directly atop the previous layer. -->
<enum name="stack" value="1" />
</attr>
+ <!-- Explicit top padding. Overrides child padding. -->
+ <attr name="paddingTop" />
+ <!-- Explicit bottom padding. Overrides child padding. -->
+ <attr name="paddingBottom" />
+ <!-- Explicit left padding. Overrides child padding. -->
+ <attr name="paddingLeft" />
+ <!-- Explicit right padding. Overrides child padding. -->
+ <attr name="paddingRight" />
+ <!-- Explicit start padding. Overrides child padding. Takes precedence
+ over absolute padding (e.g. left when layout direction is LTR). -->
+ <attr name="paddingStart" />
+ <!-- Explicit end padding. Overrides child padding. Takes precedence
+ over absolute padding (e.g. right when layout direction is LTR). -->
+ <attr name="paddingEnd" />
</declare-styleable>
<!-- Describes an item (or child) of a LayerDrawable. -->
diff --git a/docs/html/tools/support-library/index.jd b/docs/html/tools/support-library/index.jd
index 56a3954..1aef0c1 100644
--- a/docs/html/tools/support-library/index.jd
+++ b/docs/html/tools/support-library/index.jd
@@ -62,6 +62,211 @@
<div class="toggle-content opened">
<p id="rev21"><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img" alt=""
+/>Android Support Library, revision 22.1.0</a> <em>(April 2015)</em>
+ </p>
+ <div class="toggle-content-toggleme">
+ <dl>
+ <dt>Changes for <a href="features.html#v4">v4 support library:</a></dt>
+ <dd>
+ <ul>
+
+ <li>Added the {@link android.support.v4.graphics.ColorUtils ColorUtils} class
+ to provide a set of color-related utility methods.
+ </li>
+ <li>Added the {@link android.support.v4.graphics.drawable.DrawableCompat#unwrap unwrap()} and
+ {@link android.support.v4.graphics.drawable.DrawableCompat#wrap wrap()} methods to the
+ {@link android.support.v4.graphics.drawable.DrawableCompat} class , allowing you to use
+ {@link android.support.v4.graphics.drawable.DrawableCompat#setTint setTint()},
+ {@link android.support.v4.graphics.drawable.DrawableCompat#setTintList setTintList()},
+ and {@link android.support.v4.graphics.drawable.DrawableCompat#setTintMode setTintMode()}
+ on all API level 4 or higher devices.
+ </li>
+ <li>Added the {@link android.support.v4.os.TraceCompat} class to
+ write trace events to the system trace buffer, which can then be collected and
+ visualized using the <a href="{@docRoot}tools/help/systrace.html">Systrace</a> tool.
+ </li>
+ <li>Added the {@link android.support.v4.util.CircularIntArray} class
+ to create circular integer array data structures.
+ </li>
+ <li>Added the {@link android.support.v4.util.CircularArray#clear clear()},
+ {@link android.support.v4.util.CircularArray#removeFromStart removeFromStart()},
+ and {@link android.support.v4.util.CircularArray#removeFromEnd removeFromEnd()}
+ methods to the {@link android.support.v4.util.CircularArray} class. Also, changed the
+ existing methods in this class to be non-final.
+ </li>
+ <li>Added the {@link android.support.v4.view.InputDeviceCompat}
+ as a helper class to access data in the
+ {@link android.support.v4.view.InputDeviceCompat} class.
+ </li>
+ <li>Added the {@link android.support.v4.view.LayoutInflaterCompat}
+ class as a helper class to access data in the
+ {@link android.support.v4.view.LayoutInflaterCompat} class
+ and added the {@link android.support.v4.view.LayoutInflaterFactory} interface.
+ </li>
+ <li>Added classes, methods, and interfaces to support nested scrolling.
+ <ul>
+ <li>Added the {@link android.support.v4.view.NestedScrollingChildHelper}
+ and {@link android.support.v4.view.NestedScrollingParentHelper}
+ helper classes for implementing nested scrolling parent and child views.</li>
+ <li>Added the {@link android.support.v4.view.NestedScrollingChild}
+ interface to be implemented by {@link android.view.View} subclasses.</li>
+ <li>Added the {@link android.support.v4.view.NestedScrollingParent}
+ and {@link android.support.v4.view.ScrollingView} interfaces to support
+ scrolling operations and provide scroll related APIs.</li>
+ <li>Added the
+ {@link android.support.v4.view.ViewGroupCompat#getNestedScrollAxes
+ getNestedScrollAxes()} method to the {@link android.support.v4.view.ViewGroupCompat}
+ class.</li>
+ <li>Added methods to the {@link android.support.v4.view.ViewParentCompat} class to
+ support nested scrolling.
+ </li>
+ <li>Added the {@link android.support.v4.widget.NestedScrollView}
+ class to support nested scrolling parent and child on both new and old versions of
+ Android.
+ </li>
+ </ul>
+ </li>
+ <li>Added methods and constants to the {@link android.support.v4.view.MotionEventCompat}
+ class for getting axis values and event source.
+ </li>
+ <li>Updated the {@link android.support.v4.view.accessibility.AccessibilityNodeInfoCompat}
+ class to add methods for errors, content invalidation and labels.
+ </li>
+ <li>Added the following interpolation classses for animation:
+ {@link android.support.v4.view.animation.FastOutLinearInInterpolator},
+ {@link android.support.v4.view.animation.FastOutSlowInInterpolator},
+ {@link android.support.v4.view.animation.LinearOutSlowInInterpolator},
+ {@link android.support.v4.view.animation.LinearOutSlowInInterpolator}, and
+ {@link android.support.v4.view.animation.PathInterpolatorCompat}.
+ </li>
+ <li>Added the {@link android.support.v4.widget.Space} class to create gaps between
+ components in general purpose layouts. This class is deprecated in the gridlayout library.
+ </li>
+ <li>Added the {@link android.support.v4.widget.TextViewCompat} class for accessing
+ features in a {@link android.widget.TextView}.
+ </li>
+ <li>Added a displacement parameter to the
+ {@link android.support.v4.widget.EdgeEffectCompat#onPull onPull()} method in the
+ {@link android.support.v4.widget.EdgeEffectCompat} class.
+ </li>
+
+ </ul>
+ </dd>
+
+
+ <dt>Changes for <a href="features.html#v7-appcompat">v7 appcompat library</a>:</dt>
+ <dd>
+ <ul>
+ <li>Added tint support to appcompat widgets, including
+ {@link android.support.v7.widget.AppCompatAutoCompleteTextView},
+ {@link android.support.v7.widget.AppCompatButton},
+ {@link android.support.v7.widget.AppCompatCheckBox},
+ {@link android.support.v7.widget.AppCompatCheckedTextView},
+ {@link android.support.v7.widget.AppCompatEditText},
+ {@link android.support.v7.widget.AppCompatMultiAutoCompleteTextView},
+ {@link android.support.v7.widget.AppCompatRadioButton},
+ {@link android.support.v7.widget.AppCompatRatingBar},
+ {@link android.support.v7.widget.AppCompatSpinner}, and
+ {@link android.support.v7.widget.AppCompatTextView}.
+ </li>
+ <li>Updated the {@link android.support.v7.app.AppCompatActivity} as the base
+ class for activities that use the support library action bar features. This class
+ replaces the deprecated {@link android.support.v7.app.ActionBarActivity}.
+ </li>
+ <li>Added the
+ {@link android.support.v7.app.AppCompatCallback} interface
+ to be implemented for AppCompat to be able to perform callbacks.
+ </li>
+ <li>Added the
+ {@link android.support.v7.app.AppCompatDelegate} abstract class
+ as a delegate you can use to extend AppCompat's support to any activity.
+ </li>
+ <li>Added the
+ {@link android.support.v7.app.AppCompatDialog} class
+ as the base class for AppCompat themed dialogs.
+ </li>
+ <li>Added the spinner style
+ {@link android.support.v7.app.AlertDialog} and
+ {@link android.support.v7.app.AlertDialog.Builder} classes to provide an AppCompat
+ themed {@link android.app.AlertDialog}.
+ </li>
+ <li>Added the {@link android.support.v7.graphics.Palette.Builder} class
+ for generating {@link android.support.v7.graphics.Palette} instances.
+ <ul>
+ <li>Added the
+ {@link android.support.v7.graphics.Palette#from}
+ method to the {@link android.support.v7.graphics.Palette} class to
+ start generating a Palette with the returned
+ {@link android.support.v7.graphics.Palette.Builder} instance.
+ </li>
+ <li>Deprecated the {@link android.support.v7.graphics.Palette#generate generate()} and
+ {@link android.support.v7.graphics.Palette#generateAsync generateAsync()} methods.
+ </li>
+ </ul>
+ </li>
+
+ <li>Added the
+ {@link android.support.v7.widget.GridLayout.Spec#getAbsoluteAlignment
+ getAbsoluteAlignment()} method to the {@link android.support.v7.widget.GridLayout.Spec}
+ class.
+ </li>
+ <li>Deprecated use of <code>app:theme</code> for styling
+ {@link android.support.v7.widget.Toolbar}. You can now use
+ <code>android:theme</code> for toolbars on all API level 7 and higher devices and
+ <code>android:theme</code> support for all widgets on API level 11 and higher devices.
+ </li>
+ </ul>
+ </dd>
+
+
+ <dt>Changes for <a href="features.html#v17-leanback">v17 leanback library</a>:</dt>
+ <dd>
+ <ul>
+ <li> Added {@link android.support.v17.leanback.app.GuidedStepFragment},
+ {@link android.support.v17.leanback.widget.GuidanceStylist} and
+ {@link android.support.v17.leanback.widget.GuidedActionsStylist} to support
+ creating multi-step decision flows.
+ </li>
+ </ul>
+ </dd>
+
+
+ <dt>Changes for <a href="features.html#v7-recyclerview">v7 recyclerview library</a>:</dt>
+ <dd>
+ <ul>
+ <li>Added {@link android.support.v7.util.SortedList} classes to display items in
+ a list order and provide notification of changes to the list.
+ </li>
+ <li>Added the {@link android.support.v7.widget.util.SortedListAdapterCallback} class
+ that can bind a sorted list to a
+ {@link android.support.v7.widget.RecyclerView.Adapter} class.
+ </li>
+ </ul>
+ </dd>
+
+
+ <dt>Changes for v8 renderscript library:</dt>
+ <dd>
+ <ul>
+ <li>Added the {@link android.support.v8.renderscript.ScriptIntrinsicHistogram} class for
+ use as a histogram filter.</li>
+ <li>Added the {@link android.support.v8.renderscript.ScriptIntrinsicResize} class for
+ performing a resize of a 2D allocation.
+ </li>
+ </ul>
+ </dd>
+
+ </dl>
+
+
+ </div>
+</div> <!-- end of collapsible section -->
+
+
+
+<div class="toggle-content closed">
+ <p id="rev21"><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt=""
/>Android Support Library, revision 22</a> <em>(March 2015)</em>
</p>
<div class="toggle-content-toggleme">
@@ -159,6 +364,10 @@
</ul>
</dd>
+ </div>
+</div>
+
+
<div class="toggle-content closed">
@@ -1113,4 +1322,3 @@
<p>Initial release with the v4 library.</p>
</div>
</div>
-
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 48afcbf..2acb8ba 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -81,10 +81,6 @@
*/
protected int mScreenDensity = Bitmap.DENSITY_NONE;
- // Used by native code
- @SuppressWarnings("UnusedDeclaration")
- private int mSurfaceFormat;
-
/**
* Flag for drawTextRun indicating left-to-right run direction.
* @hide
@@ -137,7 +133,7 @@
public Canvas() {
if (!isHardwareAccelerated()) {
// 0 means no native bitmap
- mNativeCanvasWrapper = initRaster(0);
+ mNativeCanvasWrapper = initRaster(null);
mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
} else {
mFinalizer = null;
@@ -158,7 +154,7 @@
throw new IllegalStateException("Immutable bitmap passed to Canvas constructor");
}
throwIfCannotDraw(bitmap);
- mNativeCanvasWrapper = initRaster(bitmap.getSkBitmap());
+ mNativeCanvasWrapper = initRaster(bitmap);
mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
mBitmap = bitmap;
mDensity = bitmap.mDensity;
@@ -215,7 +211,7 @@
}
if (bitmap == null) {
- native_setBitmap(mNativeCanvasWrapper, 0, false);
+ native_setBitmap(mNativeCanvasWrapper, null);
mDensity = Bitmap.DENSITY_NONE;
} else {
if (!bitmap.isMutable()) {
@@ -223,7 +219,7 @@
}
throwIfCannotDraw(bitmap);
- native_setBitmap(mNativeCanvasWrapper, bitmap.getSkBitmap(), true);
+ native_setBitmap(mNativeCanvasWrapper, bitmap);
mDensity = bitmap.mDensity;
}
@@ -231,13 +227,6 @@
}
/**
- * setBitmap() variant for native callers with a raw bitmap handle.
- */
- private void setNativeBitmap(long bitmapHandle) {
- native_setBitmap(mNativeCanvasWrapper, bitmapHandle, false);
- }
-
- /**
* Set the viewport dimensions if this canvas is GL based. If it is not,
* this method is ignored and no exception is thrown.
*
@@ -1976,10 +1965,9 @@
*/
public static native void freeTextLayoutCaches();
- private static native long initRaster(long nativeBitmapOrZero);
+ private static native long initRaster(Bitmap bitmap);
private static native void native_setBitmap(long canvasHandle,
- long bitmapHandle,
- boolean copyState);
+ Bitmap bitmap);
private static native boolean native_isOpaque(long canvasHandle);
private static native int native_getWidth(long canvasHandle);
private static native int native_getHeight(long canvasHandle);
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 8468d9e..3bbbc71 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -86,7 +86,6 @@
LayerState mLayerState;
- private int mOpacityOverride = PixelFormat.UNKNOWN;
private int[] mPaddingL;
private int[] mPaddingT;
private int[] mPaddingR;
@@ -177,12 +176,39 @@
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
- mOpacityOverride = a.getInt(R.styleable.LayerDrawable_opacity, mOpacityOverride);
-
- state.mAutoMirrored = a.getBoolean(R.styleable.LayerDrawable_autoMirrored,
- state.mAutoMirrored);
- state.mPaddingMode = a.getInteger(R.styleable.LayerDrawable_paddingMode,
- state.mPaddingMode);
+ final int N = a.getIndexCount();
+ for (int i = 0; i < N; i++) {
+ int attr = a.getIndex(i);
+ switch (attr) {
+ case R.styleable.LayerDrawable_opacity:
+ state.mOpacityOverride = a.getInt(attr, state.mOpacityOverride);
+ break;
+ case R.styleable.LayerDrawable_paddingTop:
+ state.mPaddingTop = a.getDimensionPixelOffset(attr, state.mPaddingTop);
+ break;
+ case R.styleable.LayerDrawable_paddingBottom:
+ state.mPaddingBottom = a.getDimensionPixelOffset(attr, state.mPaddingBottom);
+ break;
+ case R.styleable.LayerDrawable_paddingLeft:
+ state.mPaddingLeft = a.getDimensionPixelOffset(attr, state.mPaddingLeft);
+ break;
+ case R.styleable.LayerDrawable_paddingRight:
+ state.mPaddingRight = a.getDimensionPixelOffset(attr, state.mPaddingRight);
+ break;
+ case R.styleable.LayerDrawable_paddingStart:
+ state.mPaddingStart = a.getDimensionPixelOffset(attr, state.mPaddingStart);
+ break;
+ case R.styleable.LayerDrawable_paddingEnd:
+ state.mPaddingEnd = a.getDimensionPixelOffset(attr, state.mPaddingEnd);
+ break;
+ case R.styleable.LayerDrawable_autoMirrored:
+ state.mAutoMirrored = a.getBoolean(attr, state.mAutoMirrored);
+ break;
+ case R.styleable.LayerDrawable_paddingMode:
+ state.mPaddingMode = a.getInteger(attr, state.mPaddingMode);
+ break;
+ }
+ }
}
/**
@@ -895,15 +921,210 @@
@Override
public boolean getPadding(Rect padding) {
- if (mLayerState.mPaddingMode == PADDING_MODE_NEST) {
+ final LayerState layerState = mLayerState;
+ if (layerState.mPaddingMode == PADDING_MODE_NEST) {
computeNestedPadding(padding);
} else {
computeStackedPadding(padding);
}
+ // If padding was explicitly specified (e.g. not -1) then override the
+ // computed padding in that dimension.
+ if (layerState.mPaddingTop >= 0) {
+ padding.top = layerState.mPaddingTop;
+ }
+
+ if (layerState.mPaddingBottom >= 0) {
+ padding.bottom = layerState.mPaddingBottom;
+ }
+
+ final int paddingRtlLeft;
+ final int paddingRtlRight;
+ if (getLayoutDirection() == LayoutDirection.RTL) {
+ paddingRtlLeft = layerState.mPaddingEnd;
+ paddingRtlRight = layerState.mPaddingStart;
+ } else {
+ paddingRtlLeft = layerState.mPaddingStart;
+ paddingRtlRight = layerState.mPaddingEnd;
+ }
+
+ final int paddingLeft = paddingRtlLeft >= 0 ? paddingRtlLeft : layerState.mPaddingLeft;
+ if (paddingLeft >= 0) {
+ padding.left = paddingLeft;
+ }
+
+ final int paddingRight = paddingRtlRight >= 0 ? paddingRtlRight : layerState.mPaddingRight;
+ if (paddingRight >= 0) {
+ padding.right = paddingRight;
+ }
+
return padding.left != 0 || padding.top != 0 || padding.right != 0 || padding.bottom != 0;
}
+ /**
+ * Sets the absolute padding.
+ * <p>
+ * If padding in a dimension is specified as {@code -1}, the resolved
+ * padding will use the value computed according to the padding mode (see
+ * {@link #setPaddingMode(int)}).
+ * <p>
+ * Calling this method clears any relative padding values previously set
+ * using {@link #setPaddingRelative(int, int, int, int)}.
+ *
+ * @param left the left padding in pixels, or -1 to use computed padding
+ * @param top the top padding in pixels, or -1 to use computed padding
+ * @param right the right padding in pixels, or -1 to use computed padding
+ * @param bottom the bottom padding in pixels, or -1 to use computed
+ * padding
+ * @attr ref android.R.styleable#LayerDrawable_paddingLeft
+ * @attr ref android.R.styleable#LayerDrawable_paddingTop
+ * @attr ref android.R.styleable#LayerDrawable_paddingRight
+ * @attr ref android.R.styleable#LayerDrawable_paddingBottom
+ * @see #setPaddingRelative(int, int, int, int)
+ */
+ public void setPadding(int left, int top, int right, int bottom) {
+ final LayerState layerState = mLayerState;
+ layerState.mPaddingLeft = left;
+ layerState.mPaddingTop = top;
+ layerState.mPaddingRight = right;
+ layerState.mPaddingBottom = bottom;
+
+ // Clear relative padding values.
+ layerState.mPaddingStart = -1;
+ layerState.mPaddingEnd = -1;
+ }
+
+ /**
+ * Sets the relative padding.
+ * <p>
+ * If padding in a dimension is specified as {@code -1}, the resolved
+ * padding will use the value computed according to the padding mode (see
+ * {@link #setPaddingMode(int)}).
+ * <p>
+ * Calling this method clears any absolute padding values previously set
+ * using {@link #setPadding(int, int, int, int)}.
+ *
+ * @param start the start padding in pixels, or -1 to use computed padding
+ * @param top the top padding in pixels, or -1 to use computed padding
+ * @param end the end padding in pixels, or -1 to use computed padding
+ * @param bottom the bottom padding in pixels, or -1 to use computed
+ * padding
+ * @attr ref android.R.styleable#LayerDrawable_paddingStart
+ * @attr ref android.R.styleable#LayerDrawable_paddingTop
+ * @attr ref android.R.styleable#LayerDrawable_paddingEnd
+ * @attr ref android.R.styleable#LayerDrawable_paddingBottom
+ * @see #setPadding(int, int, int, int)
+ */
+ public void setPaddingRelative(int start, int top, int end, int bottom) {
+ final LayerState layerState = mLayerState;
+ layerState.mPaddingStart = start;
+ layerState.mPaddingTop = top;
+ layerState.mPaddingEnd = end;
+ layerState.mPaddingBottom = bottom;
+
+ // Clear absolute padding values.
+ layerState.mPaddingLeft = -1;
+ layerState.mPaddingRight = -1;
+ }
+
+ /**
+ * Returns the left padding in pixels.
+ * <p>
+ * A return value of {@code -1} means there is no explicit padding set for
+ * this dimension. As a result, the value for this dimension returned by
+ * {@link #getPadding(Rect)} will be computed from the child layers
+ * according to the padding mode (see {@link #getPaddingMode()}.
+ *
+ * @return the left padding in pixels, or -1 if not explicitly specified
+ * @see #setPadding(int, int, int, int)
+ * @see #getPadding(Rect)
+ */
+ public int getLeftPadding() {
+ return mLayerState.mPaddingLeft;
+ }
+
+ /**
+ * Returns the right padding in pixels.
+ * <p>
+ * A return value of {@code -1} means there is no explicit padding set for
+ * this dimension. As a result, the value for this dimension returned by
+ * {@link #getPadding(Rect)} will be computed from the child layers
+ * according to the padding mode (see {@link #getPaddingMode()}.
+ *
+ * @return the right padding in pixels, or -1 if not explicitly specified
+ * @see #setPadding(int, int, int, int)
+ * @see #getPadding(Rect)
+ */
+ public int getRightPadding() {
+ return mLayerState.mPaddingRight;
+ }
+
+ /**
+ * Returns the start padding in pixels.
+ * <p>
+ * A return value of {@code -1} means there is no explicit padding set for
+ * this dimension. As a result, the value for this dimension returned by
+ * {@link #getPadding(Rect)} will be computed from the child layers
+ * according to the padding mode (see {@link #getPaddingMode()}.
+ *
+ * @return the start padding in pixels, or -1 if not explicitly specified
+ * @see #setPaddingRelative(int, int, int, int)
+ * @see #getPadding(Rect)
+ */
+ public int getStartPadding() {
+ return mLayerState.mPaddingStart;
+ }
+
+ /**
+ * Returns the end padding in pixels.
+ * <p>
+ * A return value of {@code -1} means there is no explicit padding set for
+ * this dimension. As a result, the value for this dimension returned by
+ * {@link #getPadding(Rect)} will be computed from the child layers
+ * according to the padding mode (see {@link #getPaddingMode()}.
+ *
+ * @return the end padding in pixels, or -1 if not explicitly specified
+ * @see #setPaddingRelative(int, int, int, int)
+ * @see #getPadding(Rect)
+ */
+ public int getEndPadding() {
+ return mLayerState.mPaddingEnd;
+ }
+
+ /**
+ * Returns the top padding in pixels.
+ * <p>
+ * A return value of {@code -1} means there is no explicit padding set for
+ * this dimension. As a result, the value for this dimension returned by
+ * {@link #getPadding(Rect)} will be computed from the child layers
+ * according to the padding mode (see {@link #getPaddingMode()}.
+ *
+ * @return the top padding in pixels, or -1 if not explicitly specified
+ * @see #setPadding(int, int, int, int)
+ * @see #setPaddingRelative(int, int, int, int)
+ * @see #getPadding(Rect)
+ */
+ public int getTopPadding() {
+ return mLayerState.mPaddingTop;
+ }
+
+ /**
+ * Returns the bottom padding in pixels.
+ * <p>
+ * A return value of {@code -1} means there is no explicit padding set for
+ * this dimension. As a result, the value for this dimension returned by
+ * {@link #getPadding(Rect)} will be computed from the child layers
+ * according to the padding mode (see {@link #getPaddingMode()}.
+ *
+ * @return the bottom padding in pixels, or -1 if not explicitly specified
+ * @see #setPadding(int, int, int, int)
+ * @see #setPaddingRelative(int, int, int, int)
+ * @see #getPadding(Rect)
+ */
+ public int getBottomPadding() {
+ return mLayerState.mPaddingBottom;
+ }
+
private void computeNestedPadding(Rect padding) {
padding.left = 0;
padding.top = 0;
@@ -1109,8 +1330,8 @@
}
/**
- * Sets the opacity of this drawable directly, instead of collecting the
- * states from the layers
+ * Sets the opacity of this drawable directly instead of collecting the
+ * states from the layers.
*
* @param opacity The opacity to use, or {@link PixelFormat#UNKNOWN
* PixelFormat.UNKNOWN} for the default behavior
@@ -1120,13 +1341,13 @@
* @see PixelFormat#OPAQUE
*/
public void setOpacity(int opacity) {
- mOpacityOverride = opacity;
+ mLayerState.mOpacityOverride = opacity;
}
@Override
public int getOpacity() {
- if (mOpacityOverride != PixelFormat.UNKNOWN) {
- return mOpacityOverride;
+ if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) {
+ return mLayerState.mOpacityOverride;
}
return mLayerState.getOpacity();
}
@@ -1265,12 +1486,12 @@
* dimension, defaults to START or TOP. Otherwise, defaults to FILL to
* preserve legacy behavior.
*
- * @param gravity
- * @param width
- * @param height
- * @return
+ * @param gravity layer gravity
+ * @param width width of the layer if set, -1 otherwise
+ * @param height height of the layer if set, -1 otherwise
+ * @return the default gravity for the layer
*/
- private int resolveGravity(int gravity, int width, int height) {
+ private static int resolveGravity(int gravity, int width, int height) {
if (!Gravity.isHorizontal(gravity)) {
if (width < 0) {
gravity |= Gravity.FILL_HORIZONTAL;
@@ -1504,6 +1725,14 @@
ChildDrawable[] mChildren;
int[] mThemeAttrs;
+ int mPaddingTop = -1;
+ int mPaddingBottom = -1;
+ int mPaddingLeft = -1;
+ int mPaddingRight = -1;
+ int mPaddingStart = -1;
+ int mPaddingEnd = -1;
+ int mOpacityOverride = PixelFormat.UNKNOWN;
+
int mChangingConfigurations;
int mChildrenChangingConfigurations;
@@ -1540,6 +1769,13 @@
mAutoMirrored = orig.mAutoMirrored;
mPaddingMode = orig.mPaddingMode;
mThemeAttrs = orig.mThemeAttrs;
+ mPaddingTop = orig.mPaddingTop;
+ mPaddingBottom = orig.mPaddingBottom;
+ mPaddingLeft = orig.mPaddingLeft;
+ mPaddingRight = orig.mPaddingRight;
+ mPaddingStart = orig.mPaddingStart;
+ mPaddingEnd = orig.mPaddingEnd;
+ mOpacityOverride = orig.mOpacityOverride;
} else {
mNum = 0;
mChildren = null;
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 6731a17..f67dcb3 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -323,7 +323,21 @@
*/
@Override
public boolean isProjected() {
- return getNumberOfLayers() == 0;
+ // If the maximum radius is contained entirely within the bounds, we
+ // don't need to project this ripple.
+ final int radius = mState.mMaxRadius;
+ final Rect bounds = getBounds();
+ if (radius != RADIUS_AUTO && radius <= bounds.width() / 2
+ && radius <= bounds.height() / 2) {
+ return false;
+ }
+
+ // Otherwise, if the layer is bounded then we don't need to project.
+ return !isBounded();
+ }
+
+ private boolean isBounded() {
+ return getNumberOfLayers() > 0;
}
@Override
@@ -545,7 +559,7 @@
y = mHotspotBounds.exactCenterY();
}
- final boolean isBounded = !isProjected();
+ final boolean isBounded = isBounded();
mRipple = new RippleForeground(this, mHotspotBounds, x, y, isBounded);
}
@@ -866,7 +880,7 @@
@Override
public Rect getDirtyBounds() {
- if (isProjected()) {
+ if (!isBounded()) {
final Rect drawingBounds = mDrawingBounds;
final Rect dirtyBounds = mDirtyBounds;
dirtyBounds.set(drawingBounds);
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index 7ad0683..aa24673 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -29,7 +29,7 @@
public:
virtual ~Canvas() {};
- static Canvas* create_canvas(SkBitmap* bitmap);
+ static Canvas* create_canvas(const SkBitmap& bitmap);
/**
* Create a new Canvas object which delegates to an SkCanvas.
@@ -52,7 +52,7 @@
*/
virtual SkCanvas* asSkCanvas() = 0;
- virtual void setBitmap(SkBitmap* bitmap, bool copyState) = 0;
+ virtual void setBitmap(const SkBitmap& bitmap) = 0;
virtual bool isOpaque() = 0;
virtual int width() = 0;
@@ -87,7 +87,8 @@
virtual bool quickRejectRect(float left, float top, float right, float bottom) const = 0;
virtual bool quickRejectPath(const SkPath& path) const = 0;
- virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) = 0;
+ virtual bool clipRect(float left, float top, float right, float bottom,
+ SkRegion::Op op = SkRegion::kIntersect_Op) = 0;
virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0;
virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0;
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index a9ac57d..2b0b6b2 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -136,7 +136,7 @@
// ----------------------------------------------------------------------------
virtual SkCanvas* asSkCanvas() override;
- virtual void setBitmap(SkBitmap* bitmap, bool copyState) override {
+ virtual void setBitmap(const SkBitmap& bitmap) override {
LOG_ALWAYS_FATAL("DisplayListCanvas is not backed by a bitmap.");
}
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 8b11757..a323065 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -31,7 +31,7 @@
// Holds an SkCanvas reference plus additional native data.
class SkiaCanvas : public Canvas {
public:
- explicit SkiaCanvas(SkBitmap* bitmap);
+ explicit SkiaCanvas(const SkBitmap& bitmap);
/**
* Create a new SkiaCanvas.
@@ -49,7 +49,7 @@
return mCanvas.get();
}
- virtual void setBitmap(SkBitmap* bitmap, bool copyState) override;
+ virtual void setBitmap(const SkBitmap& bitmap) override;
virtual bool isOpaque() override;
virtual int width() override;
@@ -145,19 +145,7 @@
SkAutoTDelete<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
};
-// Construct an SkCanvas from the bitmap.
-static SkCanvas* createCanvas(SkBitmap* bitmap) {
- if (bitmap) {
- return SkNEW_ARGS(SkCanvas, (*bitmap));
- }
-
- // Create an empty bitmap device to prevent callers from crashing
- // if they attempt to draw into this canvas.
- SkBitmap emptyBitmap;
- return new SkCanvas(emptyBitmap);
-}
-
-Canvas* Canvas::create_canvas(SkBitmap* bitmap) {
+Canvas* Canvas::create_canvas(const SkBitmap& bitmap) {
return new SkiaCanvas(bitmap);
}
@@ -165,8 +153,8 @@
return new SkiaCanvas(skiaCanvas);
}
-SkiaCanvas::SkiaCanvas(SkBitmap* bitmap) {
- mCanvas.reset(createCanvas(bitmap));
+SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) {
+ mCanvas.reset(new SkCanvas(bitmap));
}
// ----------------------------------------------------------------------------
@@ -191,11 +179,11 @@
SkCanvas* m_dstCanvas;
};
-void SkiaCanvas::setBitmap(SkBitmap* bitmap, bool copyState) {
- SkCanvas* newCanvas = createCanvas(bitmap);
+void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
+ SkCanvas* newCanvas = new SkCanvas(bitmap);
SkASSERT(newCanvas);
- if (copyState) {
+ if (!bitmap.isNull()) {
// Copy the canvas matrix & clip state.
newCanvas->setMatrix(mCanvas->getTotalMatrix());
if (NULL != mCanvas->getDevice() && NULL != newCanvas->getDevice()) {
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index ded9d31..093ff26 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -946,13 +946,30 @@
}
/**
- * Returns the current playback rate in Hz.
+ * Returns the current playback sample rate rate in Hz.
*/
public int getPlaybackRate() {
return native_get_playback_rate();
}
/**
+ * Returns the current playback settings.
+ * See {@link #setPlaybackSettings(PlaybackSettings)} to set playback settings
+ * @return current {@link PlaybackSettings}.
+ * @throws IllegalStateException if track is not initialized.
+ */
+ public @NonNull PlaybackSettings getPlaybackSettings() {
+ float[] floatArray = new float[2];
+ int[] intArray = new int[2];
+ native_get_playback_settings(floatArray, intArray);
+ return new PlaybackSettings()
+ .setSpeed(floatArray[0])
+ .setPitch(floatArray[1])
+ .setAudioFallbackMode(intArray[0])
+ .setAudioStretchMode(intArray[1]);
+ }
+
+ /**
* Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_16BIT}
* and {@link AudioFormat#ENCODING_PCM_8BIT}.
*/
@@ -1307,6 +1324,7 @@
* playback to last twice as long, but will also result in a pitch shift down by one octave.
* The valid sample rate range is from 1 Hz to twice the value returned by
* {@link #getNativeOutputSampleRate(int)}.
+ * Use {@link #setPlaybackSettings(PlaybackSettings)} for speed control.
* @param sampleRateInHz the sample rate expressed in Hz
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
@@ -1323,6 +1341,42 @@
/**
+ * Sets the playback settings.
+ * This method returns failure if it cannot apply the playback settings.
+ * One possible cause is that the parameters for speed or pitch are out of range.
+ * Another possible cause is that the <code>AudioTrack</code> is streaming
+ * (see {@link #MODE_STREAM}) and the
+ * buffer size is too small. For speeds greater than 1.0f, the <code>AudioTrack</code> buffer
+ * on configuration must be larger than the speed multiplied by the minimum size
+ * {@link #getMinBufferSize(int, int, int)}) to allow proper playback.
+ * @param settings see {@link PlaybackSettings}. In particular,
+ * speed, pitch, and audio mode should be set.
+ * @throws IllegalArgumentException if the settings are invalid or not accepted.
+ * @throws IllegalStateException if track is not initialized.
+ */
+ public void setPlaybackSettings(@NonNull PlaybackSettings settings) {
+ if (settings == null) {
+ throw new IllegalArgumentException("settings is null");
+ }
+ float[] floatArray;
+ int[] intArray;
+ try {
+ floatArray = new float[] {
+ settings.getSpeed(),
+ settings.getPitch(),
+ };
+ intArray = new int[] {
+ settings.getAudioFallbackMode(),
+ settings.getAudioStretchMode(),
+ };
+ } catch (IllegalStateException e) {
+ throw new IllegalArgumentException(e);
+ }
+ native_set_playback_settings(floatArray, intArray);
+ }
+
+
+ /**
* Sets the position of the notification marker. At most one marker can be active.
* @param markerInFrames marker position in wrapping frame units similar to
* {@link #getPlaybackHeadPosition}, or zero to disable the marker.
@@ -2207,6 +2261,15 @@
private native final int native_set_playback_rate(int sampleRateInHz);
private native final int native_get_playback_rate();
+ // floatArray must be a non-null array of length >= 2
+ // [0] is speed
+ // [1] is pitch
+ // intArray must be a non-null array of length >= 2
+ // [0] is audio fallback mode
+ // [1] is audio stretch mode
+ private native final void native_set_playback_settings(float[] floatArray, int[] intArray);
+ private native final void native_get_playback_settings(float[] floatArray, int[] intArray);
+
private native final int native_set_marker_pos(int marker);
private native final int native_get_marker_pos();
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index d82afdf..b0cd3e4 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -16,6 +16,9 @@
package android.media;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.media.Image;
@@ -30,6 +33,8 @@
import android.view.Surface;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
import java.util.Arrays;
@@ -253,7 +258,7 @@
* {@link #BUFFER_FLAG_END_OF_STREAM}.
*/
public void set(
- int newOffset, int newSize, long newTimeUs, int newFlags) {
+ int newOffset, int newSize, long newTimeUs, @BufferFlag int newFlags) {
offset = newOffset;
size = newSize;
presentationTimeUs = newTimeUs;
@@ -293,6 +298,7 @@
* be an empty buffer, whose sole purpose is to carry the end-of-stream
* marker.
*/
+ @BufferFlag
public int flags;
};
@@ -325,6 +331,18 @@
*/
public static final int BUFFER_FLAG_END_OF_STREAM = 4;
+ /** @hide */
+ @IntDef(
+ flag = true,
+ value = {
+ BUFFER_FLAG_SYNC_FRAME,
+ BUFFER_FLAG_KEY_FRAME,
+ BUFFER_FLAG_CODEC_CONFIG,
+ BUFFER_FLAG_END_OF_STREAM,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BufferFlag {}
+
private EventHandler mEventHandler;
private Callback mCallback;
@@ -339,13 +357,13 @@
private class EventHandler extends Handler {
private MediaCodec mCodec;
- public EventHandler(MediaCodec codec, Looper looper) {
+ public EventHandler(@NonNull MediaCodec codec, @NonNull Looper looper) {
super(looper);
mCodec = codec;
}
@Override
- public void handleMessage(Message msg) {
+ public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case EVENT_CALLBACK:
{
@@ -364,7 +382,7 @@
}
}
- private void handleCallback(Message msg) {
+ private void handleCallback(@NonNull Message msg) {
if (mCallback == null) {
return;
}
@@ -438,7 +456,8 @@
* @throws IllegalArgumentException if type is not a valid mime type.
* @throws NullPointerException if type is null.
*/
- public static MediaCodec createDecoderByType(String type)
+ @NonNull
+ public static MediaCodec createDecoderByType(@NonNull String type)
throws IOException {
return new MediaCodec(type, true /* nameIsType */, false /* encoder */);
}
@@ -450,7 +469,8 @@
* @throws IllegalArgumentException if type is not a valid mime type.
* @throws NullPointerException if type is null.
*/
- public static MediaCodec createEncoderByType(String type)
+ @NonNull
+ public static MediaCodec createEncoderByType(@NonNull String type)
throws IOException {
return new MediaCodec(type, true /* nameIsType */, true /* encoder */);
}
@@ -464,14 +484,15 @@
* @throws IllegalArgumentException if name is not valid.
* @throws NullPointerException if name is null.
*/
- public static MediaCodec createByCodecName(String name)
+ @NonNull
+ public static MediaCodec createByCodecName(@NonNull String name)
throws IOException {
return new MediaCodec(
name, false /* nameIsType */, false /* unused */);
}
private MediaCodec(
- String name, boolean nameIsType, boolean encoder) {
+ @NonNull String name, boolean nameIsType, boolean encoder) {
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
@@ -524,15 +545,26 @@
*/
public static final int CONFIGURE_FLAG_ENCODE = 1;
+ /** @hide */
+ @IntDef(flag = true, value = { CONFIGURE_FLAG_ENCODE })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ConfigureFlag {}
+
/**
* Configures a component.
*
* @param format The format of the input data (decoder) or the desired
- * format of the output data (encoder).
+ * format of the output data (encoder). Passing {@code null}
+ * as {@code format} is equivalent to passing an
+ * {@link MediaFormat#MediaFormat an empty mediaformat}.
* @param surface Specify a surface on which to render the output of this
- * decoder.
+ * decoder. Pass {@code null} as {@code surface} if the
+ * codec does not generate raw video output (e.g. not a video
+ * decoder) and/or if you want to configure the codec for
+ * {@link ByteBuffer} output.
* @param crypto Specify a crypto object to facilitate secure decryption
- * of the media data.
+ * of the media data. Pass {@code null} as {@code crypto} for
+ * non-secure codecs.
* @param flags Specify {@link #CONFIGURE_FLAG_ENCODE} to configure the
* component as an encoder.
* @throws IllegalArgumentException if the surface has been released (or is invalid),
@@ -544,14 +576,14 @@
* @throws CodecException upon codec error.
*/
public void configure(
- MediaFormat format,
- Surface surface, MediaCrypto crypto, int flags) {
- Map<String, Object> formatMap = format.getMap();
-
+ @Nullable MediaFormat format,
+ @Nullable Surface surface, @Nullable MediaCrypto crypto,
+ @ConfigureFlag int flags) {
String[] keys = null;
Object[] values = null;
if (format != null) {
+ Map<String, Object> formatMap = format.getMap();
keys = new String[formatMap.size()];
values = new Object[formatMap.size()];
@@ -578,11 +610,11 @@
native_configure(keys, values, surface, crypto, flags);
}
- private native final void native_setCallback(Callback cb);
+ private native final void native_setCallback(@Nullable Callback cb);
private native final void native_configure(
- String[] keys, Object[] values,
- Surface surface, MediaCrypto crypto, int flags);
+ @Nullable String[] keys, @Nullable Object[] values,
+ @Nullable Surface surface, @Nullable MediaCrypto crypto, @ConfigureFlag int flags);
/**
* Requests a Surface to use as the input to an encoder, in place of input buffers. This
@@ -596,6 +628,7 @@
* unexpected results.
* @throws IllegalStateException if not in the Configured state.
*/
+ @NonNull
public native final Surface createInputSurface();
/**
@@ -669,7 +702,7 @@
* Thrown when an internal codec error occurs.
*/
public final static class CodecException extends IllegalStateException {
- CodecException(int errorCode, int actionCode, String detailMessage, int reason) {
+ CodecException(int errorCode, int actionCode, @Nullable String detailMessage, int reason) {
super(detailMessage);
mErrorCode = errorCode;
mReason = reason;
@@ -704,6 +737,7 @@
* The reason could be one of {@link #REASON_HARDWARE} or {@link #REASON_RECLAIMED}.
*
*/
+ @ReasonCode
public int getReason() {
return mReason;
}
@@ -725,7 +759,7 @@
* since this string will not be localized or generally
* comprehensible to end-users.
*/
- public String getDiagnosticInfo() {
+ public @NonNull String getDiagnosticInfo() {
return mDiagnosticInfo;
}
@@ -742,6 +776,14 @@
*/
public static final int REASON_RECLAIMED = 1;
+ /** @hide */
+ @IntDef({
+ REASON_HARDWARE,
+ REASON_RECLAIMED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ReasonCode {}
+
/* Must be in sync with android_media_MediaCodec.cpp */
private final static int ACTION_TRANSIENT = 1;
private final static int ACTION_RECOVERABLE = 2;
@@ -756,7 +798,7 @@
* Thrown when a crypto error occurs while queueing a secure input buffer.
*/
public final static class CryptoException extends RuntimeException {
- public CryptoException(int errorCode, String detailMessage) {
+ public CryptoException(int errorCode, @Nullable String detailMessage) {
super(detailMessage);
mErrorCode = errorCode;
}
@@ -789,9 +831,20 @@
*/
public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4;
+ /** @hide */
+ @IntDef({
+ ERROR_NO_KEY,
+ ERROR_KEY_EXPIRED,
+ ERROR_RESOURCE_BUSY,
+ ERROR_INSUFFICIENT_OUTPUT_PROTECTION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CryptoErrorCode {}
+
/**
* Retrieve the error code associated with a CryptoException
*/
+ @CryptoErrorCode
public int getErrorCode() {
return mErrorCode;
}
@@ -885,10 +938,10 @@
public final static class CryptoInfo {
public void set(
int newNumSubSamples,
- int[] newNumBytesOfClearData,
- int[] newNumBytesOfEncryptedData,
- byte[] newKey,
- byte[] newIV,
+ @NonNull int[] newNumBytesOfClearData,
+ @NonNull int[] newNumBytesOfEncryptedData,
+ @NonNull byte[] newKey,
+ @NonNull byte[] newIV,
int newMode) {
numSubSamples = newNumSubSamples;
numBytesOfClearData = newNumBytesOfClearData;
@@ -970,7 +1023,7 @@
public final void queueSecureInputBuffer(
int index,
int offset,
- CryptoInfo info,
+ @NonNull CryptoInfo info,
long presentationTimeUs,
int flags) throws CryptoException {
synchronized(mBufferLock) {
@@ -989,7 +1042,7 @@
private native final void native_queueSecureInputBuffer(
int index,
int offset,
- CryptoInfo info,
+ @NonNull CryptoInfo info,
long presentationTimeUs,
int flags) throws CryptoException;
@@ -1043,6 +1096,15 @@
*/
public static final int INFO_OUTPUT_BUFFERS_CHANGED = -3;
+ /** @hide */
+ @IntDef({
+ INFO_TRY_AGAIN_LATER,
+ INFO_OUTPUT_FORMAT_CHANGED,
+ INFO_OUTPUT_BUFFERS_CHANGED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface OutputBufferInfo {}
+
/**
* Dequeue an output buffer, block at most "timeoutUs" microseconds.
* Returns the index of an output buffer that has been successfully
@@ -1053,8 +1115,9 @@
* or codec is configured in asynchronous mode.
* @throws MediaCodec.CodecException upon codec error.
*/
+ @OutputBufferInfo
public final int dequeueOutputBuffer(
- BufferInfo info, long timeoutUs) {
+ @NonNull BufferInfo info, long timeoutUs) {
int res = native_dequeueOutputBuffer(info, timeoutUs);
synchronized(mBufferLock) {
if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
@@ -1067,7 +1130,7 @@
}
private native final int native_dequeueOutputBuffer(
- BufferInfo info, long timeoutUs);
+ @NonNull BufferInfo info, long timeoutUs);
/**
* If you are done with a buffer, use this call to return the buffer to
@@ -1176,6 +1239,7 @@
* Configured state.
* @throws MediaCodec.CodecException upon codec error.
*/
+ @NonNull
public final MediaFormat getOutputFormat() {
return new MediaFormat(getFormatNative(false /* input */));
}
@@ -1190,6 +1254,7 @@
* Configured state.
* @throws MediaCodec.CodecException upon codec error.
*/
+ @NonNull
public final MediaFormat getInputFormat() {
return new MediaFormat(getFormatNative(true /* input */));
}
@@ -1203,12 +1268,15 @@
* @return the format for the output buffer, or null if the index
* is not a dequeued output buffer.
*/
+ @NonNull
public final MediaFormat getOutputFormat(int index) {
return new MediaFormat(getOutputFormatNative(index));
}
+ @NonNull
private native final Map<String, Object> getFormatNative(boolean input);
+ @NonNull
private native final Map<String, Object> getOutputFormatNative(int index);
// used to track dequeued buffers
@@ -1230,12 +1298,12 @@
}
}
- public void setImage(Image image) {
+ public void setImage(@Nullable Image image) {
free();
mImage = image;
}
- public void setByteBuffer(ByteBuffer buffer) {
+ public void setByteBuffer(@Nullable ByteBuffer buffer) {
free();
mByteBuffer = buffer;
}
@@ -1252,7 +1320,7 @@
}
}
- public void put(int index, ByteBuffer newBuffer) {
+ public void put(int index, @Nullable ByteBuffer newBuffer) {
CodecBuffer buffer = mMap.get(index);
if (buffer == null) { // likely
buffer = new CodecBuffer();
@@ -1261,7 +1329,7 @@
buffer.setByteBuffer(newBuffer);
}
- public void put(int index, Image newImage) {
+ public void put(int index, @Nullable Image newImage) {
CodecBuffer buffer = mMap.get(index);
if (buffer == null) { // likely
buffer = new CodecBuffer();
@@ -1285,7 +1353,7 @@
final private Object mBufferLock;
private final void invalidateByteBuffer(
- ByteBuffer[] buffers, int index) {
+ @Nullable ByteBuffer[] buffers, int index) {
if (buffers != null && index >= 0 && index < buffers.length) {
ByteBuffer buffer = buffers[index];
if (buffer != null) {
@@ -1295,7 +1363,7 @@
}
private final void validateInputByteBuffer(
- ByteBuffer[] buffers, int index) {
+ @Nullable ByteBuffer[] buffers, int index) {
if (buffers != null && index >= 0 && index < buffers.length) {
ByteBuffer buffer = buffers[index];
if (buffer != null) {
@@ -1306,7 +1374,7 @@
}
private final void revalidateByteBuffer(
- ByteBuffer[] buffers, int index) {
+ @Nullable ByteBuffer[] buffers, int index) {
synchronized(mBufferLock) {
if (buffers != null && index >= 0 && index < buffers.length) {
ByteBuffer buffer = buffers[index];
@@ -1318,7 +1386,7 @@
}
private final void validateOutputByteBuffer(
- ByteBuffer[] buffers, int index, BufferInfo info) {
+ @Nullable ByteBuffer[] buffers, int index, @NonNull BufferInfo info) {
if (buffers != null && index >= 0 && index < buffers.length) {
ByteBuffer buffer = buffers[index];
if (buffer != null) {
@@ -1328,7 +1396,7 @@
}
}
- private final void invalidateByteBuffers(ByteBuffer[] buffers) {
+ private final void invalidateByteBuffers(@Nullable ByteBuffer[] buffers) {
if (buffers != null) {
for (ByteBuffer buffer: buffers) {
if (buffer != null) {
@@ -1338,14 +1406,14 @@
}
}
- private final void freeByteBuffer(ByteBuffer buffer) {
+ private final void freeByteBuffer(@Nullable ByteBuffer buffer) {
if (buffer != null /* && buffer.isDirect() */) {
// all of our ByteBuffers are direct
java.nio.NioUtils.freeDirectBuffer(buffer);
}
}
- private final void freeByteBuffers(ByteBuffer[] buffers) {
+ private final void freeByteBuffers(@Nullable ByteBuffer[] buffers) {
if (buffers != null) {
for (ByteBuffer buffer: buffers) {
freeByteBuffer(buffer);
@@ -1388,13 +1456,14 @@
* @deprecated Use the new {@link #getInputBuffer} method instead
* each time an input buffer is dequeued.
*
- * <b>Note:</b>As of API 21, dequeued input buffers are
+ * <b>Note:</b> As of API 21, dequeued input buffers are
* automatically {@link java.nio.Buffer#clear cleared}.
*
* @throws IllegalStateException if not in the Executing state,
* or codec is configured in asynchronous mode.
* @throws MediaCodec.CodecException upon codec error.
*/
+ @NonNull
public ByteBuffer[] getInputBuffers() {
if (mCachedInputBuffers == null) {
throw new IllegalStateException();
@@ -1415,7 +1484,7 @@
* each time an output buffer is dequeued. This method is not
* supported if codec is configured in asynchronous mode.
*
- * <b>Note:</b>As of API 21, the position and limit of output
+ * <b>Note:</b> As of API 21, the position and limit of output
* buffers that are dequeued will be set to the valid data
* range.
*
@@ -1423,6 +1492,7 @@
* or codec is configured in asynchronous mode.
* @throws MediaCodec.CodecException upon codec error.
*/
+ @NonNull
public ByteBuffer[] getOutputBuffers() {
if (mCachedOutputBuffers == null) {
throw new IllegalStateException();
@@ -1449,6 +1519,7 @@
* @throws IllegalStateException if not in the Executing state.
* @throws MediaCodec.CodecException upon codec error.
*/
+ @Nullable
public ByteBuffer getInputBuffer(int index) {
ByteBuffer newBuffer = getBuffer(true /* input */, index);
synchronized(mBufferLock) {
@@ -1477,6 +1548,7 @@
* @throws IllegalStateException if not in the Executing state.
* @throws MediaCodec.CodecException upon codec error.
*/
+ @Nullable
public Image getInputImage(int index) {
Image newImage = getImage(true /* input */, index);
synchronized(mBufferLock) {
@@ -1505,6 +1577,7 @@
* @throws IllegalStateException if not in the Executing state.
* @throws MediaCodec.CodecException upon codec error.
*/
+ @Nullable
public ByteBuffer getOutputBuffer(int index) {
ByteBuffer newBuffer = getBuffer(false /* input */, index);
synchronized(mBufferLock) {
@@ -1532,6 +1605,7 @@
* @throws IllegalStateException if not in the Executing state.
* @throws MediaCodec.CodecException upon codec error.
*/
+ @Nullable
public Image getOutputImage(int index) {
Image newImage = getImage(false /* input */, index);
synchronized(mBufferLock) {
@@ -1552,19 +1626,28 @@
*/
public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2;
+ /** @hide */
+ @IntDef({
+ VIDEO_SCALING_MODE_SCALE_TO_FIT,
+ VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VideoScalingMode {}
+
/**
* If a surface has been specified in a previous call to {@link #configure}
* specifies the scaling mode to use. The default is "scale to fit".
* @throws IllegalArgumentException if mode is not recognized.
* @throws IllegalStateException if in the Uninitialized state.
*/
- public native final void setVideoScalingMode(int mode);
+ public native final void setVideoScalingMode(@VideoScalingMode int mode);
/**
* Get the component name. If the codec was created by createDecoderByType
* or createEncoderByType, what component is chosen is not known beforehand.
* @throws IllegalStateException if in the Uninitialized state.
*/
+ @NonNull
public native final String getName();
/**
@@ -1592,9 +1675,12 @@
/**
* Communicate additional parameter changes to the component instance.
+ * <b>Note:</b> Some of these parameter changes may silently fail to apply.
+ *
+ * @param params The bundle of parameters to set.
* @throws IllegalStateException if in the Uninitialized state.
*/
- public final void setParameters(Bundle params) {
+ public final void setParameters(@Nullable Bundle params) {
if (params == null) {
return;
}
@@ -1626,9 +1712,11 @@
* {@code flush}, you must call {@link #start} to "resume" receiving input buffers,
* even if an input surface was created.
*
- * @param cb The callback that will run.
+ * @param cb The callback that will run. Use {@code null} to clear a previously
+ * set callback (before {@link #configure configure} is called and run
+ * in synchronous mode).
*/
- public void setCallback(/* MediaCodec. */ Callback cb) {
+ public void setCallback(@Nullable /* MediaCodec. */ Callback cb) {
if (mEventHandler != null) {
// set java callback on handler
Message msg = mEventHandler.obtainMessage(EVENT_SET_CALLBACK, 0, 0, cb);
@@ -1652,7 +1740,7 @@
* @param codec The MediaCodec object.
* @param index The index of the available input buffer.
*/
- public abstract void onInputBufferAvailable(MediaCodec codec, int index);
+ public abstract void onInputBufferAvailable(@NonNull MediaCodec codec, int index);
/**
* Called when an output buffer becomes available.
@@ -1661,7 +1749,8 @@
* @param index The index of the available output buffer.
* @param info Info regarding the available output buffer {@link MediaCodec.BufferInfo}.
*/
- public abstract void onOutputBufferAvailable(MediaCodec codec, int index, BufferInfo info);
+ public abstract void onOutputBufferAvailable(
+ @NonNull MediaCodec codec, int index, @NonNull BufferInfo info);
/**
* Called when the MediaCodec encountered an error
@@ -1669,7 +1758,7 @@
* @param codec The MediaCodec object.
* @param e The {@link MediaCodec.CodecException} object describing the error.
*/
- public abstract void onError(MediaCodec codec, CodecException e);
+ public abstract void onError(@NonNull MediaCodec codec, @NonNull CodecException e);
/**
* Called when the output format has changed
@@ -1677,18 +1766,19 @@
* @param codec The MediaCodec object.
* @param format The new output format.
*/
- public abstract void onOutputFormatChanged(MediaCodec codec, MediaFormat format);
+ public abstract void onOutputFormatChanged(
+ @NonNull MediaCodec codec, @NonNull MediaFormat format);
}
private void postEventFromNative(
- int what, int arg1, int arg2, Object obj) {
+ int what, int arg1, int arg2, @Nullable Object obj) {
if (mEventHandler != null) {
Message msg = mEventHandler.obtainMessage(what, arg1, arg2, obj);
mEventHandler.sendMessage(msg);
}
}
- private native final void setParameters(String[] keys, Object[] values);
+ private native final void setParameters(@NonNull String[] keys, @NonNull Object[] values);
/**
* Get the codec info. If the codec was created by createDecoderByType
@@ -1696,20 +1786,24 @@
* and thus the caller does not have the MediaCodecInfo.
* @throws IllegalStateException if in the Uninitialized state.
*/
+ @NonNull
public MediaCodecInfo getCodecInfo() {
return MediaCodecList.getInfoFor(getName());
}
+ @NonNull
private native final ByteBuffer[] getBuffers(boolean input);
+ @Nullable
private native final ByteBuffer getBuffer(boolean input, int index);
+ @Nullable
private native final Image getImage(boolean input, int index);
private static native final void native_init();
private native final void native_setup(
- String name, boolean nameIsType, boolean encoder);
+ @NonNull String name, boolean nameIsType, boolean encoder);
private native final void native_finalize();
@@ -1756,6 +1850,7 @@
return mTimestamp;
}
+ @NonNull
public Plane[] getPlanes() {
checkValid();
return Arrays.copyOf(mPlanes, mPlanes.length);
@@ -1774,7 +1869,7 @@
* The crop rectangle specifies the region of valid pixels in the image,
* using coordinates in the largest-resolution plane.
*/
- public void setCropRect(Rect cropRect) {
+ public void setCropRect(@Nullable Rect cropRect) {
if (mIsReadOnly) {
throw new ReadOnlyBufferException();
}
@@ -1787,7 +1882,7 @@
}
}
- private int readInt(ByteBuffer buffer, boolean asLong) {
+ private int readInt(@NonNull ByteBuffer buffer, boolean asLong) {
if (asLong) {
return (int)buffer.getLong();
} else {
@@ -1796,8 +1891,8 @@
}
public MediaImage(
- ByteBuffer buffer, ByteBuffer info, boolean readOnly,
- long timestamp, int xOffset, int yOffset, Rect cropRect) {
+ @NonNull ByteBuffer buffer, @NonNull ByteBuffer info, boolean readOnly,
+ long timestamp, int xOffset, int yOffset, @Nullable Rect cropRect) {
mFormat = ImageFormat.YUV_420_888;
mTimestamp = timestamp;
mIsValid = true;
@@ -1863,7 +1958,7 @@
}
private class MediaPlane extends Plane {
- public MediaPlane(ByteBuffer buffer, int rowInc, int colInc) {
+ public MediaPlane(@NonNull ByteBuffer buffer, int rowInc, int colInc) {
mData = buffer;
mRowInc = rowInc;
mColInc = colInc;
@@ -1882,6 +1977,7 @@
}
@Override
+ @NonNull
public ByteBuffer getBuffer() {
checkValid();
return mData;
diff --git a/media/java/android/media/MediaCrypto.java b/media/java/android/media/MediaCrypto.java
index da81b37..474d8b9 100644
--- a/media/java/android/media/MediaCrypto.java
+++ b/media/java/android/media/MediaCrypto.java
@@ -16,6 +16,7 @@
package android.media;
+import android.annotation.NonNull;
import android.media.MediaCryptoException;
import java.util.UUID;
@@ -34,11 +35,12 @@
* this device.
* @param uuid The UUID of the crypto scheme.
*/
- public static final boolean isCryptoSchemeSupported(UUID uuid) {
+ public static final boolean isCryptoSchemeSupported(@NonNull UUID uuid) {
return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid));
}
- private static final byte[] getByteArrayFromUUID(UUID uuid) {
+ @NonNull
+ private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
long msb = uuid.getMostSignificantBits();
long lsb = uuid.getLeastSignificantBits();
@@ -51,7 +53,7 @@
return uuidBytes;
}
- private static final native boolean isCryptoSchemeSupportedNative(byte[] uuid);
+ private static final native boolean isCryptoSchemeSupportedNative(@NonNull byte[] uuid);
/**
* Instantiate a MediaCrypto object using opaque, crypto scheme specific
@@ -59,7 +61,7 @@
* @param uuid The UUID of the crypto scheme.
* @param initData Opaque initialization data specific to the crypto scheme.
*/
- public MediaCrypto(UUID uuid, byte[] initData) throws MediaCryptoException {
+ public MediaCrypto(@NonNull UUID uuid, @NonNull byte[] initData) throws MediaCryptoException {
native_setup(getByteArrayFromUUID(uuid), initData);
}
@@ -68,7 +70,7 @@
* to decode data of the given mime type.
* @param mime The mime type of the media data
*/
- public final native boolean requiresSecureDecoderComponent(String mime);
+ public final native boolean requiresSecureDecoderComponent(@NonNull String mime);
/**
* Associate a MediaDrm session with this MediaCrypto instance. The
@@ -81,7 +83,7 @@
* MediaCrypto instance
* @throws MediaCryptoException on failure to set the sessionId
*/
- public final native void setMediaDrmSession(byte[] sessionId)
+ public final native void setMediaDrmSession(@NonNull byte[] sessionId)
throws MediaCryptoException;
@Override
@@ -92,7 +94,7 @@
public native final void release();
private static native final void native_init();
- private native final void native_setup(byte[] uuid, byte[] initData)
+ private native final void native_setup(@NonNull byte[] uuid, @NonNull byte[] initData)
throws MediaCryptoException;
private native final void native_finalize();
diff --git a/media/java/android/media/MediaCryptoException.java b/media/java/android/media/MediaCryptoException.java
index 703e96f..32ddf47 100644
--- a/media/java/android/media/MediaCryptoException.java
+++ b/media/java/android/media/MediaCryptoException.java
@@ -16,12 +16,14 @@
package android.media;
+import android.annotation.Nullable;
+
/**
* Exception thrown if MediaCrypto object could not be instantiated or
* if unable to perform an operation on the MediaCrypto object.
*/
public final class MediaCryptoException extends Exception {
- public MediaCryptoException(String detailMessage) {
+ public MediaCryptoException(@Nullable String detailMessage) {
super(detailMessage);
}
}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index fc5fc43..acff301 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -16,11 +16,17 @@
package android.media;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.os.Handler;
import android.os.Looper;
@@ -104,6 +110,8 @@
private static final String PERMISSION = android.Manifest.permission.ACCESS_DRM_CERTIFICATES;
private EventHandler mEventHandler;
+ private EventHandler mOnKeysChangeEventHandler;
+ private EventHandler mOnExpirationUpdateEventHandler;
private OnEventListener mOnEventListener;
private OnKeysChangeListener mOnKeysChangeListener;
private OnExpirationUpdateListener mOnExpirationUpdateListener;
@@ -124,12 +132,20 @@
*/
public static final int CERTIFICATE_TYPE_X509 = 1;
+ /** @hide */
+ @IntDef({
+ CERTIFICATE_TYPE_NONE,
+ CERTIFICATE_TYPE_X509,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CertificateType {}
+
/**
* Query if the given scheme identified by its UUID is supported on
* this device.
* @param uuid The UUID of the crypto scheme.
*/
- public static final boolean isCryptoSchemeSupported(UUID uuid) {
+ public static final boolean isCryptoSchemeSupported(@NonNull UUID uuid) {
return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), null);
}
@@ -141,11 +157,12 @@
* @param mimeType The MIME type of the media container, e.g. "video/mp4"
* or "video/webm"
*/
- public static final boolean isCryptoSchemeSupported(UUID uuid, String mimeType) {
+ public static final boolean isCryptoSchemeSupported(
+ @NonNull UUID uuid, @NonNull String mimeType) {
return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), mimeType);
}
- private static final byte[] getByteArrayFromUUID(UUID uuid) {
+ private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
long msb = uuid.getMostSignificantBits();
long lsb = uuid.getLeastSignificantBits();
@@ -158,8 +175,8 @@
return uuidBytes;
}
- private static final native boolean isCryptoSchemeSupportedNative(byte[] uuid,
- String mimeType);
+ private static final native boolean isCryptoSchemeSupportedNative(
+ @NonNull byte[] uuid, @Nullable String mimeType);
/**
* Instantiate a MediaDrm object
@@ -169,7 +186,7 @@
* @throws UnsupportedSchemeException if the device does not support the
* specified scheme UUID
*/
- public MediaDrm(UUID uuid) throws UnsupportedSchemeException {
+ public MediaDrm(@NonNull UUID uuid) throws UnsupportedSchemeException {
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
@@ -198,7 +215,7 @@
/**
* @hide
*/
- public MediaDrmStateException(int errorCode, String detailMessage) {
+ public MediaDrmStateException(int errorCode, @Nullable String detailMessage) {
super(detailMessage);
mErrorCode = errorCode;
@@ -224,6 +241,7 @@
* since this string will not be localized or generally comprehensible
* to end-users.
*/
+ @NonNull
public String getDiagnosticInfo() {
return mDiagnosticInfo;
}
@@ -233,13 +251,13 @@
* Register a callback to be invoked when a session expiration update
* occurs. The app's OnExpirationUpdateListener will be notified
* when the expiration time of the keys in the session have changed.
- * @param listener the callback that will be run
+ * @param listener the callback that will be run, or {@code null} to unregister the
+ * previously registered callback.
* @param handler the handler on which the listener should be invoked, or
- * null if the listener should be invoked on the calling thread's looper.
+ * {@code null} if the listener should be invoked on the calling thread's looper.
*/
- public void setOnExpirationUpdateListener(OnExpirationUpdateListener listener,
- Handler handler)
- {
+ public void setOnExpirationUpdateListener(
+ @Nullable OnExpirationUpdateListener listener, @Nullable Handler handler) {
if (listener != null) {
Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
if (looper != null) {
@@ -266,20 +284,21 @@
* @param expirationTime the new expiration time for the keys in the session.
* The time is in milliseconds, relative to the Unix epoch.
*/
- void onExpirationUpdate(MediaDrm md, byte[] sessionId, long expirationTime);
+ void onExpirationUpdate(
+ @NonNull MediaDrm md, @NonNull byte[] sessionId, long expirationTime);
}
/**
* Register a callback to be invoked when the state of keys in a session
* change, e.g. when a license update occurs or when a license expires.
*
- * @param listener the callback that will be run when key status changes
+ * @param listener the callback that will be run when key status changes, or
+ * {@code null} to unregister the previously registered callback.
* @param handler the handler on which the listener should be invoked, or
* null if the listener should be invoked on the calling thread's looper.
*/
- public void setOnKeysChangeListener(OnKeysChangeListener listener,
- Handler handler)
- {
+ public void setOnKeysChangeListener(
+ @Nullable OnKeysChangeListener listener, @Nullable Handler handler) {
if (listener != null) {
Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
if (looper != null) {
@@ -309,7 +328,9 @@
* which may trigger an attempt to resume playback on the media stream
* if it is currently blocked waiting for a key.
*/
- void onKeysChange(MediaDrm md, byte[] sessionId, List<KeyStatus> keyInformation,
+ void onKeysChange(
+ @NonNull MediaDrm md, @NonNull byte[] sessionId,
+ @NonNull List<KeyStatus> keyInformation,
boolean hasNewUsableKey);
}
@@ -344,6 +365,16 @@
*/
public static final int KEY_STATUS_INTERNAL_ERROR = 4;
+ /** @hide */
+ @IntDef({
+ KEY_STATUS_USABLE,
+ KEY_STATUS_EXPIRED,
+ KEY_STATUS_OUTPUT_NOT_ALLOWED,
+ KEY_STATUS_PENDING,
+ KEY_STATUS_INTERNAL_ERROR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface KeyStatusCode {}
/**
* Defines the status of a key.
@@ -355,7 +386,7 @@
private final byte[] mKeyId;
private final int mStatusCode;
- KeyStatus(byte[] keyId, int statusCode) {
+ KeyStatus(@NonNull byte[] keyId, @KeyStatusCode int statusCode) {
mKeyId = keyId;
mStatusCode = statusCode;
}
@@ -363,20 +394,23 @@
/**
* Returns the status code for the key
*/
+ @KeyStatusCode
public int getStatusCode() { return mStatusCode; }
/**
* Returns the id for the key
*/
+ @NonNull
public byte[] getKeyId() { return mKeyId; }
}
/**
* Register a callback to be invoked when an event occurs
*
- * @param listener the callback that will be run
+ * @param listener the callback that will be run. Use {@code null} to
+ * stop receiving event callbacks.
*/
- public void setOnEventListener(OnEventListener listener)
+ public void setOnEventListener(@Nullable OnEventListener listener)
{
mOnEventListener = listener;
}
@@ -391,12 +425,16 @@
* Called when an event occurs that requires the app to be notified
*
* @param md the MediaDrm object on which the event occurred
- * @param sessionId the DRM session ID on which the event occurred
+ * @param sessionId the DRM session ID on which the event occurred,
+ * or {@code null} if there is no session ID associated with the event.
* @param event indicates the event type
* @param extra an secondary error code
* @param data optional byte array of data that may be associated with the event
*/
- void onEvent(MediaDrm md, byte[] sessionId, int event, int extra, byte[] data);
+ void onEvent(
+ @NonNull MediaDrm md, @Nullable byte[] sessionId,
+ @DrmEvent int event, int extra,
+ @Nullable byte[] data);
}
/**
@@ -433,6 +471,17 @@
*/
public static final int EVENT_SESSION_RECLAIMED = 5;
+ /** @hide */
+ @IntDef({
+ EVENT_PROVISION_REQUIRED,
+ EVENT_KEY_REQUIRED,
+ EVENT_KEY_EXPIRED,
+ EVENT_VENDOR_DEFINED,
+ EVENT_SESSION_RECLAIMED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DrmEvent {}
+
private static final int DRM_EVENT = 200;
private static final int EXPIRATION_UPDATE = 201;
private static final int KEYS_CHANGE = 202;
@@ -441,13 +490,13 @@
{
private MediaDrm mMediaDrm;
- public EventHandler(MediaDrm md, Looper looper) {
+ public EventHandler(@NonNull MediaDrm md, @NonNull Looper looper) {
super(looper);
mMediaDrm = md;
}
@Override
- public void handleMessage(Message msg) {
+ public void handleMessage(@NonNull Message msg) {
if (mMediaDrm.mNativeContext == 0) {
Log.w(TAG, "MediaDrm went away with unhandled events");
return;
@@ -516,7 +565,8 @@
/**
* Parse a list of KeyStatus objects from an event parcel
*/
- private List<KeyStatus> keyStatusListFromParcel(Parcel parcel) {
+ @NonNull
+ private List<KeyStatus> keyStatusListFromParcel(@NonNull Parcel parcel) {
int nelems = parcel.readInt();
List<KeyStatus> keyStatusList = new ArrayList(nelems);
while (nelems-- > 0) {
@@ -534,8 +584,8 @@
* code is safe from the object disappearing from underneath it. (This is
* the cookie passed to native_setup().)
*/
- private static void postEventFromNative(Object mediadrm_ref,
- int what, int eventType, int extra, Object obj)
+ private static void postEventFromNative(@NonNull Object mediadrm_ref,
+ int what, int eventType, int extra, @Nullable Object obj)
{
MediaDrm md = (MediaDrm)((WeakReference<MediaDrm>)mediadrm_ref).get();
if (md == null) {
@@ -553,6 +603,7 @@
* @throws NotProvisionedException if provisioning is needed
* @throws ResourceBusyException if required resources are in use
*/
+ @NonNull
public native byte[] openSession() throws NotProvisionedException,
ResourceBusyException;
@@ -560,7 +611,7 @@
* Close a session on the MediaDrm object that was previously opened
* with {@link #openSession}.
*/
- public native void closeSession(byte[] sessionId);
+ public native void closeSession(@NonNull byte[] sessionId);
/**
* This key request type species that the keys will be for online use, they will
@@ -580,6 +631,15 @@
*/
public static final int KEY_TYPE_RELEASE = 3;
+ /** @hide */
+ @IntDef({
+ KEY_TYPE_STREAMING,
+ KEY_TYPE_OFFLINE,
+ KEY_TYPE_RELEASE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface KeyType {}
+
/**
* Key request type is initial license request
*/
@@ -595,6 +655,15 @@
*/
public static final int REQUEST_TYPE_RELEASE = 2;
+ /** @hide */
+ @IntDef({
+ REQUEST_TYPE_INITIAL,
+ REQUEST_TYPE_RENEWAL,
+ REQUEST_TYPE_RELEASE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RequestType {}
+
/**
* Contains the opaque data an app uses to request keys from a license server
*/
@@ -608,18 +677,38 @@
/**
* Get the opaque message data
*/
- public byte[] getData() { return mData; }
+ @NonNull
+ public byte[] getData() {
+ if (mData == null) {
+ // this should never happen as mData is initialized in
+ // JNI after construction of the KeyRequest object. The check
+ // is needed here to guarantee @NonNull annotation.
+ throw new RuntimeException("KeyRequest is not initialized");
+ }
+ return mData;
+ }
/**
* Get the default URL to use when sending the key request message to a
* server, if known. The app may prefer to use a different license
* server URL from other sources.
+ * This method returns an empty string if the default URL is not known.
*/
- public String getDefaultUrl() { return mDefaultUrl; }
+ @NonNull
+ public String getDefaultUrl() {
+ if (mDefaultUrl == null) {
+ // this should never happen as mDefaultUrl is initialized in
+ // JNI after construction of the KeyRequest object. The check
+ // is needed here to guarantee @NonNull annotation.
+ throw new RuntimeException("KeyRequest is not initialized");
+ }
+ return mDefaultUrl;
+ }
/**
* Get the type of the request
*/
+ @RequestType
public int getRequestType() { return mRequestType; }
};
@@ -652,12 +741,15 @@
* keys, which are identified by a keySetId.
* @param optionalParameters are included in the key request message to
* allow a client application to provide additional message parameters to the server.
- *
+ * This may be {@code null} if no additional parameters are to be sent.
* @throws NotProvisionedException if reprovisioning is needed, due to a
* problem with the certifcate
*/
- public native KeyRequest getKeyRequest(byte[] scope, byte[] init,
- String mimeType, int keyType, HashMap<String, String> optionalParameters)
+ @NonNull
+ public native KeyRequest getKeyRequest(
+ @NonNull byte[] scope, @Nullable byte[] init,
+ @Nullable String mimeType, @KeyType int keyType,
+ @Nullable HashMap<String, String> optionalParameters)
throws NotProvisionedException;
@@ -680,7 +772,9 @@
* @throws DeniedByServerException if the response indicates that the
* server rejected the request
*/
- public native byte[] provideKeyResponse(byte[] scope, byte[] response)
+ @Nullable
+ public native byte[] provideKeyResponse(
+ @NonNull byte[] scope, @NonNull byte[] response)
throws NotProvisionedException, DeniedByServerException;
@@ -691,14 +785,14 @@
* @param sessionId the session ID for the DRM session
* @param keySetId identifies the saved key set to restore
*/
- public native void restoreKeys(byte[] sessionId, byte[] keySetId);
+ public native void restoreKeys(@NonNull byte[] sessionId, @NonNull byte[] keySetId);
/**
* Remove the current keys from a session.
*
* @param sessionId the session ID for the DRM session
*/
- public native void removeKeys(byte[] sessionId);
+ public native void removeKeys(@NonNull byte[] sessionId);
/**
* Request an informative description of the key status for the session. The status is
@@ -709,7 +803,8 @@
*
* @param sessionId the session ID for the DRM session
*/
- public native HashMap<String, String> queryKeyStatus(byte[] sessionId);
+ @NonNull
+ public native HashMap<String, String> queryKeyStatus(@NonNull byte[] sessionId);
/**
* Contains the opaque data an app uses to request a certificate from a provisioning
@@ -721,14 +816,33 @@
/**
* Get the opaque message data
*/
- public byte[] getData() { return mData; }
+ @NonNull
+ public byte[] getData() {
+ if (mData == null) {
+ // this should never happen as mData is initialized in
+ // JNI after construction of the KeyRequest object. The check
+ // is needed here to guarantee @NonNull annotation.
+ throw new RuntimeException("ProvisionRequest is not initialized");
+ }
+ return mData;
+ }
/**
* Get the default URL to use when sending the provision request
* message to a server, if known. The app may prefer to use a different
* provisioning server URL obtained from other sources.
+ * This method returns an empty string if the default URL is not known.
*/
- public String getDefaultUrl() { return mDefaultUrl; }
+ @NonNull
+ public String getDefaultUrl() {
+ if (mDefaultUrl == null) {
+ // this should never happen as mDefaultUrl is initialized in
+ // JNI after construction of the ProvisionRequest object. The check
+ // is needed here to guarantee @NonNull annotation.
+ throw new RuntimeException("ProvisionRequest is not initialized");
+ }
+ return mDefaultUrl;
+ }
private byte[] mData;
private String mDefaultUrl;
@@ -743,12 +857,14 @@
* is returned in ProvisionRequest.data. The recommended URL to deliver the provision
* request to is returned in ProvisionRequest.defaultUrl.
*/
+ @NonNull
public ProvisionRequest getProvisionRequest() {
return getProvisionRequestNative(CERTIFICATE_TYPE_NONE, "");
}
+ @NonNull
private native ProvisionRequest getProvisionRequestNative(int certType,
- String certAuthority);
+ @NonNull String certAuthority);
/**
* After a provision response is received by the app, it is provided to the DRM
@@ -760,12 +876,14 @@
* @throws DeniedByServerException if the response indicates that the
* server rejected the request
*/
- public void provideProvisionResponse(byte[] response)
+ public void provideProvisionResponse(@NonNull byte[] response)
throws DeniedByServerException {
provideProvisionResponseNative(response);
}
- private native Certificate provideProvisionResponseNative(byte[] response)
+ @NonNull
+ /* could there be a valid response with 0-sized certificate or key? */
+ private native Certificate provideProvisionResponseNative(@NonNull byte[] response)
throws DeniedByServerException;
/**
@@ -795,6 +913,7 @@
* record on the client is only removed after positive confirmation that the server
* received the message using releaseSecureStops().
*/
+ @NonNull
public native List<byte[]> getSecureStops();
/**
@@ -802,7 +921,8 @@
*
* @param ssid - The secure stop ID provided by the license server.
*/
- public native byte[] getSecureStop(byte[] ssid);
+ @NonNull
+ public native byte[] getSecureStop(@NonNull byte[] ssid);
/**
* Process the SecureStop server response message ssRelease. After authenticating
@@ -810,7 +930,7 @@
*
* @param ssRelease the server response indicating which secure stops to release
*/
- public native void releaseSecureStops(byte[] ssRelease);
+ public native void releaseSecureStops(@NonNull byte[] ssRelease);
/**
* Remove all secure stops without requiring interaction with the server.
@@ -839,6 +959,16 @@
*/
public static final String PROPERTY_ALGORITHMS = "algorithms";
+ /** @hide */
+ @StringDef({
+ PROPERTY_VENDOR,
+ PROPERTY_VERSION,
+ PROPERTY_DESCRIPTION,
+ PROPERTY_ALGORITHMS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StringProperty {}
+
/**
* Read a DRM engine plugin String property value, given the property name string.
* <p>
@@ -846,51 +976,68 @@
* {@link #PROPERTY_VENDOR}, {@link #PROPERTY_VERSION},
* {@link #PROPERTY_DESCRIPTION}, {@link #PROPERTY_ALGORITHMS}
*/
- public native String getPropertyString(String propertyName);
-
+ /* FIXME this throws IllegalStateException for invalid property names */
+ @NonNull
+ public native String getPropertyString(@NonNull @StringProperty String propertyName);
/**
* Byte array property name: the device unique identifier is established during
* device provisioning and provides a means of uniquely identifying each device.
*/
+ /* FIXME this throws IllegalStateException for invalid property names */
public static final String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
+ /** @hide */
+ @StringDef({
+ PROPERTY_DEVICE_UNIQUE_ID,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ArrayProperty {}
+
/**
* Read a DRM engine plugin byte array property value, given the property name string.
* <p>
* Standard fields names are {@link #PROPERTY_DEVICE_UNIQUE_ID}
*/
- public native byte[] getPropertyByteArray(String propertyName);
-
+ @NonNull
+ public native byte[] getPropertyByteArray(@ArrayProperty String propertyName);
/**
* Set a DRM engine plugin String property value.
*/
- public native void setPropertyString(String propertyName, String value);
+ public native void setPropertyString(
+ @StringProperty String propertyName, @NonNull String value);
/**
* Set a DRM engine plugin byte array property value.
*/
- public native void setPropertyByteArray(String propertyName, byte[] value);
+ public native void setPropertyByteArray(
+ @ArrayProperty String propertyName, @NonNull byte[] value);
+ private static final native void setCipherAlgorithmNative(
+ @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
- private static final native void setCipherAlgorithmNative(MediaDrm drm, byte[] sessionId,
- String algorithm);
+ private static final native void setMacAlgorithmNative(
+ @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
- private static final native void setMacAlgorithmNative(MediaDrm drm, byte[] sessionId,
- String algorithm);
+ @NonNull
+ private static final native byte[] encryptNative(
+ @NonNull MediaDrm drm, @NonNull byte[] sessionId,
+ @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv);
- private static final native byte[] encryptNative(MediaDrm drm, byte[] sessionId,
- byte[] keyId, byte[] input, byte[] iv);
+ @NonNull
+ private static final native byte[] decryptNative(
+ @NonNull MediaDrm drm, @NonNull byte[] sessionId,
+ @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv);
- private static final native byte[] decryptNative(MediaDrm drm, byte[] sessionId,
- byte[] keyId, byte[] input, byte[] iv);
+ @NonNull
+ private static final native byte[] signNative(
+ @NonNull MediaDrm drm, @NonNull byte[] sessionId,
+ @NonNull byte[] keyId, @NonNull byte[] message);
- private static final native byte[] signNative(MediaDrm drm, byte[] sessionId,
- byte[] keyId, byte[] message);
-
- private static final native boolean verifyNative(MediaDrm drm, byte[] sessionId,
- byte[] keyId, byte[] message, byte[] signature);
+ private static final native boolean verifyNative(
+ @NonNull MediaDrm drm, @NonNull byte[] sessionId,
+ @NonNull byte[] keyId, @NonNull byte[] message, @NonNull byte[] signature);
/**
* In addition to supporting decryption of DASH Common Encrypted Media, the
@@ -919,8 +1066,8 @@
private MediaDrm mDrm;
private byte[] mSessionId;
- CryptoSession(MediaDrm drm, byte[] sessionId,
- String cipherAlgorithm, String macAlgorithm)
+ CryptoSession(@NonNull MediaDrm drm, @NonNull byte[] sessionId,
+ @NonNull String cipherAlgorithm, @NonNull String macAlgorithm)
{
mSessionId = sessionId;
mDrm = drm;
@@ -935,7 +1082,9 @@
* @param input the data to encrypt
* @param iv the initialization vector to use for the cipher
*/
- public byte[] encrypt(byte[] keyid, byte[] input, byte[] iv) {
+ @NonNull
+ public byte[] encrypt(
+ @NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) {
return encryptNative(mDrm, mSessionId, keyid, input, iv);
}
@@ -946,7 +1095,9 @@
* @param input the data to encrypt
* @param iv the initialization vector to use for the cipher
*/
- public byte[] decrypt(byte[] keyid, byte[] input, byte[] iv) {
+ @NonNull
+ public byte[] decrypt(
+ @NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) {
return decryptNative(mDrm, mSessionId, keyid, input, iv);
}
@@ -956,7 +1107,8 @@
* @param keyid specifies which key to use
* @param message the data for which a signature is to be computed
*/
- public byte[] sign(byte[] keyid, byte[] message) {
+ @NonNull
+ public byte[] sign(@NonNull byte[] keyid, @NonNull byte[] message) {
return signNative(mDrm, mSessionId, keyid, message);
}
@@ -969,7 +1121,8 @@
* @param signature the reference signature which will be compared with the
* computed signature
*/
- public boolean verify(byte[] keyid, byte[] message, byte[] signature) {
+ public boolean verify(
+ @NonNull byte[] keyid, @NonNull byte[] message, @NonNull byte[] signature) {
return verifyNative(mDrm, mSessionId, keyid, message, signature);
}
};
@@ -994,8 +1147,9 @@
* using the method {@link #getPropertyString} with the property name
* "algorithms".
*/
- public CryptoSession getCryptoSession(byte[] sessionId,
- String cipherAlgorithm, String macAlgorithm)
+ public CryptoSession getCryptoSession(
+ @NonNull byte[] sessionId,
+ @NonNull String cipherAlgorithm, @NonNull String macAlgorithm)
{
return new CryptoSession(this, sessionId, cipherAlgorithm, macAlgorithm);
}
@@ -1010,7 +1164,7 @@
private byte[] mData;
private String mDefaultUrl;
- CertificateRequest(byte[] data, String defaultUrl) {
+ CertificateRequest(@NonNull byte[] data, @NonNull String defaultUrl) {
mData = data;
mDefaultUrl = defaultUrl;
}
@@ -1018,6 +1172,7 @@
/**
* Get the opaque message data
*/
+ @NonNull
public byte[] getData() { return mData; }
/**
@@ -1025,6 +1180,7 @@
* message to a server, if known. The app may prefer to use a different
* certificate server URL obtained from other sources.
*/
+ @NonNull
public String getDefaultUrl() { return mDefaultUrl; }
}
@@ -1040,8 +1196,9 @@
*
* @hide - not part of the public API at this time
*/
- public CertificateRequest getCertificateRequest(int certType,
- String certAuthority)
+ @NonNull
+ public CertificateRequest getCertificateRequest(
+ @CertificateType int certType, @NonNull String certAuthority)
{
ProvisionRequest provisionRequest = getProvisionRequestNative(certType, certAuthority);
return new CertificateRequest(provisionRequest.getData(),
@@ -1060,12 +1217,30 @@
/**
* Get the wrapped private key data
*/
- public byte[] getWrappedPrivateKey() { return mWrappedKey; }
+ @NonNull
+ public byte[] getWrappedPrivateKey() {
+ if (mWrappedKey == null) {
+ // this should never happen as mWrappedKey is initialized in
+ // JNI after construction of the KeyRequest object. The check
+ // is needed here to guarantee @NonNull annotation.
+ throw new RuntimeException("Cerfificate is not initialized");
+ }
+ return mWrappedKey;
+ }
/**
* Get the PEM-encoded certificate chain
*/
- public byte[] getContent() { return mCertificateData; }
+ @NonNull
+ public byte[] getContent() {
+ if (mCertificateData == null) {
+ // this should never happen as mCertificateData is initialized in
+ // JNI after construction of the KeyRequest object. The check
+ // is needed here to guarantee @NonNull annotation.
+ throw new RuntimeException("Cerfificate is not initialized");
+ }
+ return mCertificateData;
+ }
private byte[] mWrappedKey;
private byte[] mCertificateData;
@@ -1089,13 +1264,16 @@
*
* @hide - not part of the public API at this time
*/
- public Certificate provideCertificateResponse(byte[] response)
+ @NonNull
+ public Certificate provideCertificateResponse(@NonNull byte[] response)
throws DeniedByServerException {
return provideProvisionResponseNative(response);
}
- private static final native byte[] signRSANative(MediaDrm drm, byte[] sessionId,
- String algorithm, byte[] wrappedKey, byte[] message);
+ @NonNull
+ private static final native byte[] signRSANative(
+ @NonNull MediaDrm drm, @NonNull byte[] sessionId,
+ @NonNull String algorithm, @NonNull byte[] wrappedKey, @NonNull byte[] message);
/**
* Sign data using an RSA key
@@ -1108,8 +1286,10 @@
*
* @hide - not part of the public API at this time
*/
- public byte[] signRSA(byte[] sessionId, String algorithm,
- byte[] wrappedKey, byte[] message) {
+ @NonNull
+ public byte[] signRSA(
+ @NonNull byte[] sessionId, @NonNull String algorithm,
+ @NonNull byte[] wrappedKey, @NonNull byte[] message) {
return signRSANative(this, sessionId, algorithm, wrappedKey, message);
}
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index b4acbc0..0bf995f 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -16,6 +16,9 @@
package android.media;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
@@ -27,6 +30,8 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;
@@ -69,21 +74,26 @@
* Sets the data source (MediaDataSource) to use.
*
* @param dataSource the MediaDataSource for the media you want to extract from
+ *
+ * @throws IllegalArgumentException if dataSource is invalid.
*/
- public native final void setDataSource(MediaDataSource dataSource) throws IllegalArgumentException, IOException;
+ public native final void setDataSource(@NonNull MediaDataSource dataSource)
+ throws IOException;
/**
* Sets the data source as a content Uri.
*
* @param context the Context to use when resolving the Uri
* @param uri the Content URI of the data you want to extract from.
- * @param headers the headers to be sent together with the request for the data
+ * @param headers the headers to be sent together with the request for the data.
+ * This can be {@code null} if no specific headers are to be sent with the
+ * request.
*/
public final void setDataSource(
- Context context, Uri uri, Map<String, String> headers)
+ @NonNull Context context, @NonNull Uri uri, @Nullable Map<String, String> headers)
throws IOException {
String scheme = uri.getScheme();
- if(scheme == null || scheme.equals("file")) {
+ if (scheme == null || scheme.equals("file")) {
setDataSource(uri.getPath());
return;
}
@@ -122,9 +132,11 @@
* Sets the data source (file-path or http URL) to use.
*
* @param path the path of the file, or the http URL
- * @param headers the headers associated with the http request for the stream you want to play
+ * @param headers the headers associated with the http request for the stream you want to play.
+ * This can be {@code null} if no specific headers are to be sent with the
+ * request.
*/
- public final void setDataSource(String path, Map<String, String> headers)
+ public final void setDataSource(@NonNull String path, @Nullable Map<String, String> headers)
throws IOException {
String[] keys = null;
String[] values = null;
@@ -149,10 +161,10 @@
}
private native final void nativeSetDataSource(
- IBinder httpServiceBinder,
- String path,
- String[] keys,
- String[] values) throws IOException;
+ @NonNull IBinder httpServiceBinder,
+ @NonNull String path,
+ @Nullable String[] keys,
+ @Nullable String[] values) throws IOException;
/**
* Sets the data source (file-path or http URL) to use.
@@ -166,7 +178,7 @@
* As an alternative, the application could first open the file for reading,
* and then use the file descriptor form {@link #setDataSource(FileDescriptor)}.
*/
- public final void setDataSource(String path) throws IOException {
+ public final void setDataSource(@NonNull String path) throws IOException {
nativeSetDataSource(
MediaHTTPService.createHttpServiceBinderIfNecessary(path),
path,
@@ -180,7 +192,7 @@
*
* @param fd the FileDescriptor for the file you want to extract from.
*/
- public final void setDataSource(FileDescriptor fd) throws IOException {
+ public final void setDataSource(@NonNull FileDescriptor fd) throws IOException {
setDataSource(fd, 0, 0x7ffffffffffffffL);
}
@@ -194,7 +206,7 @@
* @param length the length in bytes of the data to be extracted
*/
public native final void setDataSource(
- FileDescriptor fd, long offset, long length) throws IOException;
+ @NonNull FileDescriptor fd, long offset, long length) throws IOException;
@Override
protected void finalize() {
@@ -217,7 +229,9 @@
* Get the PSSH info if present.
* @return a map of uuid-to-bytes, with the uuid specifying
* the crypto scheme, and the bytes being the data specific to that scheme.
+ * This can be {@code null} if the source does not contain PSSH info.
*/
+ @Nullable
public Map<UUID, byte[]> getPsshInfo() {
Map<UUID, byte[]> psshMap = null;
Map<String, Object> formatMap = getFileFormatNative();
@@ -243,16 +257,19 @@
return psshMap;
}
+ @NonNull
private native Map<String, Object> getFileFormatNative();
/**
* Get the track format at the specified index.
* More detail on the representation can be found at {@link android.media.MediaCodec}
*/
+ @NonNull
public MediaFormat getTrackFormat(int index) {
return new MediaFormat(getTrackFormatNative(index));
}
+ @NonNull
private native Map<String, Object> getTrackFormatNative(int index);
/**
@@ -284,11 +301,20 @@
*/
public static final int SEEK_TO_CLOSEST_SYNC = 2;
+ /** @hide */
+ @IntDef({
+ SEEK_TO_PREVIOUS_SYNC,
+ SEEK_TO_NEXT_SYNC,
+ SEEK_TO_CLOSEST_SYNC,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SeekMode {}
+
/**
* All selected tracks seek near the requested time according to the
* specified mode.
*/
- public native void seekTo(long timeUs, int mode);
+ public native void seekTo(long timeUs, @SeekMode int mode);
/**
* Advance to the next sample. Returns false if no more sample data
@@ -305,7 +331,7 @@
* @param byteBuf the destination byte buffer
* @return the sample size (or -1 if no more samples are available).
*/
- public native int readSampleData(ByteBuffer byteBuf, int offset);
+ public native int readSampleData(@NonNull ByteBuffer byteBuf, int offset);
/**
* Returns the track index the current sample originates from (or -1
@@ -334,9 +360,20 @@
*/
public static final int SAMPLE_FLAG_ENCRYPTED = 2;
+ /** @hide */
+ @IntDef(
+ flag = true,
+ value = {
+ SAMPLE_FLAG_SYNC,
+ SAMPLE_FLAG_ENCRYPTED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SampleFlag {}
+
/**
* Returns the current sample's flags.
*/
+ @SampleFlag
public native int getSampleFlags();
/**
@@ -347,7 +384,7 @@
* to be filled in.
* @return true iff the sample flags contain {@link #SAMPLE_FLAG_ENCRYPTED}
*/
- public native boolean getSampleCryptoInfo(MediaCodec.CryptoInfo info);
+ public native boolean getSampleCryptoInfo(@NonNull MediaCodec.CryptoInfo info);
/**
* Returns an estimate of how much data is presently cached in memory
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index f518ab2..4b6b4fa 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -16,12 +16,18 @@
package android.media;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
import dalvik.system.CloseGuard;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
import java.util.Map;
@@ -80,19 +86,27 @@
public static final int MUXER_OUTPUT_WEBM = 1;
};
+ /** @hide */
+ @IntDef({
+ OutputFormat.MUXER_OUTPUT_MPEG_4,
+ OutputFormat.MUXER_OUTPUT_WEBM,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Format {}
+
// All the native functions are listed here.
- private static native long nativeSetup(FileDescriptor fd, int format);
+ private static native long nativeSetup(@NonNull FileDescriptor fd, int format);
private static native void nativeRelease(long nativeObject);
private static native void nativeStart(long nativeObject);
private static native void nativeStop(long nativeObject);
- private static native int nativeAddTrack(long nativeObject, String[] keys,
- Object[] values);
- private static native void nativeSetOrientationHint(long nativeObject,
- int degrees);
+ private static native int nativeAddTrack(
+ long nativeObject, @NonNull String[] keys, @NonNull Object[] values);
+ private static native void nativeSetOrientationHint(
+ long nativeObject, int degrees);
private static native void nativeSetLocation(long nativeObject, int latitude, int longitude);
- private static native void nativeWriteSampleData(long nativeObject,
- int trackIndex, ByteBuffer byteBuf,
- int offset, int size, long presentationTimeUs, int flags);
+ private static native void nativeWriteSampleData(
+ long nativeObject, int trackIndex, @NonNull ByteBuffer byteBuf,
+ int offset, int size, long presentationTimeUs, @MediaCodec.BufferFlag int flags);
// Muxer internal states.
private static final int MUXER_STATE_UNINITIALIZED = -1;
@@ -115,7 +129,7 @@
* @see android.media.MediaMuxer.OutputFormat
* @throws IOException if failed to open the file for write
*/
- public MediaMuxer(String path, int format) throws IOException {
+ public MediaMuxer(@NonNull String path, @Format int format) throws IOException {
if (path == null) {
throw new IllegalArgumentException("path must not be null");
}
@@ -246,11 +260,12 @@
/**
* Adds a track with the specified format.
- * @param format The media format for the track.
+ * @param format The media format for the track. This must not be an empty
+ * MediaFormat.
* @return The track index for this newly added track, and it should be used
* in the {@link #writeSampleData}.
*/
- public int addTrack(MediaFormat format) {
+ public int addTrack(@NonNull MediaFormat format) {
if (format == null) {
throw new IllegalArgumentException("format must not be null.");
}
@@ -302,8 +317,8 @@
* MediaMuxer uses the flags provided in {@link MediaCodec.BufferInfo},
* to signal sync frames.
*/
- public void writeSampleData(int trackIndex, ByteBuffer byteBuf,
- BufferInfo bufferInfo) {
+ public void writeSampleData(int trackIndex, @NonNull ByteBuffer byteBuf,
+ @NonNull BufferInfo bufferInfo) {
if (trackIndex < 0 || trackIndex > mLastTrackIndex) {
throw new IllegalArgumentException("trackIndex is invalid");
}
diff --git a/media/java/android/media/MediaSync.java b/media/java/android/media/MediaSync.java
index e6bc10d..74a2fb2 100644
--- a/media/java/android/media/MediaSync.java
+++ b/media/java/android/media/MediaSync.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.media.AudioTrack;
import android.os.Handler;
import android.os.Looper;
@@ -120,9 +121,10 @@
*
* @param sync The MediaSync object.
* @param audioBuffer The returned audio buffer.
+ * @param bufferIndex The index associated with the audio buffer
*/
public abstract void onReturnAudioBuffer(
- MediaSync sync, ByteBuffer audioBuffer, int bufferIndex);
+ @NonNull MediaSync sync, @NonNull ByteBuffer audioBuffer, int bufferIndex);
}
private static final String TAG = "MediaSync";
@@ -138,7 +140,7 @@
public int mSizeInBytes;
long mPresentationTimeUs;
- public AudioBuffer(ByteBuffer byteBuffer, int bufferIndex,
+ public AudioBuffer(@NonNull ByteBuffer byteBuffer, int bufferIndex,
int sizeInBytes, long presentationTimeUs) {
mByteBuffer = byteBuffer;
mBufferIndex = bufferIndex;
@@ -200,13 +202,17 @@
/**
* Sets an asynchronous callback for actionable MediaSync events.
- * It shouldn't be called inside callback.
+ * <p>
+ * This method can be called multiple times to update a previously set callback. If the
+ * handler is changed, undelivered notifications scheduled for the old handler may be dropped.
+ * <p>
+ * <b>Do not call this inside callback.</b>
*
- * @param cb The callback that will run.
- * @param handler The Handler that will run the callback. Using null means to use MediaSync's
+ * @param cb The callback that will run. Use {@code null} to stop receiving callbacks.
+ * @param handler The Handler that will run the callback. Use {@code null} to use MediaSync's
* internal handler if it exists.
*/
- public void setCallback(/* MediaSync. */ Callback cb, Handler handler) {
+ public void setCallback(@Nullable /* MediaSync. */ Callback cb, @Nullable Handler handler) {
synchronized(mCallbackLock) {
if (handler != null) {
mCallbackHandler = handler;
@@ -235,11 +241,11 @@
* @throws IllegalStateException if not in the Initialized state, or another surface
* has already been configured.
*/
- public void configureSurface(Surface surface) {
+ public void configureSurface(@Nullable Surface surface) {
native_configureSurface(surface);
}
- private native final void native_configureSurface(Surface surface);
+ private native final void native_configureSurface(@Nullable Surface surface);
/**
* Configures the audio track for MediaSync.
@@ -249,7 +255,7 @@
* @throws IllegalStateException if not in the Initialized state, or another audio track
* has already been configured.
*/
- public void configureAudioTrack(AudioTrack audioTrack) {
+ public void configureAudioTrack(@Nullable AudioTrack audioTrack) {
// AudioTrack has sanity check for configured sample rate.
int nativeSampleRateInHz = (audioTrack == null ? 0 : audioTrack.getSampleRate());
@@ -261,7 +267,7 @@
}
private native final void native_configureAudioTrack(
- AudioTrack audioTrack, int nativeSampleRateInHz);
+ @Nullable AudioTrack audioTrack, int nativeSampleRateInHz);
/**
* Requests a Surface to use as the input. This may only be called after
@@ -272,6 +278,7 @@
* @throws IllegalStateException if not configured, or another input surface has
* already been created.
*/
+ @NonNull
public native final Surface createInputSurface();
/**
@@ -412,7 +419,7 @@
return native_getTimestamp(timestamp);
}
- private native final boolean native_getTimestamp(MediaTimestamp timestamp);
+ private native final boolean native_getTimestamp(@NonNull MediaTimestamp timestamp);
/**
* Queues the audio data asynchronously for playback (AudioTrack must be in streaming mode).
@@ -427,7 +434,8 @@
* has not been done correctly.
*/
public void queueAudio(
- ByteBuffer audioData, int bufferIndex, int sizeInBytes, long presentationTimeUs) {
+ @NonNull ByteBuffer audioData, int bufferIndex, int sizeInBytes,
+ long presentationTimeUs) {
if (mAudioTrack == null || mAudioThread == null) {
throw new IllegalStateException(
"AudioTrack is NOT configured or audio thread is not created");
@@ -489,7 +497,7 @@
private native final void native_updateQueuedAudioData(
int sizeInBytes, long presentationTimeUs);
- private final void postReturnByteBuffer(final AudioBuffer audioBuffer) {
+ private final void postReturnByteBuffer(@NonNull final AudioBuffer audioBuffer) {
synchronized(mCallbackLock) {
if (mCallbackHandler != null) {
final MediaSync sync = this;
diff --git a/media/java/android/media/PlaybackSettings.java b/media/java/android/media/PlaybackSettings.java
new file mode 100644
index 0000000..ceb6bb1
--- /dev/null
+++ b/media/java/android/media/PlaybackSettings.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2015 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 android.media;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import android.annotation.IntDef;
+
+/**
+ * Structure for common playback settings.
+ *
+ * Used by {@link AudioTrack} {@link AudioTrack#getPlaybackSettings()} and
+ * {@link AudioTrack#setPlaybackSettings(PlaybackSettings)}
+ * to control playback behavior.
+ * <p> <strong>audio fallback mode:</strong>
+ * select out-of-range parameter handling.
+ * <ul>
+ * <li> {@link PlaybackSettings#AUDIO_FALLBACK_MODE_DEFAULT}:
+ * System will determine best handling. </li>
+ * <li> {@link PlaybackSettings#AUDIO_FALLBACK_MODE_MUTE}:
+ * Play silence for settings normally out of range.</li>
+ * <li> {@link PlaybackSettings#AUDIO_FALLBACK_MODE_FAIL}:
+ * Return {@link java.lang.IllegalArgumentException} from
+ * <code>AudioTrack.setPlaybackSettings(PlaybackSettings)</code>.</li>
+ * </ul>
+ * <p> <strong>audio stretch mode:</strong> select
+ * timestretch handling.
+ * <ul>
+ * <li> {@link PlaybackSettings#AUDIO_STRETCH_MODE_DEFAULT}:
+ * System will determine best selection. </li>
+ * <li> {@link PlaybackSettings#AUDIO_STRETCH_MODE_VOICE}:
+ * Content is primarily voice.</li>
+ * </ul>
+ * <p> <strong>pitch:</strong> increases or decreases the tonal frequency of the audio content.
+ * It is expressed as a multiplicative factor, where normal pitch is 1.0f.
+ * <p> <strong>speed:</strong> increases or decreases the time to
+ * play back a set of audio or video frames.
+ * It is expressed as a multiplicative factor, where normal speed is 1.0f.
+ * <p> Different combinations of speed and pitch may be used for audio playback;
+ * some common ones:
+ * <ul>
+ * <li> <em>Pitch equals 1.0f.</em> Speed change will be done with pitch preserved,
+ * often called <em>timestretching</em>.</li>
+ * <li> <em>Pitch equals speed.</em> Speed change will be done by <em>resampling</em>,
+ * similar to {@link AudioTrack#setPlaybackRate(int)}.</li>
+ * </ul>
+ */
+public final class PlaybackSettings {
+ /** @hide */
+ @IntDef(
+ value = {
+ AUDIO_FALLBACK_MODE_DEFAULT,
+ AUDIO_FALLBACK_MODE_MUTE,
+ AUDIO_FALLBACK_MODE_FAIL,
+ }
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AudioFallbackMode {}
+ public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0;
+ public static final int AUDIO_FALLBACK_MODE_MUTE = 1;
+ public static final int AUDIO_FALLBACK_MODE_FAIL = 2;
+
+ /** @hide */
+ @IntDef(
+ value = {
+ AUDIO_STRETCH_MODE_DEFAULT,
+ AUDIO_STRETCH_MODE_VOICE,
+ }
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AudioStretchMode {}
+ public static final int AUDIO_STRETCH_MODE_DEFAULT = 0;
+ public static final int AUDIO_STRETCH_MODE_VOICE = 1;
+
+ // flags to indicate which settings are actually set
+ private static final int SET_SPEED = 1 << 0;
+ private static final int SET_PITCH = 1 << 1;
+ private static final int SET_AUDIO_FALLBACK_MODE = 1 << 2;
+ private static final int SET_AUDIO_STRETCH_MODE = 1 << 3;
+ private int mSet = 0;
+
+ // settings
+ private int mAudioFallbackMode = AUDIO_FALLBACK_MODE_DEFAULT;
+ private int mAudioStretchMode = AUDIO_STRETCH_MODE_DEFAULT;
+ private float mPitch = 1.0f;
+ private float mSpeed = 1.0f;
+
+ /**
+ * Allows defaults to be returned for properties not set.
+ * Otherwise a {@link java.lang.IllegalArgumentException} exception
+ * is raised when getting those properties
+ * which have defaults but have never been set.
+ * @return this <code>PlaybackSettings</code> instance.
+ */
+ public PlaybackSettings allowDefaults() {
+ mSet |= SET_AUDIO_FALLBACK_MODE | SET_AUDIO_STRETCH_MODE | SET_PITCH | SET_SPEED;
+ return this;
+ }
+
+ /**
+ * Sets the audio fallback mode.
+ * @param audioFallbackMode
+ * @return this <code>PlaybackSettings</code> instance.
+ */
+ public PlaybackSettings setAudioFallbackMode(@AudioFallbackMode int audioFallbackMode) {
+ mAudioFallbackMode = audioFallbackMode;
+ mSet |= SET_AUDIO_FALLBACK_MODE;
+ return this;
+ }
+
+ /**
+ * Retrieves the audio fallback mode.
+ * @return audio fallback mode
+ * @throws IllegalStateException if the audio fallback mode is not set.
+ */
+ public @AudioFallbackMode int getAudioFallbackMode() {
+ if ((mSet & SET_AUDIO_FALLBACK_MODE) == 0) {
+ throw new IllegalStateException("audio fallback mode not set");
+ }
+ return mAudioFallbackMode;
+ }
+
+ /**
+ * Sets the audio stretch mode.
+ * @param audioStretchMode
+ * @return this <code>PlaybackSettings</code> instance.
+ */
+ public PlaybackSettings setAudioStretchMode(@AudioStretchMode int audioStretchMode) {
+ mAudioStretchMode = audioStretchMode;
+ mSet |= SET_AUDIO_STRETCH_MODE;
+ return this;
+ }
+
+ /**
+ * Retrieves the audio stretch mode.
+ * @return audio stretch mode
+ * @throws IllegalStateException if the audio stretch mode is not set.
+ */
+ public @AudioStretchMode int getAudioStretchMode() {
+ if ((mSet & SET_AUDIO_STRETCH_MODE) == 0) {
+ throw new IllegalStateException("audio stretch mode not set");
+ }
+ return mAudioStretchMode;
+ }
+
+ /**
+ * Sets the pitch factor.
+ * @param pitch
+ * @return this <code>PlaybackSettings</code> instance.
+ */
+ public PlaybackSettings setPitch(float pitch) {
+ mPitch = pitch;
+ mSet |= SET_PITCH;
+ return this;
+ }
+
+ /**
+ * Retrieves the pitch factor.
+ * @return pitch
+ * @throws IllegalStateException if pitch is not set.
+ */
+ public float getPitch() {
+ if ((mSet & SET_PITCH) == 0) {
+ throw new IllegalStateException("pitch not set");
+ }
+ return mPitch;
+ }
+
+ /**
+ * Sets the speed factor.
+ * @param speed
+ * @return this <code>PlaybackSettings</code> instance.
+ */
+ public PlaybackSettings setSpeed(float speed) {
+ mSpeed = speed;
+ mSet |= SET_SPEED;
+ return this;
+ }
+
+ /**
+ * Retrieves the speed factor.
+ * @return speed
+ * @throws IllegalStateException if speed is not set.
+ */
+ public float getSpeed() {
+ if ((mSet & SET_SPEED) == 0) {
+ throw new IllegalStateException("speed not set");
+ }
+ return mSpeed;
+ }
+}
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 8cf9874..c8464c7 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -43,7 +43,7 @@
libusbhost \
libjhead \
libexif \
- libstagefright_amrnb_common \
+ libstagefright_amrnb_common
LOCAL_REQUIRED_MODULES := \
libjhead_jni
@@ -55,6 +55,7 @@
external/libexif/ \
external/tremor/Tremor \
frameworks/base/core/jni \
+ frameworks/base/libs/hwui \
frameworks/av/media/libmedia \
frameworks/av/media/libstagefright \
frameworks/av/media/libstagefright/codecs/amrnb/enc/src \
diff --git a/native/graphics/jni/Android.mk b/native/graphics/jni/Android.mk
index 91c9ac6..f89a5af 100644
--- a/native/graphics/jni/Android.mk
+++ b/native/graphics/jni/Android.mk
@@ -24,7 +24,8 @@
LOCAL_C_INCLUDES += \
frameworks/base/native/include \
- frameworks/base/core/jni/android/graphics
+ frameworks/base/core/jni/android/graphics \
+ frameworks/base/libs/hwui
LOCAL_MODULE:= libjnigraphics
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 8039b71..efe71d4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -16,17 +16,47 @@
package com.android.documentsui;
+import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
+import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
+import static com.android.documentsui.DirectoryFragment.ANIM_UP;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.Executor;
+import libcore.io.IoUtils;
import android.app.Activity;
import android.app.Fragment;
-import android.content.pm.ResolveInfo;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Root;
+import android.util.Log;
import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MenuItem.OnActionExpandListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.SearchView;
+import android.widget.SearchView.OnQueryTextListener;
+import android.widget.TextView;
+import com.android.documentsui.RecentsProvider.ResumeColumns;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.DurableUtils;
@@ -34,20 +64,125 @@
import com.google.common.collect.Maps;
abstract class BaseActivity extends Activity {
+
+ static final String EXTRA_STATE = "state";
+
+ private final String mTag;
+ RootsCache mRoots;
+
public abstract State getDisplayState();
- public abstract RootInfo getCurrentRoot();
- public abstract void onStateChanged();
- public abstract void setRootsDrawerOpen(boolean open);
public abstract void onDocumentPicked(DocumentInfo doc);
public abstract void onDocumentsPicked(List<DocumentInfo> docs);
- public abstract DocumentInfo getCurrentDirectory();
- public abstract void setPending(boolean pending);
- public abstract void onStackPicked(DocumentStack stack);
- public abstract void onPickRequested(DocumentInfo pickTarget);
- public abstract void onAppPicked(ResolveInfo info);
- public abstract void onRootPicked(RootInfo root, boolean closeDrawer);
- public abstract void onSaveRequested(DocumentInfo replaceTarget);
- public abstract void onSaveRequested(String mimeType, String displayName);
+ abstract void onTaskFinished(Uri... uris);
+ abstract void onDirectoryChanged(int anim);
+ abstract void updateActionBar();
+ abstract void saveStackBlocking();
+
+ public BaseActivity(String tag) {
+ mTag = tag;
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ mRoots = DocumentsApplication.getRootsCache(this);
+ }
+
+ void onStackRestored(boolean restored, boolean external) {}
+
+ void onRootPicked(RootInfo root) {
+ State state = getDisplayState();
+
+ // Clear entire backstack and start in new root
+ state.stack.root = root;
+ state.stack.clear();
+ state.stackTouched = true;
+
+ if (!mRoots.isRecentsRoot(root)) {
+ new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
+ } else {
+ onCurrentDirectoryChanged(ANIM_SIDE);
+ }
+ }
+
+ void expandMenus(Menu menu) {
+ for (int i = 0; i < menu.size(); i++) {
+ final MenuItem item = menu.getItem(i);
+ switch (item.getItemId()) {
+ case R.id.menu_advanced:
+ case R.id.menu_file_size:
+ break;
+ default:
+ item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ }
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final int id = item.getItemId();
+ if (id == android.R.id.home) {
+ onBackPressed();
+ return true;
+ } else if (id == R.id.menu_create_dir) {
+ CreateDirectoryFragment.show(getFragmentManager());
+ return true;
+ } else if (id == R.id.menu_search) {
+ return false;
+ } else if (id == R.id.menu_sort_name) {
+ setUserSortOrder(State.SORT_ORDER_DISPLAY_NAME);
+ return true;
+ } else if (id == R.id.menu_sort_date) {
+ setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
+ return true;
+ } else if (id == R.id.menu_sort_size) {
+ setUserSortOrder(State.SORT_ORDER_SIZE);
+ return true;
+ } else if (id == R.id.menu_grid) {
+ setUserMode(State.MODE_GRID);
+ return true;
+ } else if (id == R.id.menu_list) {
+ setUserMode(State.MODE_LIST);
+ return true;
+ } else if (id == R.id.menu_advanced) {
+ setDisplayAdvancedDevices(!LocalPreferences.getDisplayAdvancedDevices(this));
+ return true;
+ } else if (id == R.id.menu_file_size) {
+ setDisplayFileSize(!LocalPreferences.getDisplayFileSize(this));
+ return true;
+ } else if (id == R.id.menu_settings) {
+ final RootInfo root = getCurrentRoot();
+ final Intent intent = new Intent(DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS);
+ intent.setDataAndType(DocumentsContract.buildRootUri(root.authority, root.rootId),
+ DocumentsContract.Root.MIME_TYPE_ITEM);
+ startActivity(intent);
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * Call this when directory changes. Prior to root fragment update
+ * the (abstract) directoryChanged method will be called.
+ * @param anim
+ */
+ final void onCurrentDirectoryChanged(int anim) {
+ onDirectoryChanged(anim);
+
+ final RootsFragment roots = RootsFragment.get(getFragmentManager());
+ if (roots != null) {
+ roots.onCurrentRootChanged();
+ }
+
+ updateActionBar();
+ invalidateOptionsMenu();
+ }
+
+ final String getCallingPackageMaybeExtra() {
+ final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
+ return (extra != null) ? extra : getCallingPackage();
+ }
public static BaseActivity get(Fragment fragment) {
return (BaseActivity) fragment.getActivity();
@@ -169,4 +304,382 @@
}
};
}
+
+ void setDisplayAdvancedDevices(boolean display) {
+ State state = getDisplayState();
+ LocalPreferences.setDisplayAdvancedDevices(this, display);
+ state.showAdvanced = state.forceAdvanced | display;
+ RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
+ invalidateOptionsMenu();
+ }
+
+ void setDisplayFileSize(boolean display) {
+ LocalPreferences.setDisplayFileSize(this, display);
+ getDisplayState().showSize = display;
+ DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
+ invalidateOptionsMenu();
+ }
+
+ void onStateChanged() {
+ invalidateOptionsMenu();
+ }
+
+ /**
+ * Set state sort order based on explicit user action.
+ */
+ void setUserSortOrder(int sortOrder) {
+ getDisplayState().userSortOrder = sortOrder;
+ DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
+ }
+
+ /**
+ * Set state mode based on explicit user action.
+ */
+ void setUserMode(int mode) {
+ getDisplayState().userMode = mode;
+ DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
+ }
+
+ void setPending(boolean pending) {
+ final SaveFragment save = SaveFragment.get(getFragmentManager());
+ if (save != null) {
+ save.setPending(pending);
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle state) {
+ super.onSaveInstanceState(state);
+ state.putParcelable(EXTRA_STATE, getDisplayState());
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle state) {
+ super.onRestoreInstanceState(state);
+ }
+
+ RootInfo getCurrentRoot() {
+ State state = getDisplayState();
+ if (state.stack.root != null) {
+ return state.stack.root;
+ } else {
+ return mRoots.getRecentsRoot();
+ }
+ }
+
+ public DocumentInfo getCurrentDirectory() {
+ return getDisplayState().stack.peek();
+ }
+
+ public Executor getCurrentExecutor() {
+ final DocumentInfo cwd = getCurrentDirectory();
+ if (cwd != null && cwd.authority != null) {
+ return ProviderExecutor.forAuthority(cwd.authority);
+ } else {
+ return AsyncTask.THREAD_POOL_EXECUTOR;
+ }
+ }
+
+ public void onStackPicked(DocumentStack stack) {
+ try {
+ // Update the restored stack to ensure we have freshest data
+ stack.updateDocuments(getContentResolver());
+
+ State state = getDisplayState();
+ state.stack = stack;
+ state.stackTouched = true;
+ onCurrentDirectoryChanged(ANIM_SIDE);
+
+ } catch (FileNotFoundException e) {
+ Log.w(mTag, "Failed to restore stack: " + e);
+ }
+ }
+
+ final class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
+ private RootInfo mRoot;
+
+ public PickRootTask(RootInfo root) {
+ mRoot = root;
+ }
+
+ @Override
+ protected DocumentInfo doInBackground(Void... params) {
+ try {
+ final Uri uri = DocumentsContract.buildDocumentUri(
+ mRoot.authority, mRoot.documentId);
+ return DocumentInfo.fromUri(getContentResolver(), uri);
+ } catch (FileNotFoundException e) {
+ Log.w(mTag, "Failed to find root", e);
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(DocumentInfo result) {
+ if (result != null) {
+ State state = getDisplayState();
+ state.stack.push(result);
+ state.stackTouched = true;
+ onCurrentDirectoryChanged(ANIM_SIDE);
+ }
+ }
+ }
+
+ final class RestoreStackTask extends AsyncTask<Void, Void, Void> {
+ private volatile boolean mRestoredStack;
+ private volatile boolean mExternal;
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ State state = getDisplayState();
+ RootsCache roots = DocumentsApplication.getRootsCache(BaseActivity.this);
+
+ // Restore last stack for calling package
+ final String packageName = getCallingPackageMaybeExtra();
+ final Cursor cursor = getContentResolver()
+ .query(RecentsProvider.buildResume(packageName), null, null, null, null);
+ try {
+ if (cursor.moveToFirst()) {
+ mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
+ final byte[] rawStack = cursor.getBlob(
+ cursor.getColumnIndex(ResumeColumns.STACK));
+ DurableUtils.readFromArray(rawStack, state.stack);
+ mRestoredStack = true;
+ }
+ } catch (IOException e) {
+ Log.w(mTag, "Failed to resume: " + e);
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+
+ if (mRestoredStack) {
+ // Update the restored stack to ensure we have freshest data
+ final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(state);
+ try {
+ state.stack.updateRoot(matchingRoots);
+ state.stack.updateDocuments(getContentResolver());
+ } catch (FileNotFoundException e) {
+ Log.w(mTag, "Failed to restore stack: " + e);
+ state.stack.reset();
+ mRestoredStack = false;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ if (isDestroyed()) return;
+ getDisplayState().restored = true;
+ onCurrentDirectoryChanged(ANIM_NONE);
+
+ onStackRestored(mRestoredStack, mExternal);
+
+ getDisplayState().restored = true;
+ onCurrentDirectoryChanged(ANIM_NONE);
+ }
+ }
+
+ final class ItemSelectedListener implements OnItemSelectedListener {
+
+ boolean mIgnoreNextNavigation;
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ if (mIgnoreNextNavigation) {
+ mIgnoreNextNavigation = false;
+ return;
+ }
+
+ State state = getDisplayState();
+ while (state.stack.size() > position + 1) {
+ state.stackTouched = true;
+ state.stack.pop();
+ }
+ onCurrentDirectoryChanged(ANIM_UP);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ // Ignored
+ }
+ }
+
+ /**
+ * Class providing toolbar with runtime access to useful activity data.
+ */
+ final class StackAdapter extends BaseAdapter {
+ @Override
+ public int getCount() {
+ return getDisplayState().stack.size();
+ }
+
+ @Override
+ public DocumentInfo getItem(int position) {
+ State state = getDisplayState();
+ return state.stack.get(state.stack.size() - position - 1);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_subdir_title, parent, false);
+ }
+
+ final TextView title = (TextView) convertView.findViewById(android.R.id.title);
+ final DocumentInfo doc = getItem(position);
+
+ if (position == 0) {
+ final RootInfo root = getCurrentRoot();
+ title.setText(root.title);
+ } else {
+ title.setText(doc.displayName);
+ }
+
+ return convertView;
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_subdir, parent, false);
+ }
+
+ final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
+ final TextView title = (TextView) convertView.findViewById(android.R.id.title);
+ final DocumentInfo doc = getItem(position);
+
+ if (position == 0) {
+ final RootInfo root = getCurrentRoot();
+ title.setText(root.title);
+ subdir.setVisibility(View.GONE);
+ } else {
+ title.setText(doc.displayName);
+ subdir.setVisibility(View.VISIBLE);
+ }
+
+ return convertView;
+ }
+ }
+
+ /**
+ * Facade over the various search parts in the menu.
+ */
+ final class SearchManager implements
+ SearchView.OnCloseListener, OnActionExpandListener, OnQueryTextListener {
+
+ protected boolean mSearchExpanded;
+ protected boolean mIgnoreNextClose;
+ protected boolean mIgnoreNextCollapse;
+
+ private MenuItem mMenu;
+ private SearchView mView;
+
+ public void install(MenuItem menu) {
+ assert(mMenu == null);
+ mMenu = menu;
+ mView = (SearchView) menu.getActionView();
+
+ mMenu.setOnActionExpandListener(this);
+ mView.setOnQueryTextListener(this);
+ mView.setOnCloseListener(this);
+ }
+
+ /**
+ * @param root Info about the current directory.
+ */
+ void update(RootInfo root) {
+ if (mMenu == null) {
+ Log.d(mTag, "showMenu called before Search MenuItem installed.");
+ return;
+ }
+ State state = getDisplayState();
+ if (state.currentSearch != null) {
+ mMenu.expandActionView();
+
+ mView.setIconified(false);
+ mView.clearFocus();
+ mView.setQuery(state.currentSearch, false);
+ } else {
+ mIgnoreNextClose = true;
+ mView.setIconified(true);
+ mView.clearFocus();
+
+ mIgnoreNextCollapse = true;
+ mMenu.collapseActionView();
+ }
+
+ showMenu(root != null
+ && ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0));
+ }
+
+ void showMenu(boolean visible) {
+ if (mMenu == null) {
+ Log.d(mTag, "showMenu called before Search MenuItem installed.");
+ return;
+ }
+ mMenu.setVisible(visible);
+ }
+
+ boolean isSearching() {
+ return getDisplayState().currentSearch != null;
+ }
+
+ boolean isExpanded() {
+ return mSearchExpanded;
+ }
+
+ @Override
+ public boolean onClose() {
+ mSearchExpanded = false;
+ if (mIgnoreNextClose) {
+ mIgnoreNextClose = false;
+ return false;
+ }
+
+ getDisplayState().currentSearch = null;
+ onCurrentDirectoryChanged(ANIM_NONE);
+ return false;
+ }
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ mSearchExpanded = true;
+ updateActionBar();
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem item) {
+ mSearchExpanded = false;
+ if (mIgnoreNextCollapse) {
+ mIgnoreNextCollapse = false;
+ return true;
+ }
+
+ getDisplayState().currentSearch = null;
+ onCurrentDirectoryChanged(ANIM_NONE);
+ return true;
+ }
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ mSearchExpanded = true;
+ getDisplayState().currentSearch = query;
+ mView.clearFocus();
+ onCurrentDirectoryChanged(ANIM_NONE);
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ return false;
+ }
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 37a14c6..7cf58cc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -28,7 +28,6 @@
import static com.android.documentsui.model.DocumentInfo.getCursorInt;
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
-
import android.app.Activity;
import android.app.ActivityManager;
import android.app.Fragment;
@@ -311,8 +310,9 @@
updateDisplayState();
// When launched into empty recents, show drawer
- if (mType == TYPE_RECENT_OPEN && mAdapter.isEmpty() && !state.stackTouched) {
- ((BaseActivity) context).setRootsDrawerOpen(true);
+ if (mType == TYPE_RECENT_OPEN && mAdapter.isEmpty() && !state.stackTouched &&
+ context instanceof DocumentsActivity) {
+ ((DocumentsActivity) context).setRootsDrawerOpen(true);
}
// Restore any previous instance state
@@ -503,7 +503,8 @@
open.setVisible(!manageOrBrowse);
share.setVisible(manageOrBrowse);
delete.setVisible(manageOrBrowse);
- copy.setVisible(manageOrBrowse);
+ // Disable copying from the Recents view.
+ copy.setVisible(manageOrBrowse && mType != TYPE_RECENT_OPEN);
return true;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index a2a789f..9d828de 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -21,16 +21,17 @@
import static com.android.documentsui.BaseActivity.State.ACTION_GET_CONTENT;
import static com.android.documentsui.BaseActivity.State.ACTION_MANAGE;
import static com.android.documentsui.BaseActivity.State.ACTION_OPEN;
-import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE;
import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_COPY_DESTINATION;
-import static com.android.documentsui.BaseActivity.State.MODE_GRID;
-import static com.android.documentsui.BaseActivity.State.MODE_LIST;
+import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE;
import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
-import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
import static com.android.documentsui.DirectoryFragment.ANIM_UP;
+import java.util.Arrays;
+import java.util.List;
+
import android.app.Activity;
+import android.app.Fragment;
import android.app.FragmentManager;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
@@ -42,7 +43,6 @@
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
-import android.database.Cursor;
import android.graphics.Point;
import android.net.Uri;
import android.os.AsyncTask;
@@ -54,51 +54,27 @@
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.DrawerLayout.DrawerListener;
import android.util.Log;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.MenuItem.OnActionExpandListener;
import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowManager;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.SearchView;
-import android.widget.SearchView.OnQueryTextListener;
import android.widget.Spinner;
-import android.widget.TextView;
import android.widget.Toast;
import android.widget.Toolbar;
-import libcore.io.IoUtils;
-
import com.android.documentsui.RecentsProvider.RecentColumns;
import com.android.documentsui.RecentsProvider.ResumeColumns;
import com.android.documentsui.model.DocumentInfo;
-import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.DurableUtils;
import com.android.documentsui.model.RootInfo;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.Executor;
-
public class DocumentsActivity extends BaseActivity {
+ private static final int CODE_FORWARD = 42;
public static final String TAG = "Documents";
- private static final String EXTRA_STATE = "state";
-
- private static final int CODE_FORWARD = 42;
-
private boolean mShowAsDialog;
- private SearchView mSearchView;
-
private Toolbar mToolbar;
private Spinner mToolbarStack;
@@ -110,21 +86,20 @@
private DirectoryContainerView mDirectoryContainer;
- private boolean mIgnoreNextNavigation;
- private boolean mIgnoreNextClose;
- private boolean mIgnoreNextCollapse;
-
- private boolean mSearchExpanded;
-
- private RootsCache mRoots;
private State mState;
+ private SearchManager mSearchManager;
+ private ItemSelectedListener mStackListener;
+ private BaseAdapter mStackAdapter;
+
+ public DocumentsActivity() {
+ super(TAG);
+ }
+
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- mRoots = DocumentsApplication.getRootsCache(this);
-
setResult(Activity.RESULT_CANCELED);
setContentView(R.layout.activity);
@@ -157,16 +132,16 @@
mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
- if (icicle != null) {
- mState = icicle.getParcelable(EXTRA_STATE);
- } else {
- buildDefaultState();
- }
+ mState = (icicle != null)
+ ? icicle.<State>getParcelable(EXTRA_STATE)
+ : buildDefaultState();
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mToolbar.setTitleTextAppearance(context,
android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
+ mStackAdapter = new StackAdapter();
+ mStackListener = new ItemSelectedListener();
mToolbarStack = (Spinner) findViewById(R.id.stack);
mToolbarStack.setOnItemSelectedListener(mStackListener);
@@ -176,6 +151,7 @@
android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
}
+ mSearchManager = new SearchManager();
setActionBar(mToolbar);
// Hide roots when we're managing a specific root
@@ -220,55 +196,57 @@
}
}
- private void buildDefaultState() {
- mState = new State();
+ private State buildDefaultState() {
+ State state = new State();
final Intent intent = getIntent();
final String action = intent.getAction();
if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
- mState.action = ACTION_OPEN;
+ state.action = ACTION_OPEN;
} else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
- mState.action = ACTION_CREATE;
+ state.action = ACTION_CREATE;
} else if (Intent.ACTION_GET_CONTENT.equals(action)) {
- mState.action = ACTION_GET_CONTENT;
+ state.action = ACTION_GET_CONTENT;
} else if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) {
- mState.action = ACTION_OPEN_TREE;
+ state.action = ACTION_OPEN_TREE;
} else if (DocumentsContract.ACTION_MANAGE_ROOT.equals(action)) {
- mState.action = ACTION_MANAGE;
+ state.action = ACTION_MANAGE;
} else if (DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT.equals(action)) {
- mState.action = ACTION_BROWSE;
+ state.action = ACTION_BROWSE;
} else if (DocumentsIntent.ACTION_OPEN_COPY_DESTINATION.equals(action)) {
- mState.action = ACTION_OPEN_COPY_DESTINATION;
+ state.action = ACTION_OPEN_COPY_DESTINATION;
}
- if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
- mState.allowMultiple = intent.getBooleanExtra(
+ if (state.action == ACTION_OPEN || state.action == ACTION_GET_CONTENT) {
+ state.allowMultiple = intent.getBooleanExtra(
Intent.EXTRA_ALLOW_MULTIPLE, false);
}
- if (mState.action == ACTION_MANAGE || mState.action == ACTION_BROWSE) {
- mState.acceptMimes = new String[] { "*/*" };
- mState.allowMultiple = true;
+ if (state.action == ACTION_MANAGE || state.action == ACTION_BROWSE) {
+ state.acceptMimes = new String[] { "*/*" };
+ state.allowMultiple = true;
} else if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
- mState.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
+ state.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
} else {
- mState.acceptMimes = new String[] { intent.getType() };
+ state.acceptMimes = new String[] { intent.getType() };
}
- mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
- mState.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
- mState.showAdvanced = mState.forceAdvanced
+ state.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
+ state.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+ state.showAdvanced = state.forceAdvanced
| LocalPreferences.getDisplayAdvancedDevices(this);
- if (mState.action == ACTION_MANAGE || mState.action == ACTION_BROWSE) {
- mState.showSize = true;
+ if (state.action == ACTION_MANAGE || state.action == ACTION_BROWSE) {
+ state.showSize = true;
} else {
- mState.showSize = LocalPreferences.getDisplayFileSize(this);
+ state.showSize = LocalPreferences.getDisplayFileSize(this);
}
- if (mState.action == ACTION_OPEN_COPY_DESTINATION) {
- mState.directoryCopy = intent.getBooleanExtra(
+ if (state.action == ACTION_OPEN_COPY_DESTINATION) {
+ state.directoryCopy = intent.getBooleanExtra(
BaseActivity.DocumentsIntent.EXTRA_DIRECTORY_COPY, false);
}
+
+ return state;
}
private class RestoreRootTask extends AsyncTask<Void, Void, RootInfo> {
@@ -290,7 +268,7 @@
mState.restored = true;
if (root != null) {
- onRootPicked(root, true);
+ onRootPicked(root);
} else {
Log.w(TAG, "Failed to find root: " + mRootUri);
finish();
@@ -298,71 +276,55 @@
}
}
- private class RestoreStackTask extends AsyncTask<Void, Void, Void> {
- private volatile boolean mRestoredStack;
- private volatile boolean mExternal;
+ @Override
+ void onStackRestored(boolean restored, boolean external) {
+ // Show drawer when no stack restored, but only when requesting
+ // non-visual content. However, if we last used an external app,
+ // drawer is always shown.
- @Override
- protected Void doInBackground(Void... params) {
- // Restore last stack for calling package
- final String packageName = getCallingPackageMaybeExtra();
- final Cursor cursor = getContentResolver()
- .query(RecentsProvider.buildResume(packageName), null, null, null, null);
- try {
- if (cursor.moveToFirst()) {
- mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
- final byte[] rawStack = cursor.getBlob(
- cursor.getColumnIndex(ResumeColumns.STACK));
- DurableUtils.readFromArray(rawStack, mState.stack);
- mRestoredStack = true;
- }
- } catch (IOException e) {
- Log.w(TAG, "Failed to resume: " + e);
- } finally {
- IoUtils.closeQuietly(cursor);
- }
-
- if (mRestoredStack) {
- // Update the restored stack to ensure we have freshest data
- final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState);
- try {
- mState.stack.updateRoot(matchingRoots);
- mState.stack.updateDocuments(getContentResolver());
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to restore stack: " + e);
- mState.stack.reset();
- mRestoredStack = false;
- }
- }
-
- return null;
+ boolean showDrawer = false;
+ if (!restored) {
+ showDrawer = true;
+ }
+ if (MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
+ showDrawer = false;
+ }
+ if (external && mState.action == ACTION_GET_CONTENT) {
+ showDrawer = true;
}
- @Override
- protected void onPostExecute(Void result) {
- if (isDestroyed()) return;
- mState.restored = true;
+ if (showDrawer) {
+ setRootsDrawerOpen(true);
+ }
+ }
- // Show drawer when no stack restored, but only when requesting
- // non-visual content. However, if we last used an external app,
- // drawer is always shown.
+ public void onAppPicked(ResolveInfo info) {
+ final Intent intent = new Intent(getIntent());
+ intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ intent.setComponent(new ComponentName(
+ info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
+ startActivityForResult(intent, CODE_FORWARD);
+ }
- boolean showDrawer = false;
- if (!mRestoredStack) {
- showDrawer = true;
- }
- if (MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
- showDrawer = false;
- }
- if (mExternal && mState.action == ACTION_GET_CONTENT) {
- showDrawer = true;
- }
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d(TAG, "onActivityResult() code=" + resultCode);
- if (showDrawer) {
- setRootsDrawerOpen(true);
- }
+ // Only relay back results when not canceled; otherwise stick around to
+ // let the user pick another app/backend.
+ if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
- onCurrentDirectoryChanged(ANIM_NONE);
+ // Remember that we last picked via external app
+ final String packageName = getCallingPackageMaybeExtra();
+ final ContentValues values = new ContentValues();
+ values.put(ResumeColumns.EXTERNAL, 1);
+ getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
+
+ // Pass back result to original caller
+ setResult(resultCode, data);
+ finish();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
}
}
@@ -397,7 +359,6 @@
updateActionBar();
}
- @Override
public void setRootsDrawerOpen(boolean open) {
if (!mShowAsDialog) {
if (open) {
@@ -416,6 +377,7 @@
}
}
+ @Override
public void updateActionBar() {
if (mRootsToolbar != null) {
if (mState.action == ACTION_OPEN ||
@@ -447,7 +409,7 @@
});
}
- if (mSearchExpanded) {
+ if (mSearchManager.isExpanded()) {
mToolbar.setTitle(null);
mToolbarStack.setVisibility(View.GONE);
mToolbarStack.setAdapter(null);
@@ -461,7 +423,7 @@
mToolbarStack.setVisibility(View.VISIBLE);
mToolbarStack.setAdapter(mStackAdapter);
- mIgnoreNextNavigation = true;
+ mStackListener.mIgnoreNextNavigation = true;
mToolbarStack.setSelection(mStackAdapter.getCount() - 1);
}
}
@@ -469,79 +431,18 @@
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
+ boolean showMenu = super.onCreateOptionsMenu(menu);
+
getMenuInflater().inflate(R.menu.activity, menu);
// Most actions are visible when showing as dialog
if (mShowAsDialog) {
- for (int i = 0; i < menu.size(); i++) {
- final MenuItem item = menu.getItem(i);
- switch (item.getItemId()) {
- case R.id.menu_advanced:
- case R.id.menu_file_size:
- break;
- default:
- item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- }
- }
+ expandMenus(menu);
}
- final MenuItem searchMenu = menu.findItem(R.id.menu_search);
- mSearchView = (SearchView) searchMenu.getActionView();
- mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String query) {
- mSearchExpanded = true;
- mState.currentSearch = query;
- mSearchView.clearFocus();
- onCurrentDirectoryChanged(ANIM_NONE);
- return true;
- }
+ this.mSearchManager.install(menu.findItem(R.id.menu_search));
- @Override
- public boolean onQueryTextChange(String newText) {
- return false;
- }
- });
-
- searchMenu.setOnActionExpandListener(new OnActionExpandListener() {
- @Override
- public boolean onMenuItemActionExpand(MenuItem item) {
- mSearchExpanded = true;
- updateActionBar();
- return true;
- }
-
- @Override
- public boolean onMenuItemActionCollapse(MenuItem item) {
- mSearchExpanded = false;
- if (mIgnoreNextCollapse) {
- mIgnoreNextCollapse = false;
- return true;
- }
-
- mState.currentSearch = null;
- onCurrentDirectoryChanged(ANIM_NONE);
- return true;
- }
- });
-
- mSearchView.setOnCloseListener(new SearchView.OnCloseListener() {
- @Override
- public boolean onClose() {
- mSearchExpanded = false;
- if (mIgnoreNextClose) {
- mIgnoreNextClose = false;
- return false;
- }
-
- mState.currentSearch = null;
- onCurrentDirectoryChanged(ANIM_NONE);
- return false;
- }
- });
-
- return true;
+ return showMenu;
}
@Override
@@ -554,7 +455,6 @@
final DocumentInfo cwd = getCurrentDirectory();
final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
- final MenuItem search = menu.findItem(R.id.menu_search);
final MenuItem sort = menu.findItem(R.id.menu_sort);
final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
final MenuItem grid = menu.findItem(R.id.menu_grid);
@@ -564,36 +464,23 @@
final MenuItem settings = menu.findItem(R.id.menu_settings);
sort.setVisible(cwd != null);
- grid.setVisible(mState.derivedMode != MODE_GRID);
- list.setVisible(mState.derivedMode != MODE_LIST);
+ grid.setVisible(mState.derivedMode != State.MODE_GRID);
+ list.setVisible(mState.derivedMode != State.MODE_LIST);
- if (mState.currentSearch != null) {
- // Search uses backend ranking; no sorting
- sort.setVisible(false);
- search.expandActionView();
+ mSearchManager.update(root);
- mSearchView.setIconified(false);
- mSearchView.clearFocus();
- mSearchView.setQuery(mState.currentSearch, false);
- } else {
- mIgnoreNextClose = true;
- mSearchView.setIconified(true);
- mSearchView.clearFocus();
-
- mIgnoreNextCollapse = true;
- search.collapseActionView();
- }
+ // Search uses backend ranking; no sorting
+ sort.setVisible(mSearchManager.isSearching());
// Only sort by size when visible
sortSize.setVisible(mState.showSize);
- boolean searchVisible;
boolean fileSizeVisible = !(mState.action == ACTION_MANAGE
|| mState.action == ACTION_BROWSE);
if (mState.action == ACTION_CREATE || mState.action == ACTION_OPEN_TREE) {
createDir.setVisible(cwd != null && cwd.isCreateSupported());
- searchVisible = false;
+ mSearchManager.showMenu(false);
// No display options in recent directories
if (cwd == null) {
@@ -607,14 +494,8 @@
}
} else {
createDir.setVisible(false);
-
- searchVisible = root != null
- && ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0);
}
- // TODO: close any search in-progress when hiding
- search.setVisible(searchVisible);
-
advanced.setTitle(LocalPreferences.getDisplayAdvancedDevices(this)
? R.string.menu_advanced_hide : R.string.menu_advanced_show);
fileSize.setTitle(LocalPreferences.getDisplayFileSize(this)
@@ -634,90 +515,7 @@
if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
-
- final int id = item.getItemId();
- if (id == android.R.id.home) {
- onBackPressed();
- return true;
- } else if (id == R.id.menu_create_dir) {
- CreateDirectoryFragment.show(getFragmentManager());
- return true;
- } else if (id == R.id.menu_search) {
- return false;
- } else if (id == R.id.menu_sort_name) {
- setUserSortOrder(State.SORT_ORDER_DISPLAY_NAME);
- return true;
- } else if (id == R.id.menu_sort_date) {
- setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
- return true;
- } else if (id == R.id.menu_sort_size) {
- setUserSortOrder(State.SORT_ORDER_SIZE);
- return true;
- } else if (id == R.id.menu_grid) {
- setUserMode(State.MODE_GRID);
- return true;
- } else if (id == R.id.menu_list) {
- setUserMode(State.MODE_LIST);
- return true;
- } else if (id == R.id.menu_advanced) {
- setDisplayAdvancedDevices(!LocalPreferences.getDisplayAdvancedDevices(this));
- return true;
- } else if (id == R.id.menu_file_size) {
- setDisplayFileSize(!LocalPreferences.getDisplayFileSize(this));
- return true;
- } else if (id == R.id.menu_settings) {
- final RootInfo root = getCurrentRoot();
- final Intent intent = new Intent(DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS);
- intent.setDataAndType(DocumentsContract.buildRootUri(root.authority, root.rootId),
- DocumentsContract.Root.MIME_TYPE_ITEM);
- startActivity(intent);
- return true;
- } else {
- return super.onOptionsItemSelected(item);
- }
- }
-
- private void setDisplayAdvancedDevices(boolean display) {
- LocalPreferences.setDisplayAdvancedDevices(this, display);
- mState.showAdvanced = mState.forceAdvanced | display;
- RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
- invalidateOptionsMenu();
- }
-
- private void setDisplayFileSize(boolean display) {
- LocalPreferences.setDisplayFileSize(this, display);
- mState.showSize = display;
- DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
- invalidateOptionsMenu();
- }
-
- @Override
- public void onStateChanged() {
- invalidateOptionsMenu();
- }
-
- /**
- * Set state sort order based on explicit user action.
- */
- private void setUserSortOrder(int sortOrder) {
- mState.userSortOrder = sortOrder;
- DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
- }
-
- /**
- * Set state mode based on explicit user action.
- */
- private void setUserMode(int mode) {
- mState.userMode = mode;
- DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
- }
-
- @Override
- public void setPending(boolean pending) {
- final SaveFragment save = SaveFragment.get(getFragmentManager());
- if (save != null) {
- save.setPending(pending);
- }
+ return super.onOptionsItemSelected(item);
}
@Override
@@ -740,131 +538,12 @@
}
@Override
- protected void onSaveInstanceState(Bundle state) {
- super.onSaveInstanceState(state);
- state.putParcelable(EXTRA_STATE, mState);
- }
-
- @Override
- protected void onRestoreInstanceState(Bundle state) {
- super.onRestoreInstanceState(state);
- }
-
- private BaseAdapter mStackAdapter = new BaseAdapter() {
- @Override
- public int getCount() {
- return mState.stack.size();
- }
-
- @Override
- public DocumentInfo getItem(int position) {
- return mState.stack.get(mState.stack.size() - position - 1);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.item_subdir_title, parent, false);
- }
-
- final TextView title = (TextView) convertView.findViewById(android.R.id.title);
- final DocumentInfo doc = getItem(position);
-
- if (position == 0) {
- final RootInfo root = getCurrentRoot();
- title.setText(root.title);
- } else {
- title.setText(doc.displayName);
- }
-
- return convertView;
- }
-
- @Override
- public View getDropDownView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.item_subdir, parent, false);
- }
-
- final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
- final TextView title = (TextView) convertView.findViewById(android.R.id.title);
- final DocumentInfo doc = getItem(position);
-
- if (position == 0) {
- final RootInfo root = getCurrentRoot();
- title.setText(root.title);
- subdir.setVisibility(View.GONE);
- } else {
- title.setText(doc.displayName);
- subdir.setVisibility(View.VISIBLE);
- }
-
- return convertView;
- }
- };
-
- private OnItemSelectedListener mStackListener = new OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- if (mIgnoreNextNavigation) {
- mIgnoreNextNavigation = false;
- return;
- }
-
- while (mState.stack.size() > position + 1) {
- mState.stackTouched = true;
- mState.stack.pop();
- }
- onCurrentDirectoryChanged(ANIM_UP);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- // Ignored
- }
- };
-
- @Override
- public RootInfo getCurrentRoot() {
- if (mState.stack.root != null) {
- return mState.stack.root;
- } else {
- return mRoots.getRecentsRoot();
- }
- }
-
- @Override
- public DocumentInfo getCurrentDirectory() {
- return mState.stack.peek();
- }
-
- private String getCallingPackageMaybeExtra() {
- final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
- return (extra != null) ? extra : getCallingPackage();
- }
-
- public Executor getCurrentExecutor() {
- final DocumentInfo cwd = getCurrentDirectory();
- if (cwd != null && cwd.authority != null) {
- return ProviderExecutor.forAuthority(cwd.authority);
- } else {
- return AsyncTask.THREAD_POOL_EXECUTOR;
- }
- }
-
- @Override
public State getDisplayState() {
return mState;
}
- private void onCurrentDirectoryChanged(int anim) {
+ @Override
+ void onDirectoryChanged(int anim) {
final FragmentManager fm = getFragmentManager();
final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory();
@@ -883,7 +562,7 @@
// Start recents in grid when requesting visual things
final boolean visualMimes = MimePredicate.mimeMatches(
MimePredicate.VISUAL_MIMES, mState.acceptMimes);
- mState.userMode = visualMimes ? MODE_GRID : MODE_LIST;
+ mState.userMode = visualMimes ? State.MODE_GRID : State.MODE_LIST;
mState.derivedMode = mState.userMode;
}
} else {
@@ -913,108 +592,20 @@
pick.setPickTarget(mState.action, cwd, displayName);
}
}
+ }
- final RootsFragment roots = RootsFragment.get(fm);
- if (roots != null) {
- roots.onCurrentRootChanged();
- }
+ void onSaveRequested(DocumentInfo replaceTarget) {
+ new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
+ }
- updateActionBar();
- invalidateOptionsMenu();
- dumpStack();
+ void onSaveRequested(String mimeType, String displayName) {
+ new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
}
@Override
- public void onStackPicked(DocumentStack stack) {
- try {
- // Update the restored stack to ensure we have freshest data
- stack.updateDocuments(getContentResolver());
-
- mState.stack = stack;
- mState.stackTouched = true;
- onCurrentDirectoryChanged(ANIM_SIDE);
-
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to restore stack: " + e);
- }
- }
-
- @Override
- public void onRootPicked(RootInfo root, boolean closeDrawer) {
- // Clear entire backstack and start in new root
- mState.stack.root = root;
- mState.stack.clear();
- mState.stackTouched = true;
-
- if (!mRoots.isRecentsRoot(root)) {
- new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
- } else {
- onCurrentDirectoryChanged(ANIM_SIDE);
- }
-
- if (closeDrawer) {
- setRootsDrawerOpen(false);
- }
- }
-
- private class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
- private RootInfo mRoot;
-
- public PickRootTask(RootInfo root) {
- mRoot = root;
- }
-
- @Override
- protected DocumentInfo doInBackground(Void... params) {
- try {
- final Uri uri = DocumentsContract.buildDocumentUri(
- mRoot.authority, mRoot.documentId);
- return DocumentInfo.fromUri(getContentResolver(), uri);
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to find root", e);
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(DocumentInfo result) {
- if (result != null) {
- mState.stack.push(result);
- mState.stackTouched = true;
- onCurrentDirectoryChanged(ANIM_SIDE);
- }
- }
- }
-
- @Override
- public void onAppPicked(ResolveInfo info) {
- final Intent intent = new Intent(getIntent());
- intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
- intent.setComponent(new ComponentName(
- info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
- startActivityForResult(intent, CODE_FORWARD);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- Log.d(TAG, "onActivityResult() code=" + resultCode);
-
- // Only relay back results when not canceled; otherwise stick around to
- // let the user pick another app/backend.
- if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
-
- // Remember that we last picked via external app
- final String packageName = getCallingPackageMaybeExtra();
- final ContentValues values = new ContentValues();
- values.put(ResumeColumns.EXTERNAL, 1);
- getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
-
- // Pass back result to original caller
- setResult(resultCode, data);
- finish();
- } else {
- super.onActivityResult(requestCode, resultCode, data);
- }
+ void onRootPicked(RootInfo root) {
+ super.onRootPicked(root);
+ setRootsDrawerOpen(false);
}
@Override
@@ -1076,17 +667,6 @@
}
}
- @Override
- public void onSaveRequested(DocumentInfo replaceTarget) {
- new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
- }
-
- @Override
- public void onSaveRequested(String mimeType, String displayName) {
- new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
- }
-
- @Override
public void onPickRequested(DocumentInfo pickTarget) {
Uri result;
if (mState.action == ACTION_OPEN_TREE) {
@@ -1101,7 +681,8 @@
new PickFinishTask(result).executeOnExecutor(getCurrentExecutor());
}
- private void saveStackBlocking() {
+ @Override
+ void saveStackBlocking() {
final ContentResolver resolver = getContentResolver();
final ContentValues values = new ContentValues();
@@ -1124,7 +705,8 @@
resolver.insert(RecentsProvider.buildResume(packageName), values);
}
- private void onFinished(Uri... uris) {
+ @Override
+ void onTaskFinished(Uri... uris) {
Log.d(TAG, "onFinished() " + Arrays.toString(uris));
final Intent intent = new Intent();
@@ -1159,7 +741,52 @@
finish();
}
- private class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
+ public static DocumentsActivity get(Fragment fragment) {
+ return (DocumentsActivity) fragment.getActivity();
+ }
+
+ private final class PickFinishTask extends AsyncTask<Void, Void, Void> {
+ private final Uri mUri;
+
+ public PickFinishTask(Uri uri) {
+ mUri = uri;
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ saveStackBlocking();
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ onTaskFinished(mUri);
+ }
+ }
+
+ final class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
+ private final Uri[] mUris;
+
+ public ExistingFinishTask(Uri... uris) {
+ mUris = uris;
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ saveStackBlocking();
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ onTaskFinished(mUris);
+ }
+ }
+
+ /**
+ * Task that creates a new document in the background.
+ */
+ final class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
private final String mMimeType;
private final String mDisplayName;
@@ -1201,7 +828,7 @@
@Override
protected void onPostExecute(Uri result) {
if (result != null) {
- onFinished(result);
+ onTaskFinished(result);
} else {
Toast.makeText(DocumentsActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
.show();
@@ -1210,50 +837,4 @@
setPending(false);
}
}
-
- private class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
- private final Uri[] mUris;
-
- public ExistingFinishTask(Uri... uris) {
- mUris = uris;
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- saveStackBlocking();
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- onFinished(mUris);
- }
- }
-
- private class PickFinishTask extends AsyncTask<Void, Void, Void> {
- private final Uri mUri;
-
- public PickFinishTask(Uri uri) {
- mUri = uri;
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- saveStackBlocking();
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- onFinished(mUri);
- }
- }
-
- private void dumpStack() {
- Log.d(TAG, "Current stack: ");
- Log.d(TAG, " * " + mState.stack.root);
- for (DocumentInfo doc : mState.stack) {
- Log.d(TAG, " +-- " + doc);
- }
- }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
index 7ea51b9..e899379 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
@@ -75,7 +75,7 @@
private View.OnClickListener mPickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
- final BaseActivity activity = BaseActivity.get(PickFragment.this);
+ final DocumentsActivity activity = DocumentsActivity.get(PickFragment.this);
activity.onPickRequested(mPickTarget);
}
};
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index 26aecc5..e11d7d9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -109,8 +109,9 @@
mAdapter.swapStacks(data);
// When launched into empty recents, show drawer
- if (mAdapter.isEmpty() && !state.stackTouched) {
- ((BaseActivity) context).setRootsDrawerOpen(true);
+ if (mAdapter.isEmpty() && !state.stackTouched &&
+ context instanceof DocumentsActivity) {
+ ((DocumentsActivity) context).setRootsDrawerOpen(true);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index ed5e123..fd67a77 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -174,11 +174,12 @@
private OnItemClickListener mItemListener = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- final BaseActivity activity = BaseActivity.get(RootsFragment.this);
- final Item item = mAdapter.getItem(position);
+ Item item = mAdapter.getItem(position);
if (item instanceof RootItem) {
- activity.onRootPicked(((RootItem) item).root, true);
+ BaseActivity activity = BaseActivity.get(RootsFragment.this);
+ activity.onRootPicked(((RootItem) item).root);
} else if (item instanceof AppItem) {
+ DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this);
activity.onAppPicked(((AppItem) item).info);
} else {
throw new IllegalStateException("Unknown root: " + item);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
index a13fccc..ce98db2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
@@ -113,7 +113,7 @@
private View.OnClickListener mSaveListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
- final BaseActivity activity = BaseActivity.get(SaveFragment.this);
+ final DocumentsActivity activity = DocumentsActivity.get(SaveFragment.this);
if (mReplaceTarget != null) {
activity.onSaveRequested(mReplaceTarget);
} else {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
index 976f21d..f89b182 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
@@ -16,50 +16,29 @@
package com.android.documentsui;
-
import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
-import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
import static com.android.documentsui.DirectoryFragment.ANIM_UP;
import android.app.Activity;
-import android.app.Fragment;
import android.app.FragmentManager;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
-import android.content.ComponentName;
-import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.database.Cursor;
import android.graphics.Point;
import android.net.Uri;
-import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Debug;
import android.provider.DocumentsContract;
-import android.support.v4.app.ActionBarDrawerToggle;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v4.widget.DrawerLayout.DrawerListener;
+import android.provider.DocumentsContract.Root;
import android.util.Log;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.MenuItem.OnActionExpandListener;
import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowManager;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.SearchView;
-import android.widget.SearchView.OnQueryTextListener;
import android.widget.Spinner;
-import android.widget.TextView;
import android.widget.Toast;
import android.widget.Toolbar;
@@ -70,55 +49,37 @@
import com.android.documentsui.model.DurableUtils;
import com.android.documentsui.model.RootInfo;
-import libcore.io.IoUtils;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.List;
-import java.util.concurrent.Executor;
+/**
+ * Activity providing a directly launchable file management activity.
+ */
public class StandaloneActivity extends BaseActivity {
public static final String TAG = "StandaloneFileManagement";
- private static final String EXTRA_STATE = "state";
-
- private static final int CODE_FORWARD = 42;
-
- private SearchView mSearchView;
-
private Toolbar mToolbar;
private Spinner mToolbarStack;
-
private Toolbar mRootsToolbar;
-
- private ActionBarDrawerToggle mDrawerToggle;
-
private DirectoryContainerView mDirectoryContainer;
-
- private boolean mIgnoreNextNavigation;
- private boolean mIgnoreNextClose;
- private boolean mIgnoreNextCollapse;
-
- private boolean mSearchExpanded;
-
- private RootsCache mRoots;
+ private SearchManager mSearchManager;
private State mState;
+ private ItemSelectedListener mStackListener;
+ private BaseAdapter mStackAdapter;
+
+ public StandaloneActivity() {
+ super(TAG);
+ }
@Override
public void onCreate(Bundle icicle) {
- // Debug.waitForDebugger();
super.onCreate(icicle);
- mRoots = DocumentsApplication.getRootsCache(this);
-
setResult(Activity.RESULT_CANCELED);
setContentView(R.layout.activity);
final Context context = this;
- final Resources res = getResources();
// Strongly define our horizontal dimension; we leave vertical as
final WindowManager.LayoutParams a = getWindow().getAttributes();
@@ -131,16 +92,17 @@
mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
- if (icicle != null) {
- mState = icicle.getParcelable(EXTRA_STATE);
- } else {
- buildDefaultState();
- }
+ mState = (icicle != null)
+ ? icicle.<State>getParcelable(EXTRA_STATE)
+ : buildDefaultState();
+ mSearchManager = new SearchManager();
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mToolbar.setTitleTextAppearance(context,
android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
+ mStackAdapter = new StackAdapter();
+ mStackListener = new ItemSelectedListener();
mToolbarStack = (Spinner) findViewById(R.id.stack);
mToolbarStack.setOnItemSelectedListener(mStackListener);
@@ -167,87 +129,33 @@
}
}
- private void buildDefaultState() {
- mState = new State();
+ private State buildDefaultState() {
+ State state = new State();
final Intent intent = getIntent();
- mState.action = State.ACTION_BROWSE_ALL;
- mState.acceptMimes = new String[] { "*/*" };
- mState.allowMultiple = true;
- mState.acceptMimes = new String[] { intent.getType() };
- mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
- mState.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
- mState.showAdvanced = mState.forceAdvanced
+ state.action = State.ACTION_BROWSE_ALL;
+ state.acceptMimes = new String[] { "*/*" };
+ state.allowMultiple = true;
+ state.acceptMimes = new String[] { intent.getType() };
+ state.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
+ state.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+ state.showAdvanced = state.forceAdvanced
| LocalPreferences.getDisplayAdvancedDevices(this);
- mState.showSize = true;
+ state.showSize = true;
final DocumentStack stack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
if (stack != null)
- mState.stack = stack;
- }
+ state.stack = stack;
- private class RestoreStackTask extends AsyncTask<Void, Void, Void> {
- private volatile boolean mRestoredStack;
- private volatile boolean mExternal;
-
- @Override
- protected Void doInBackground(Void... params) {
- // Restore last stack for calling package
- final String packageName = getCallingPackageMaybeExtra();
- final Cursor cursor = getContentResolver()
- .query(RecentsProvider.buildResume(packageName), null, null, null, null);
- try {
- if (cursor.moveToFirst()) {
- mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
- final byte[] rawStack = cursor.getBlob(
- cursor.getColumnIndex(ResumeColumns.STACK));
- DurableUtils.readFromArray(rawStack, mState.stack);
- mRestoredStack = true;
- }
- } catch (IOException e) {
- Log.w(TAG, "Failed to resume: " + e);
- } finally {
- IoUtils.closeQuietly(cursor);
- }
-
- if (mRestoredStack) {
- // Update the restored stack to ensure we have freshest data
- final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState);
- try {
- mState.stack.updateRoot(matchingRoots);
- mState.stack.updateDocuments(getContentResolver());
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to restore stack: " + e);
- mState.stack.reset();
- mRestoredStack = false;
- }
- }
-
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- if (isDestroyed()) return;
- mState.restored = true;
- onCurrentDirectoryChanged(ANIM_NONE);
- }
+ return state;
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
- if (mDrawerToggle != null) {
- mDrawerToggle.syncState();
- }
updateActionBar();
}
@Override
- public void setRootsDrawerOpen(boolean open) {
- Log.w(TAG, "Trying to change state of roots drawer to > " + (open ? "open" : "closed"));
- // throw new UnsupportedOperationException();
- }
-
public void updateActionBar() {
final RootInfo root = getCurrentRoot();
mToolbar.setNavigationIcon(
@@ -255,7 +163,7 @@
mToolbar.setNavigationContentDescription(R.string.drawer_open);
mToolbar.setNavigationOnClickListener(null);
- if (mSearchExpanded) {
+ if (mSearchManager.isExpanded()) {
mToolbar.setTitle(null);
mToolbarStack.setVisibility(View.GONE);
mToolbarStack.setAdapter(null);
@@ -269,7 +177,7 @@
mToolbarStack.setVisibility(View.VISIBLE);
mToolbarStack.setAdapter(mStackAdapter);
- mIgnoreNextNavigation = true;
+ mStackListener.mIgnoreNextNavigation = true;
mToolbarStack.setSelection(mStackAdapter.getCount() - 1);
}
}
@@ -277,220 +185,60 @@
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
+ boolean showMenu = super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.activity, menu);
- for (int i = 0; i < menu.size(); i++) {
- final MenuItem item = menu.getItem(i);
- switch (item.getItemId()) {
- case R.id.menu_advanced:
- case R.id.menu_file_size:
- break;
- default:
- item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- }
- }
+ expandMenus(menu);
- final MenuItem searchMenu = menu.findItem(R.id.menu_search);
- mSearchView = (SearchView) searchMenu.getActionView();
- mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String query) {
- mSearchExpanded = true;
- mState.currentSearch = query;
- mSearchView.clearFocus();
- onCurrentDirectoryChanged(ANIM_NONE);
- return true;
- }
+ this.mSearchManager.install(menu.findItem(R.id.menu_search));
- @Override
- public boolean onQueryTextChange(String newText) {
- return false;
- }
- });
-
- searchMenu.setOnActionExpandListener(new OnActionExpandListener() {
- @Override
- public boolean onMenuItemActionExpand(MenuItem item) {
- mSearchExpanded = true;
- updateActionBar();
- return true;
- }
-
- @Override
- public boolean onMenuItemActionCollapse(MenuItem item) {
- mSearchExpanded = false;
- if (mIgnoreNextCollapse) {
- mIgnoreNextCollapse = false;
- return true;
- }
-
- mState.currentSearch = null;
- onCurrentDirectoryChanged(ANIM_NONE);
- return true;
- }
- });
-
- mSearchView.setOnCloseListener(new SearchView.OnCloseListener() {
- @Override
- public boolean onClose() {
- mSearchExpanded = false;
- if (mIgnoreNextClose) {
- mIgnoreNextClose = false;
- return false;
- }
-
- mState.currentSearch = null;
- onCurrentDirectoryChanged(ANIM_NONE);
- return false;
- }
- });
-
- return true;
+ return showMenu;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- final FragmentManager fm = getFragmentManager();
-
final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory();
final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
- final MenuItem search = menu.findItem(R.id.menu_search);
final MenuItem sort = menu.findItem(R.id.menu_sort);
final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
final MenuItem grid = menu.findItem(R.id.menu_grid);
final MenuItem list = menu.findItem(R.id.menu_list);
final MenuItem advanced = menu.findItem(R.id.menu_advanced);
final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
+ final MenuItem settings = menu.findItem(R.id.menu_settings);
- sort.setVisible(cwd != null);
grid.setVisible(mState.derivedMode != State.MODE_GRID);
list.setVisible(mState.derivedMode != State.MODE_LIST);
- if (mState.currentSearch != null) {
- // Search uses backend ranking; no sorting
- sort.setVisible(false);
-
- search.expandActionView();
-
- mSearchView.setIconified(false);
- mSearchView.clearFocus();
- mSearchView.setQuery(mState.currentSearch, false);
- } else {
- mIgnoreNextClose = true;
- mSearchView.setIconified(true);
- mSearchView.clearFocus();
-
- mIgnoreNextCollapse = true;
- search.collapseActionView();
- }
+ mSearchManager.update(root);
+ sort.setVisible(cwd != null && !mSearchManager.isSearching());
// Only sort by size when visible
sortSize.setVisible(mState.showSize);
- fileSize.setVisible(true);
- search.setVisible(true);
- createDir.setVisible(true);
- advanced.setVisible(true);
+ createDir.setVisible(cwd != null
+ && cwd.isCreateSupported()
+ && !mSearchManager.isSearching()
+ && !root.isDownloads());
+
+ fileSize.setVisible(cwd != null);
+ advanced.setVisible(cwd != null);
advanced.setTitle(LocalPreferences.getDisplayAdvancedDevices(this)
? R.string.menu_advanced_hide : R.string.menu_advanced_show);
fileSize.setTitle(LocalPreferences.getDisplayFileSize(this)
? R.string.menu_file_size_hide : R.string.menu_file_size_show);
+ settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0);
return true;
}
@Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
- return true;
- }
-
- final int id = item.getItemId();
- if (id == android.R.id.home) {
- onBackPressed();
- return true;
- } else if (id == R.id.menu_create_dir) {
- CreateDirectoryFragment.show(getFragmentManager());
- return true;
- } else if (id == R.id.menu_search) {
- return false;
- } else if (id == R.id.menu_sort_name) {
- setUserSortOrder(State.SORT_ORDER_DISPLAY_NAME);
- return true;
- } else if (id == R.id.menu_sort_date) {
- setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
- return true;
- } else if (id == R.id.menu_sort_size) {
- setUserSortOrder(State.SORT_ORDER_SIZE);
- return true;
- } else if (id == R.id.menu_grid) {
- setUserMode(State.MODE_GRID);
- return true;
- } else if (id == R.id.menu_list) {
- setUserMode(State.MODE_LIST);
- return true;
- } else if (id == R.id.menu_advanced) {
- setDisplayAdvancedDevices(!LocalPreferences.getDisplayAdvancedDevices(this));
- return true;
- } else if (id == R.id.menu_file_size) {
- setDisplayFileSize(!LocalPreferences.getDisplayFileSize(this));
- return true;
- } else {
- return super.onOptionsItemSelected(item);
- }
- }
-
- private void setDisplayAdvancedDevices(boolean display) {
- LocalPreferences.setDisplayAdvancedDevices(this, display);
- mState.showAdvanced = mState.forceAdvanced | display;
- RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
- invalidateOptionsMenu();
- }
-
- private void setDisplayFileSize(boolean display) {
- LocalPreferences.setDisplayFileSize(this, display);
- mState.showSize = display;
- DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
- invalidateOptionsMenu();
- }
-
- @Override
- public void onStateChanged() {
- invalidateOptionsMenu();
- }
-
- /**
- * Set state sort order based on explicit user action.
- */
- private void setUserSortOrder(int sortOrder) {
- mState.userSortOrder = sortOrder;
- DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
- }
-
- /**
- * Set state mode based on explicit user action.
- */
- private void setUserMode(int mode) {
- mState.userMode = mode;
- DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
- }
-
- @Override
- public void setPending(boolean pending) {
- final SaveFragment save = SaveFragment.get(getFragmentManager());
- if (save != null) {
- save.setPending(pending);
- }
- }
-
- @Override
public void onBackPressed() {
if (!mState.stackTouched) {
super.onBackPressed();
@@ -507,130 +255,12 @@
}
@Override
- protected void onSaveInstanceState(Bundle state) {
- super.onSaveInstanceState(state);
- state.putParcelable(EXTRA_STATE, mState);
- }
-
- @Override
- protected void onRestoreInstanceState(Bundle state) {
- super.onRestoreInstanceState(state);
- }
-
- private BaseAdapter mStackAdapter = new BaseAdapter() {
- @Override
- public int getCount() {
- return mState.stack.size();
- }
-
- @Override
- public DocumentInfo getItem(int position) {
- return mState.stack.get(mState.stack.size() - position - 1);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.item_subdir_title, parent, false);
- }
-
- final TextView title = (TextView) convertView.findViewById(android.R.id.title);
- final DocumentInfo doc = getItem(position);
-
- if (position == 0) {
- final RootInfo root = getCurrentRoot();
- title.setText(root.title);
- } else {
- title.setText(doc.displayName);
- }
-
- return convertView;
- }
-
- @Override
- public View getDropDownView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.item_subdir, parent, false);
- }
-
- final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
- final TextView title = (TextView) convertView.findViewById(android.R.id.title);
- final DocumentInfo doc = getItem(position);
-
- if (position == 0) {
- final RootInfo root = getCurrentRoot();
- title.setText(root.title);
- subdir.setVisibility(View.GONE);
- } else {
- title.setText(doc.displayName);
- subdir.setVisibility(View.VISIBLE);
- }
-
- return convertView;
- }
- };
-
- private OnItemSelectedListener mStackListener = new OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- if (mIgnoreNextNavigation) {
- mIgnoreNextNavigation = false;
- return;
- }
-
- while (mState.stack.size() > position + 1) {
- mState.stackTouched = true;
- mState.stack.pop();
- }
- onCurrentDirectoryChanged(ANIM_UP);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- // Ignored
- }
- };
-
- @Override
- public RootInfo getCurrentRoot() {
- if (mState.stack.root != null) {
- return mState.stack.root;
- } else {
- return mRoots.getRecentsRoot();
- }
- }
-
- public DocumentInfo getCurrentDirectory() {
- return mState.stack.peek();
- }
-
- private String getCallingPackageMaybeExtra() {
- final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
- return (extra != null) ? extra : getCallingPackage();
- }
-
- public Executor getCurrentExecutor() {
- final DocumentInfo cwd = getCurrentDirectory();
- if (cwd != null && cwd.authority != null) {
- return ProviderExecutor.forAuthority(cwd.authority);
- } else {
- return AsyncTask.THREAD_POOL_EXECUTOR;
- }
- }
-
- @Override
public State getDisplayState() {
return mState;
}
- private void onCurrentDirectoryChanged(int anim) {
+ @Override
+ void onDirectoryChanged(int anim) {
final FragmentManager fm = getFragmentManager();
final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory();
@@ -654,109 +284,10 @@
DirectoryFragment.showNormal(fm, root, cwd, anim);
}
}
-
- final RootsFragment roots = RootsFragment.get(fm);
- if (roots != null) {
- roots.onCurrentRootChanged();
- }
-
- updateActionBar();
- invalidateOptionsMenu();
- dumpStack();
- }
-
- @Override
- public void onStackPicked(DocumentStack stack) {
- try {
- // Update the restored stack to ensure we have freshest data
- stack.updateDocuments(getContentResolver());
-
- mState.stack = stack;
- mState.stackTouched = true;
- onCurrentDirectoryChanged(ANIM_SIDE);
-
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to restore stack: " + e);
- }
- }
-
- @Override
- public void onRootPicked(RootInfo root, boolean closeDrawer) {
- // Clear entire backstack and start in new root
- mState.stack.root = root;
- mState.stack.clear();
- mState.stackTouched = true;
-
- if (!mRoots.isRecentsRoot(root)) {
- new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
- } else {
- onCurrentDirectoryChanged(ANIM_SIDE);
- }
- }
-
- private class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
- private RootInfo mRoot;
-
- public PickRootTask(RootInfo root) {
- mRoot = root;
- }
-
- @Override
- protected DocumentInfo doInBackground(Void... params) {
- try {
- final Uri uri = DocumentsContract.buildDocumentUri(
- mRoot.authority, mRoot.documentId);
- return DocumentInfo.fromUri(getContentResolver(), uri);
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to find root", e);
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(DocumentInfo result) {
- if (result != null) {
- mState.stack.push(result);
- mState.stackTouched = true;
- onCurrentDirectoryChanged(ANIM_SIDE);
- }
- }
- }
-
- @Override
- public void onAppPicked(ResolveInfo info) {
- final Intent intent = new Intent(getIntent());
- intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
- intent.setComponent(new ComponentName(
- info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
- startActivityForResult(intent, CODE_FORWARD);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- Log.d(TAG, "onActivityResult() code=" + resultCode);
-
- // Only relay back results when not canceled; otherwise stick around to
- // let the user pick another app/backend.
- if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
-
- // Remember that we last picked via external app
- final String packageName = getCallingPackageMaybeExtra();
- final ContentValues values = new ContentValues();
- values.put(ResumeColumns.EXTERNAL, 1);
- getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
-
- // Pass back result to original caller
- setResult(resultCode, data);
- finish();
- } else {
- super.onActivityResult(requestCode, resultCode, data);
- }
}
@Override
public void onDocumentPicked(DocumentInfo doc) {
- final FragmentManager fm = getFragmentManager();
if (doc.isDirectory()) {
mState.stack.push(doc);
mState.stackTouched = true;
@@ -780,27 +311,12 @@
}
@Override
- public void onSaveRequested(DocumentInfo replaceTarget) {
- new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
- }
-
- @Override
- public void onSaveRequested(String mimeType, String displayName) {
- new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
- }
-
- @Override
- public void onPickRequested(DocumentInfo pickTarget) {
- final Uri viaUri = DocumentsContract.buildTreeDocumentUri(pickTarget.authority,
- pickTarget.documentId);
- new PickFinishTask(viaUri).executeOnExecutor(getCurrentExecutor());
- }
-
- private void saveStackBlocking() {
+ void saveStackBlocking() {
final ContentResolver resolver = getContentResolver();
final ContentValues values = new ContentValues();
- final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
+ final byte[] rawStack = DurableUtils.writeToArrayOrNull(
+ getDisplayState().stack);
// Remember location for next app launch
final String packageName = getCallingPackageMaybeExtra();
@@ -810,7 +326,8 @@
resolver.insert(RecentsProvider.buildResume(packageName), values);
}
- private void onFinished(Uri... uris) {
+ @Override
+ void onTaskFinished(Uri... uris) {
Log.d(TAG, "onFinished() " + Arrays.toString(uris));
final Intent intent = new Intent();
@@ -832,106 +349,4 @@
setResult(Activity.RESULT_OK, intent);
finish();
}
-
- private class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
- private final String mMimeType;
- private final String mDisplayName;
-
- public CreateFinishTask(String mimeType, String displayName) {
- mMimeType = mimeType;
- mDisplayName = displayName;
- }
-
- @Override
- protected void onPreExecute() {
- setPending(true);
- }
-
- @Override
- protected Uri doInBackground(Void... params) {
- final ContentResolver resolver = getContentResolver();
- final DocumentInfo cwd = getCurrentDirectory();
-
- ContentProviderClient client = null;
- Uri childUri = null;
- try {
- client = DocumentsApplication.acquireUnstableProviderOrThrow(
- resolver, cwd.derivedUri.getAuthority());
- childUri = DocumentsContract.createDocument(
- client, cwd.derivedUri, mMimeType, mDisplayName);
- } catch (Exception e) {
- Log.w(TAG, "Failed to create document", e);
- } finally {
- ContentProviderClient.releaseQuietly(client);
- }
-
- if (childUri != null) {
- saveStackBlocking();
- }
-
- return childUri;
- }
-
- @Override
- protected void onPostExecute(Uri result) {
- if (result != null) {
- onFinished(result);
- } else {
- Toast.makeText(StandaloneActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
- .show();
- }
-
- setPending(false);
- }
- }
-
- private class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
- private final Uri[] mUris;
-
- public ExistingFinishTask(Uri... uris) {
- mUris = uris;
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- saveStackBlocking();
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- onFinished(mUris);
- }
- }
-
- private class PickFinishTask extends AsyncTask<Void, Void, Void> {
- private final Uri mUri;
-
- public PickFinishTask(Uri uri) {
- mUri = uri;
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- saveStackBlocking();
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- onFinished(mUri);
- }
- }
-
- private void dumpStack() {
- Log.d(TAG, "Current stack: ");
- Log.d(TAG, " * " + mState.stack.root);
- for (DocumentInfo doc : mState.stack) {
- Log.d(TAG, " +-- " + doc);
- }
- }
-
- public static BaseActivity get(Fragment fragment) {
- return (BaseActivity) fragment.getActivity();
- }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index c7092b3..0385d1e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -383,8 +383,9 @@
Slog.i(LOG_TAG, "[PERSIST END]");
}
- } catch (IOException e) {
- Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", e);
+ // Any error while writing is fatal.
+ } catch (Throwable t) {
+ Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
destination.failWrite(out);
} finally {
IoUtils.closeQuietly(out);
@@ -406,9 +407,11 @@
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, null);
parseStateLocked(parser);
- } catch (XmlPullParserException | IOException ise) {
+
+ // Any error while parsing is fatal.
+ } catch (Throwable t) {
throw new IllegalStateException("Failed parsing settings file: "
- + mStatePersistFile , ise);
+ + mStatePersistFile , t);
} finally {
IoUtils.closeQuietly(in);
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a8a4baa..24f6931 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -208,6 +208,10 @@
</intent-filter>
</receiver>
+ <!-- Callback for dismissing screenshot notification after a share target is picked -->
+ <receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
+ android:exported="false" />
+
<!-- started from UsbDeviceSettingsManager -->
<activity android:name=".usb.UsbConfirmActivity"
android:exported="true"
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 105bf0f..715f4e4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -25,6 +25,7 @@
import android.app.Notification.BigPictureStyle;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -242,7 +243,12 @@
sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
- Intent chooserIntent = Intent.createChooser(sharingIntent, null);
+ final PendingIntent callback = PendingIntent.getBroadcast(context, 0,
+ new Intent(context, GlobalScreenshot.TargetChosenReceiver.class)
+ .putExtra(GlobalScreenshot.CANCEL_ID, mNotificationId),
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+ Intent chooserIntent = Intent.createChooser(sharingIntent, null,
+ callback.getIntentSender());
chooserIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -341,6 +347,8 @@
class GlobalScreenshot {
private static final String TAG = "GlobalScreenshot";
+ static final String CANCEL_ID = "android:cancel_id";
+
private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;
private static final int SCREENSHOT_DROP_IN_DURATION = 430;
private static final int SCREENSHOT_DROP_OUT_DELAY = 500;
@@ -732,4 +740,22 @@
.build();
nManager.notify(R.id.notification_screenshot, n);
}
+
+ /**
+ * Removes the notification for a screenshot after a share target is chosen.
+ */
+ public static class TargetChosenReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.hasExtra(CANCEL_ID)) {
+ return;
+ }
+
+ final NotificationManager nm =
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ final int id = intent.getIntExtra(CANCEL_ID, 0);
+ nm.cancel(id);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 964d75f..1c53655 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -95,7 +95,7 @@
if (mContractedChild != null) {
int size = Math.min(maxSize, mSmallHeight);
mContractedChild.measure(widthMeasureSpec,
- MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
+ MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY));
maxChildHeight = Math.max(maxChildHeight, mContractedChild.getMeasuredHeight());
}
if (mExpandedChild != null) {
diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk
index 94f0859..0658620 100644
--- a/rs/jni/Android.mk
+++ b/rs/jni/Android.mk
@@ -25,6 +25,7 @@
$(JNI_H_INCLUDE) \
frameworks/rs \
frameworks/base/core/jni \
+ frameworks/base/libs/hwui \
$(rs_generated_include_dir)
LOCAL_CFLAGS += -Wno-unused-parameter -std=c++11
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9f80fd8..5d02576 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -460,6 +460,7 @@
// set to null if in idle mode; while in this mode, any alarms we don't want
// to run during this time are placed in mPendingWhileIdleAlarms
Alarm mPendingIdleUntil = null;
+ Alarm mNextWakeFromIdle = null;
final ArrayList<Alarm> mPendingWhileIdleAlarms = new ArrayList<>();
public AlarmManagerService(Context context) {
@@ -583,7 +584,7 @@
final int mAlarmType;
InFlight(AlarmManagerService service, PendingIntent pendingIntent, WorkSource workSource,
- int alarmType, String tag) {
+ int alarmType, String tag, long nowELAPSED) {
mPendingIntent = pendingIntent;
mWorkSource = workSource;
mTag = tag;
@@ -593,6 +594,7 @@
fs = new FilterStats(mBroadcastStats, mTag);
mBroadcastStats.filterStats.put(mTag, fs);
}
+ fs.lastTime = nowELAPSED;
mFilterStats = fs;
mAlarmType = alarmType;
}
@@ -602,6 +604,7 @@
final BroadcastStats mBroadcastStats;
final String mTag;
+ long lastTime;
long aggregateTime;
int count;
int numWakeup;
@@ -806,33 +809,38 @@
setImplLocked(a, false, doValidate);
}
+ private void updateNextWakeFromIdleFuzzLocked() {
+ if (mNextWakeFromIdle != null) {
+
+ }
+ }
+
private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) {
if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
- // This is a special alarm that will put the system idle until it goes off.
+ // This is a special alarm that will put the system into idle until it goes off.
// The caller has given the time they want this to happen at, however we need
// to pull that earlier if there are existing alarms that have requested to
// bring us out of idle.
- final int N = mAlarmBatches.size();
- for (int i = 0; i < N; i++) {
- Batch b = mAlarmBatches.get(i);
- if (a.whenElapsed > b.end) {
- // There are no interesting things happening before our idle until,
- // so keep the requested time.
- break;
- }
- if ((b.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
- a.when = a.whenElapsed = a.maxWhenElapsed = b.end;
- break;
- }
+ if (mNextWakeFromIdle != null) {
+ a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed;
}
// Add fuzz to make the alarm go off some time before the actual desired time.
final long nowElapsed = SystemClock.elapsedRealtime();
- long fuzz = fuzzForDuration(a.whenElapsed-nowElapsed);
+ final int fuzz = fuzzForDuration(a.whenElapsed-nowElapsed);
if (fuzz > 0) {
if (mRandom == null) {
mRandom = new Random();
}
- a.whenElapsed -= mRandom.nextLong() % fuzz;
+ final int delta = mRandom.nextInt(fuzz);
+ a.whenElapsed -= delta;
+ if (false) {
+ Slog.d(TAG, "Alarm when: " + a.whenElapsed);
+ Slog.d(TAG, "Delta until alarm: " + (a.whenElapsed-nowElapsed));
+ Slog.d(TAG, "Applied fuzz: " + fuzz);
+ Slog.d(TAG, "Final delta: " + delta);
+ Slog.d(TAG, "Final when: " + a.whenElapsed);
+ }
+ a.when = a.maxWhenElapsed = a.whenElapsed;
}
} else if (mPendingIdleUntil != null) {
@@ -869,11 +877,16 @@
if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
mPendingIdleUntil = a;
needRebatch = true;
- } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0 && mPendingIdleUntil != null) {
- // If we are adding an alarm that asks to wake from idle, and we are currently
- // idling, then we need to rebatch alarms in case the idle until time needs to
- // be updated.
- needRebatch = true;
+ } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+ if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) {
+ mNextWakeFromIdle = a;
+ // If this wake from idle is earlier than whatever was previously scheduled,
+ // and we are currently idling, then we need to rebatch alarms in case the idle
+ // until time needs to be updated.
+ if (mPendingIdleUntil != null) {
+ needRebatch = true;
+ }
+ }
}
if (!rebatching) {
@@ -960,6 +973,11 @@
}
@Override
+ public long getNextWakeFromIdleTime() {
+ return getNextWakeFromIdleTimeImpl();
+ }
+
+ @Override
public AlarmManager.AlarmClockInfo getNextAlarmClock(int userId) {
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false /* allowAll */, false /* requireFull */,
@@ -1030,10 +1048,15 @@
pw.println();
pw.println("Idle mode state:");
pw.print(" Idling until: "); pw.println(mPendingIdleUntil);
- mPendingIdleUntil.dump(pw, " ", nowELAPSED, nowRTC, sdf);
+ mPendingIdleUntil.dump(pw, " ", nowRTC, nowELAPSED, sdf);
pw.println(" Pending alarms:");
dumpAlarmList(pw, mPendingWhileIdleAlarms, " ", nowELAPSED, nowRTC, sdf);
}
+ if (mNextWakeFromIdle != null) {
+ pw.println();
+ pw.print(" Next wake from idle: "); pw.println(mNextWakeFromIdle);
+ mNextWakeFromIdle.dump(pw, " ", nowRTC, nowELAPSED, sdf);
+ }
pw.println();
pw.print("Past-due non-wakeup alarms: ");
@@ -1140,7 +1163,10 @@
TimeUtils.formatDuration(fs.aggregateTime, pw);
pw.print(" "); pw.print(fs.numWakeup);
pw.print(" wakes " ); pw.print(fs.count);
- pw.print(" alarms: ");
+ pw.print(" alarms, last ");
+ TimeUtils.formatDuration(fs.lastTime, nowELAPSED, pw);
+ pw.println(":");
+ pw.print(" ");
pw.print(fs.mTag);
pw.println();
}
@@ -1216,7 +1242,13 @@
return null;
}
- private AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) {
+ long getNextWakeFromIdleTimeImpl() {
+ synchronized (mLock) {
+ return mNextWakeFromIdle != null ? mNextWakeFromIdle.whenElapsed : Long.MAX_VALUE;
+ }
+ }
+
+ AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) {
synchronized (mLock) {
return mNextAlarmClockForUser.get(userId);
}
@@ -1398,6 +1430,9 @@
mPendingIdleUntil = null;
restorePending = true;
}
+ if (mNextWakeFromIdle != null && mNextWakeFromIdle.operation.equals(operation)) {
+ mNextWakeFromIdle = null;
+ }
rebatchAllAlarmsLocked(true);
if (restorePending) {
restorePendingWhileIdleAlarmsLocked();
@@ -1585,11 +1620,19 @@
Alarm alarm = batch.get(i);
alarm.count = 1;
triggerList.add(alarm);
+ if ((alarm.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+ EventLogTags.writeDeviceIdleWakeFromIdle(mPendingIdleUntil != null ? 1 : 0,
+ alarm.tag);
+ }
if (mPendingIdleUntil == alarm) {
mPendingIdleUntil = null;
rebatchAllAlarmsLocked(false);
restorePendingWhileIdleAlarmsLocked();
}
+ if (mNextWakeFromIdle == alarm) {
+ mNextWakeFromIdle = null;
+ rebatchAllAlarmsLocked(false);
+ }
// Recurring alarms may have passed several alarm intervals while the
// phone was asleep or off, so pass a trigger count when sending them.
@@ -1655,7 +1698,7 @@
public final long origWhen;
public final boolean wakeup;
public final PendingIntent operation;
- public final String tag;
+ public final String tag;
public final WorkSource workSource;
public final int flags;
public int count;
@@ -1725,6 +1768,12 @@
pw.print(" repeatInterval="); pw.print(repeatInterval);
pw.print(" count="); pw.print(count);
pw.print(" flags=0x"); pw.println(Integer.toHexString(flags));
+ if (alarmClock != null) {
+ pw.print(prefix); pw.println("Alarm clock:");
+ pw.print(prefix); pw.print(" triggerTime=");
+ pw.println(sdf.format(new Date(alarmClock.getTriggerTime())));
+ pw.print(prefix); pw.print(" showIntent="); pw.println(alarmClock.getShowIntent());
+ }
pw.print(prefix); pw.print("operation="); pw.println(operation);
}
}
@@ -1762,11 +1811,11 @@
}
}
- static long fuzzForDuration(long duration) {
+ static int fuzzForDuration(long duration) {
if (duration < 15*60*1000) {
// If the duration until the time is less than 15 minutes, the maximum fuzz
// is the duration.
- return duration;
+ return (int)duration;
} else if (duration < 90*60*1000) {
// If duration is less than 1 1/2 hours, the maximum fuzz is 15 minutes,
return 15*60*1000;
@@ -1824,7 +1873,7 @@
mWakeLock.acquire();
}
final InFlight inflight = new InFlight(AlarmManagerService.this,
- alarm.operation, alarm.workSource, alarm.type, alarm.tag);
+ alarm.operation, alarm.workSource, alarm.type, alarm.tag, nowELAPSED);
mInFlight.add(inflight);
mBroadcastRefCount++;
diff --git a/services/core/java/com/android/server/AssetAtlasService.java b/services/core/java/com/android/server/AssetAtlasService.java
index 66cc29a..9e28b64 100644
--- a/services/core/java/com/android/server/AssetAtlasService.java
+++ b/services/core/java/com/android/server/AssetAtlasService.java
@@ -199,9 +199,6 @@
private final ArrayList<Bitmap> mBitmaps;
private final int mPixelCount;
- private long mNativeBitmap;
-
- // Used for debugging only
private Bitmap mAtlasBitmap;
Renderer(ArrayList<Bitmap> bitmaps, int pixelCount) {
@@ -299,9 +296,7 @@
}
final long endRender = System.nanoTime();
- if (mNativeBitmap != 0) {
- result = nUploadAtlas(buffer, mNativeBitmap);
- }
+ result = nUploadAtlas(buffer, mAtlasBitmap);
final long endUpload = System.nanoTime();
if (DEBUG_ATLAS) {
@@ -326,14 +321,8 @@
* @param height
*/
private Canvas acquireCanvas(int width, int height) {
- if (DEBUG_ATLAS_TEXTURE) {
- mAtlasBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- return new Canvas(mAtlasBitmap);
- } else {
- Canvas canvas = new Canvas();
- mNativeBitmap = nAcquireAtlasCanvas(canvas, width, height);
- return canvas;
- }
+ mAtlasBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ return new Canvas(mAtlasBitmap);
}
/**
@@ -343,8 +332,8 @@
* to disk in /data/system/atlas.png for debugging.
*/
private void releaseCanvas(Canvas canvas) {
+ canvas.setBitmap(null);
if (DEBUG_ATLAS_TEXTURE) {
- canvas.setBitmap(null);
File systemDirectory = new File(Environment.getDataDirectory(), "system");
File dataFile = new File(systemDirectory, "atlas.png");
@@ -358,18 +347,13 @@
} catch (IOException e) {
// Ignore
}
-
- mAtlasBitmap.recycle();
- mAtlasBitmap = null;
- } else {
- nReleaseAtlasCanvas(canvas, mNativeBitmap);
}
+ mAtlasBitmap.recycle();
+ mAtlasBitmap = null;
}
}
- private static native long nAcquireAtlasCanvas(Canvas canvas, int width, int height);
- private static native void nReleaseAtlasCanvas(Canvas canvas, long bitmap);
- private static native boolean nUploadAtlas(GraphicBuffer buffer, long bitmap);
+ private static native boolean nUploadAtlas(GraphicBuffer buffer, Bitmap bitmap);
@Override
public boolean isCompatible(int ppid) {
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index a227341..abd2ca0 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -173,6 +173,13 @@
# ---------------------------
33000 wp_wallpaper_crashed (component|3)
+# ---------------------------
+# Device idle
+# ---------------------------
+34000 device_idle (state|1|5), (reason|3)
+34001 device_idle_step
+34002 device_idle_wake_from_idle (is_idle|1|5), (reason|3)
+
# ---------------------------
# ConnectivityService.java
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index ac488e3..28597c1 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -111,6 +111,7 @@
static native int nativeOpenHal();
static native int nativeCloseHal();
static native void nativeInit(MessageQueue queue, FingerprintService service);
+ static native long nativeGetAuthenticatorId();
static final class FpHalMsg {
int type; // Type of the message. One of the constants in fingerprint.h
@@ -595,6 +596,12 @@
checkPermission(USE_FINGERPRINT);
return FingerprintService.this.hasEnrolledFingerprints(groupId);
}
+
+ @Override
+ public long getAuthenticatorId() {
+ checkPermission(USE_FINGERPRINT);
+ return nativeGetAuthenticatorId();
+ }
}
@Override
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index a72c77e..d3240ec 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -550,14 +550,19 @@
}
}
- try {
- // Convert properties to string contents and send it to HAL.
- ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
- properties.store(baos, null);
- native_configuration_update(baos.toString());
- Log.d(TAG, "final config = " + baos.toString());
- } catch (IOException ex) {
- Log.w(TAG, "failed to dump properties contents");
+ if (native_is_gnss_configuration_supported()) {
+ try {
+ // Convert properties to string contents and send it to HAL.
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
+ properties.store(baos, null);
+ native_configuration_update(baos.toString());
+ Log.d(TAG, "final config = " + baos.toString());
+ } catch (IOException ex) {
+ Log.w(TAG, "failed to dump properties contents");
+ }
+ } else if (DEBUG) {
+ Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not"
+ + " supported");
}
// SUPL_ES configuration.
@@ -732,16 +737,21 @@
}
if (info != null) {
- boolean dataEnabled = TelephonyManager.getDefault().getDataEnabled();
- boolean networkAvailable = info.isAvailable() && dataEnabled;
- String defaultApn = getSelectedApn();
- if (defaultApn == null) {
- defaultApn = "dummy-apn";
- }
+ if (native_is_agps_ril_supported()) {
+ boolean dataEnabled = TelephonyManager.getDefault().getDataEnabled();
+ boolean networkAvailable = info.isAvailable() && dataEnabled;
+ String defaultApn = getSelectedApn();
+ if (defaultApn == null) {
+ defaultApn = "dummy-apn";
+ }
- native_update_network_state(info.isConnected(), info.getType(),
- info.isRoaming(), networkAvailable,
- info.getExtraInfo(), defaultApn);
+ native_update_network_state(info.isConnected(), info.getType(),
+ info.isRoaming(), networkAvailable,
+ info.getExtraInfo(), defaultApn);
+ } else if (DEBUG) {
+ Log.d(TAG, "Skipped network state update because AGPS-RIL in GPS HAL is not"
+ + " supported");
+ }
}
if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
@@ -1752,7 +1762,7 @@
// NI Client support
//=============================================================
private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
- // Sends a response for an NI reqeust to HAL.
+ // Sends a response for an NI request to HAL.
@Override
public boolean sendNiResponse(int notificationId, int userResponse)
{
@@ -1843,7 +1853,7 @@
private void requestSetID(int flags) {
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
- int type = AGPS_SETID_TYPE_NONE;
+ int type = AGPS_SETID_TYPE_NONE;
String data = "";
if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
@@ -1994,20 +2004,26 @@
.addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
// listen for events
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
- intentFilter.addDataScheme("sms");
- intentFilter.addDataAuthority("localhost","7275");
- mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+ IntentFilter intentFilter;
+ if (native_is_agps_ril_supported()) {
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
+ intentFilter.addDataScheme("sms");
+ intentFilter.addDataAuthority("localhost", "7275");
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
- intentFilter = new IntentFilter();
- intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
- try {
- intentFilter.addDataType("application/vnd.omaloc-supl-init");
- } catch (IntentFilter.MalformedMimeTypeException e) {
- Log.w(TAG, "Malformed SUPL init mime type");
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
+ try {
+ intentFilter.addDataType("application/vnd.omaloc-supl-init");
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ Log.w(TAG, "Malformed SUPL init mime type");
+ }
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+ } else if (DEBUG) {
+ Log.d(TAG, "Skipped registration for SMS/WAP-PUSH messages because AGPS Ril in GPS"
+ + " HAL is not supported");
}
- mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
intentFilter = new IntentFilter();
intentFilter.addAction(ALARM_WAKEUP);
@@ -2187,6 +2203,8 @@
static { class_init_native(); }
private static native void class_init_native();
private static native boolean native_is_supported();
+ private static native boolean native_is_agps_ril_supported();
+ private static native boolean native_is_gnss_configuration_supported();
private native boolean native_init();
private native void native_cleanup();
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f3fdb0d..b3aa966 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4269,9 +4269,11 @@
serializer.endTag(null, TAG_RUNTIME_PERMISSIONS);
serializer.endDocument();
destination.finishWrite(out);
- } catch (IOException e) {
+
+ // Any error while writing is fatal.
+ } catch (Throwable t) {
Slog.wtf(PackageManagerService.TAG,
- "Failed to write settings, restoring backup", e);
+ "Failed to write settings, restoring backup", t);
destination.failWrite(out);
} finally {
IoUtils.closeQuietly(out);
@@ -4319,9 +4321,11 @@
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, null);
parseRuntimePermissionsLPr(parser, userId);
- } catch (XmlPullParserException | IOException ise) {
+
+ // Any error while parsing is fatal.
+ } catch (Throwable t) {
throw new IllegalStateException("Failed parsing permissions file: "
- + permissionsFile , ise);
+ + permissionsFile , t);
} finally {
IoUtils.closeQuietly(in);
}
diff --git a/services/core/java/com/android/server/power/DeviceIdleController.java b/services/core/java/com/android/server/power/DeviceIdleController.java
index a23a87b..6b29b9a 100644
--- a/services/core/java/com/android/server/power/DeviceIdleController.java
+++ b/services/core/java/com/android/server/power/DeviceIdleController.java
@@ -41,6 +41,7 @@
import com.android.internal.app.IBatteryStats;
import com.android.server.SystemService;
import com.android.server.am.BatteryStatsService;
+import com.android.server.EventLogTags;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -100,6 +101,11 @@
* Scaling factor to apply to current idle timeout each time we cycle through that state.
*/
private static final float DEFAULT_IDLE_FACTOR = 2f;
+ /**
+ * This is the minimum time we will allow until the next upcoming alarm for us to
+ * actually go in to idle mode.
+ */
+ private static final long DEFAULT_MIN_TIME_TO_ALARM = 60*60*1000L;
private AlarmManager mAlarmManager;
private IBatteryStats mBatteryStats;
@@ -239,7 +245,7 @@
becomeInactiveIfAppropriateLocked();
} else if (screenOn) {
mScreenOn = true;
- becomeActiveLocked();
+ becomeActiveLocked("screen");
}
}
@@ -249,12 +255,13 @@
becomeInactiveIfAppropriateLocked();
} else if (charging) {
mCharging = charging;
- becomeActiveLocked();
+ becomeActiveLocked("charging");
}
}
- void becomeActiveLocked() {
+ void becomeActiveLocked(String reason) {
if (mState != STATE_ACTIVE) {
+ EventLogTags.writeDeviceIdle(STATE_ACTIVE, reason);
mLocalPowerManager.setDeviceIdleMode(false);
try {
mNetworkPolicyManager.setDeviceIdleMode(false);
@@ -281,10 +288,22 @@
mNextIdlePendingDelay = 0;
mNextIdleDelay = 0;
scheduleAlarmLocked(mInactiveTimeout, false);
+ EventLogTags.writeDeviceIdle(mState, "no activity");
}
}
void stepIdleStateLocked() {
+ EventLogTags.writeDeviceIdleStep();
+
+ final long now = SystemClock.elapsedRealtime();
+ if ((now+DEFAULT_MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) {
+ // Whoops, there is an upcoming alarm. We don't actually want to go idle.
+ if (mState != STATE_ACTIVE) {
+ becomeActiveLocked("alarm");
+ }
+ return;
+ }
+
switch (mState) {
case STATE_INACTIVE:
// We have now been inactive long enough, it is time to start looking
@@ -295,6 +314,7 @@
mNextIdlePendingDelay = DEFAULT_IDLE_PENDING_TIMEOUT;
mNextIdleDelay = DEFAULT_IDLE_TIMEOUT;
mState = STATE_IDLE_PENDING;
+ EventLogTags.writeDeviceIdle(mState, "step");
break;
case STATE_IDLE_PENDING:
// We have been waiting to become idle, and now it is time! This is the
@@ -307,6 +327,7 @@
mNextIdleDelay = DEFAULT_MAX_IDLE_TIMEOUT;
}
mState = STATE_IDLE;
+ EventLogTags.writeDeviceIdle(mState, "step");
mLocalPowerManager.setDeviceIdleMode(true);
try {
mNetworkPolicyManager.setDeviceIdleMode(true);
@@ -323,6 +344,7 @@
mNextIdlePendingDelay = DEFAULT_MAX_IDLE_PENDING_TIMEOUT;
}
mState = STATE_IDLE_PENDING;
+ EventLogTags.writeDeviceIdle(mState, "step");
mLocalPowerManager.setDeviceIdleMode(false);
try {
mNetworkPolicyManager.setDeviceIdleMode(false);
@@ -352,6 +374,7 @@
}
mState = STATE_ACTIVE;
mInactiveTimeout = DEFAULT_MOTION_INACTIVE_TIMEOUT;
+ EventLogTags.writeDeviceIdle(mState, "motion");
becomeInactiveIfAppropriateLocked();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4f795a6..16d27fa 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -523,8 +523,9 @@
public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf, Rect sf) {
mHaveFrame = true;
- TaskStack stack = mAppToken != null ? getStack() : null;
- if (stack != null && !stack.isFullscreen()) {
+ final TaskStack stack = mAppToken != null ? getStack() : null;
+ final boolean nonFullscreenStack = stack != null && !stack.isFullscreen();
+ if (nonFullscreenStack) {
stack.getBounds(mContainingFrame);
final WindowState imeWin = mService.mInputMethodWindow;
if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this
@@ -607,9 +608,13 @@
y = mAttrs.y;
}
- // Make sure window fits in containing frame required by {@link Gravity#apply} call.
- w = Math.min(w, pw);
- h = Math.min(h, ph);
+ if (nonFullscreenStack) {
+ // Make sure window fits in containing frame since it is in a non-fullscreen stack as
+ // required by {@link Gravity#apply} call.
+ w = Math.min(w, pw);
+ h = Math.min(h, ph);
+ }
+
Gravity.apply(mAttrs.gravity, w, h, mContainingFrame,
(int) (x + mAttrs.horizontalMargin * pw),
(int) (y + mAttrs.verticalMargin * ph), mFrame);
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 19ca2b4..a5546cf 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -33,6 +33,7 @@
$(JNI_H_INCLUDE) \
frameworks/base/services \
frameworks/base/libs \
+ frameworks/base/libs/hwui \
frameworks/base/core/jni \
frameworks/native/services \
libcore/include \
diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp
index e4f242e..ad1d0f5 100644
--- a/services/core/jni/com_android_server_AssetAtlasService.cpp
+++ b/services/core/jni/com_android_server_AssetAtlasService.cpp
@@ -47,40 +47,9 @@
#define FENCE_TIMEOUT 2000000000
// ----------------------------------------------------------------------------
-// JNI Helpers
-// ----------------------------------------------------------------------------
-
-static struct {
- jmethodID setNativeBitmap;
-} gCanvasClassInfo;
-
-#define INVOKEV(object, method, ...) \
- env->CallVoidMethod(object, method, __VA_ARGS__)
-
-// ----------------------------------------------------------------------------
// Canvas management
// ----------------------------------------------------------------------------
-static jlong com_android_server_AssetAtlasService_acquireCanvas(JNIEnv* env, jobject,
- jobject canvas, jint width, jint height) {
-
- SkBitmap* bitmap = new SkBitmap;
- bitmap->allocN32Pixels(width, height);
- bitmap->eraseColor(0);
- INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(bitmap));
-
- return reinterpret_cast<jlong>(bitmap);
-}
-
-static void com_android_server_AssetAtlasService_releaseCanvas(JNIEnv* env, jobject,
- jobject canvas, jlong bitmapHandle) {
-
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0);
-
- delete bitmap;
-}
-
#define CLEANUP_GL_AND_RETURN(result) \
if (fence != EGL_NO_SYNC_KHR) eglDestroySyncKHR(display, fence); \
if (image) eglDestroyImageKHR(display, image); \
@@ -93,9 +62,11 @@
return result;
static jboolean com_android_server_AssetAtlasService_upload(JNIEnv* env, jobject,
- jobject graphicBuffer, jlong bitmapHandle) {
+ jobject graphicBuffer, jobject bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ SkBitmap& bitmap = *GraphicsJNI::getSkBitmap(env, bitmapHandle);
+ SkAutoLockPixels alp(bitmap);
+
// The goal of this method is to copy the bitmap into the GraphicBuffer
// using the GPU to swizzle the texture content
sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer));
@@ -186,9 +157,9 @@
}
// Upload the content of the bitmap in the GraphicBuffer
- glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap->width(), bitmap->height(),
- GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
+ glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap.bytesPerPixel());
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(),
+ GL_RGBA, GL_UNSIGNED_BYTE, bitmap.getPixels());
if (glGetError() != GL_NO_ERROR) {
ALOGW("Could not upload to texture");
CLEANUP_GL_AND_RETURN(JNI_FALSE);
@@ -233,20 +204,11 @@
const char* const kClassPathName = "com/android/server/AssetAtlasService";
static JNINativeMethod gMethods[] = {
- { "nAcquireAtlasCanvas", "(Landroid/graphics/Canvas;II)J",
- (void*) com_android_server_AssetAtlasService_acquireCanvas },
- { "nReleaseAtlasCanvas", "(Landroid/graphics/Canvas;J)V",
- (void*) com_android_server_AssetAtlasService_releaseCanvas },
- { "nUploadAtlas", "(Landroid/view/GraphicBuffer;J)Z",
+ { "nUploadAtlas", "(Landroid/view/GraphicBuffer;Landroid/graphics/Bitmap;)Z",
(void*) com_android_server_AssetAtlasService_upload },
};
int register_android_server_AssetAtlasService(JNIEnv* env) {
- jclass clazz;
-
- FIND_CLASS(clazz, "android/graphics/Canvas");
- GET_METHOD_ID(gCanvasClassInfo.setNativeBitmap, clazz, "setNativeBitmap", "(J)V");
-
return jniRegisterNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp
index 5a86923..7dbfaf6 100644
--- a/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp
+++ b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp
@@ -177,6 +177,10 @@
return reinterpret_cast<jint>(ret);
}
+static jlong nativeGetAuthenticatorId(JNIEnv *, jobject clazz) {
+ return gContext.device->get_authenticator_id(gContext.device);
+}
+
static jint nativeOpenHal(JNIEnv* env, jobject clazz) {
ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n");
int err;
@@ -229,6 +233,7 @@
return -ENOSYS; // TODO
}
+
// ----------------------------------------------------------------------------
@@ -240,6 +245,7 @@
{ "nativePreEnroll", "()J", (void*)nativePreEnroll },
{ "nativeStopEnrollment", "()I", (void*)nativeStopEnrollment },
{ "nativeRemove", "(II)I", (void*)nativeRemove },
+ { "nativeGetAuthenticatorId", "()J", (void*)nativeGetAuthenticatorId },
{ "nativeOpenHal", "()I", (void*)nativeOpenHal },
{ "nativeCloseHal", "()I", (void*)nativeCloseHal },
{ "nativeInit","(Landroid/os/MessageQueue;"
diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
index 0cd6eb5..3804e1d 100644
--- a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -509,13 +509,22 @@
}
}
-static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* /* env */,
- jclass /* clazz */) {
- if (sGpsInterface != NULL) {
- return JNI_TRUE;
- } else {
- return JNI_FALSE;
- }
+static jboolean android_location_GpsLocationProvider_is_supported(
+ JNIEnv* /* env */, jclass /* clazz */)
+{
+ return (sGpsInterface != NULL) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_is_agps_ril_supported(
+ JNIEnv* /* env */, jclass /* clazz */)
+{
+ return (sAGpsRilInterface != NULL) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean android_location_gpsLocationProvider_is_gnss_configuration_supported(
+ JNIEnv* /* env */, jclass /* jclazz */)
+{
+ return (sGnssConfigurationInterface != NULL) ? JNI_TRUE : JNI_FALSE;
}
static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj)
@@ -715,14 +724,10 @@
sGpsInterface->inject_location(latitude, longitude, accuracy);
}
-static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* /* env */,
- jobject /* obj */)
+static jboolean android_location_GpsLocationProvider_supports_xtra(
+ JNIEnv* /* env */, jobject /* obj */)
{
- if (sGpsXtraInterface != NULL) {
- return JNI_TRUE;
- } else {
- return JNI_FALSE;
- }
+ return (sGpsXtraInterface != NULL) ? JNI_TRUE : JNI_FALSE;
}
static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject /* obj */,
@@ -844,13 +849,10 @@
}
}
-static jboolean android_location_GpsLocationProvider_is_geofence_supported(JNIEnv* /* env */,
- jobject /* obj */)
+static jboolean android_location_GpsLocationProvider_is_geofence_supported(
+ JNIEnv* /* env */, jobject /* obj */)
{
- if (sGpsGeofencingInterface != NULL) {
- return JNI_TRUE;
- }
- return JNI_FALSE;
+ return (sGpsGeofencingInterface != NULL) ? JNI_TRUE : JNI_FALSE;
}
static jboolean android_location_GpsLocationProvider_add_geofence(JNIEnv* /* env */,
@@ -1436,6 +1438,10 @@
/* name, signature, funcPtr */
{"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
{"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported},
+ {"native_is_agps_ril_supported", "()Z",
+ (void*)android_location_GpsLocationProvider_is_agps_ril_supported},
+ {"native_is_gnss_configuration_supported", "()Z",
+ (void*)android_location_gpsLocationProvider_is_gnss_configuration_supported},
{"native_init", "()Z", (void*)android_location_GpsLocationProvider_init},
{"native_cleanup", "()V", (void*)android_location_GpsLocationProvider_cleanup},
{"native_set_position_mode",
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 17db3fb..7b58755 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -470,8 +470,6 @@
/**
* Get signal level as an int from 0..4
- *
- * @hide
*/
public int getLevel() {
int level;
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index e7cde74..3dae917 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -54,7 +54,7 @@
mWantUTF16(false), mValues(false), mIncludeMetaData(false),
mCompressionMethod(0), mJunkPath(false), mOutputAPKFile(NULL),
mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL),
- mAutoAddOverlay(false), mGenDependencies(false),
+ mAutoAddOverlay(false), mGenDependencies(false), mNoVersionVectors(false),
mCrunchedOutputDir(NULL), mProguardFile(NULL),
mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
@@ -203,6 +203,8 @@
void setSingleCrunchOutputFile(const char* val) { mSingleCrunchOutputFile = val; }
bool getBuildSharedLibrary() const { return mBuildSharedLibrary; }
void setBuildSharedLibrary(bool val) { mBuildSharedLibrary = val; }
+ void setNoVersionVectors(bool val) { mNoVersionVectors = val; }
+ bool getNoVersionVectors() const { return mNoVersionVectors; }
/*
* Set and get the file specification.
@@ -282,6 +284,7 @@
const char* mInstrumentationPackageNameOverride;
bool mAutoAddOverlay;
bool mGenDependencies;
+ bool mNoVersionVectors;
const char* mCrunchedOutputDir;
const char* mProguardFile;
const char* mAndroidManifestFile;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 8b416aa..7dee585 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -211,7 +211,9 @@
" specified folder.\n"
" --ignore-assets\n"
" Assets to be ignored. Default pattern is:\n"
- " %s\n",
+ " %s\n"
+ " --no-version-vectors\n"
+ " Do not automatically generate versioned copies of vector XML resources.\n",
gDefaultIgnoreAssets);
}
@@ -673,6 +675,8 @@
gUserIgnoreAssets = argv[0];
} else if (strcmp(cp, "-pseudo-localize") == 0) {
bundle.setPseudolocalize(PSEUDO_ACCENTED | PSEUDO_BIDI);
+ } else if (strcmp(cp, "-no-version-vectors") == 0) {
+ bundle.setNoVersionVectors(true);
} else {
fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
wantUsage = true;
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index c5fccbf..3b146da 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -4611,6 +4611,9 @@
const String16& resourceName,
const sp<AaptFile>& target,
const sp<XMLNode>& root) {
+ const String16 vector16("vector");
+ const String16 animatedVector16("animated-vector");
+
const int minSdk = getMinSdkVersion(bundle);
if (minSdk >= SDK_LOLLIPOP_MR1) {
// Lollipop MR1 and up handles public attributes differently, no
@@ -4620,8 +4623,8 @@
const ConfigDescription config(target->getGroupEntry().toParams());
if (target->getResourceType() == "" || config.sdkVersion >= SDK_LOLLIPOP_MR1) {
- // Skip resources that have no type (AndroidManifest.xml) or are already version qualified with v21
- // or higher.
+ // Skip resources that have no type (AndroidManifest.xml) or are already version qualified
+ // with v21 or higher.
return NO_ERROR;
}
@@ -4635,6 +4638,12 @@
sp<XMLNode> node = nodesToVisit.top();
nodesToVisit.pop();
+ if (bundle->getNoVersionVectors() && (node->getElementName() == vector16 ||
+ node->getElementName() == animatedVector16)) {
+ // We were told not to version vector tags, so skip the children here.
+ continue;
+ }
+
const Vector<XMLNode::attribute_entry>& attrs = node->getAttributes();
for (size_t i = 0; i < attrs.size(); i++) {
const XMLNode::attribute_entry& attr = attrs[i];