summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt20
-rw-r--r--api/system-current.txt20
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java16
-rw-r--r--core/java/android/app/ActivityManagerNative.java20
-rw-r--r--core/java/android/app/IActivityContainer.aidl1
-rw-r--r--core/java/android/app/IActivityManager.java2
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java41
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl40
-rw-r--r--core/java/android/bluetooth/BluetoothA2dp.java12
-rw-r--r--core/java/android/midi/MidiDevice.java1
-rw-r--r--core/java/android/midi/MidiDeviceInfo.java3
-rw-r--r--core/java/android/midi/MidiDeviceServer.java9
-rw-r--r--core/java/android/midi/MidiInputPort.java1
-rw-r--r--core/java/android/midi/MidiManager.java3
-rw-r--r--core/java/android/midi/MidiOutputPort.java6
-rw-r--r--core/java/android/midi/MidiPort.java1
-rw-r--r--core/java/android/midi/MidiReceiver.java11
-rw-r--r--core/java/android/midi/MidiSender.java1
-rw-r--r--core/java/android/net/StaticIpConfiguration.java2
-rw-r--r--core/java/android/provider/ContactsContract.java17
-rw-r--r--core/java/android/view/View.java7
-rw-r--r--core/java/android/view/ViewRootImpl.java8
-rw-r--r--core/java/android/view/WindowManager.java22
-rw-r--r--core/java/android/widget/PopupWindow.java510
-rw-r--r--core/java/android/widget/Switch.java172
-rw-r--r--core/java/com/android/internal/transition/EpicenterClipReveal.java115
-rw-r--r--core/res/res/transition/popup_window_enter.xml25
-rw-r--r--core/res/res/transition/popup_window_exit.xml17
-rw-r--r--core/res/res/values/attrs.xml30
-rw-r--r--core/res/res/values/public.xml3
-rw-r--r--core/res/res/values/styles_material.xml13
-rw-r--r--core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java10
-rw-r--r--graphics/java/android/graphics/Picture.java5
-rw-r--r--libs/hwui/Android.common.mk3
-rw-r--r--libs/hwui/Caches.cpp86
-rw-r--r--libs/hwui/Caches.h72
-rw-r--r--libs/hwui/Canvas.h16
-rw-r--r--libs/hwui/DisplayListRenderer.cpp10
-rw-r--r--libs/hwui/DisplayListRenderer.h8
-rw-r--r--libs/hwui/Dither.cpp15
-rw-r--r--libs/hwui/Dither.h4
-rw-r--r--libs/hwui/FontRenderer.cpp16
-rw-r--r--libs/hwui/GradientCache.cpp2
-rw-r--r--libs/hwui/Image.cpp4
-rw-r--r--libs/hwui/Layer.cpp6
-rw-r--r--libs/hwui/LayerRenderer.cpp10
-rw-r--r--libs/hwui/OpenGLRenderer.cpp122
-rwxr-xr-xlibs/hwui/OpenGLRenderer.h22
-rw-r--r--libs/hwui/PathCache.cpp4
-rw-r--r--libs/hwui/PixelBuffer.cpp10
-rw-r--r--libs/hwui/SkiaCanvasProxy.cpp347
-rw-r--r--libs/hwui/SkiaCanvasProxy.h104
-rw-r--r--libs/hwui/SkiaShader.cpp8
-rw-r--r--libs/hwui/TextDropShadowCache.cpp2
-rw-r--r--libs/hwui/Texture.cpp52
-rw-r--r--libs/hwui/TextureCache.cpp2
-rw-r--r--libs/hwui/font/CacheTexture.cpp6
-rw-r--r--libs/hwui/renderstate/Blend.cpp121
-rw-r--r--libs/hwui/renderstate/Blend.h46
-rw-r--r--libs/hwui/renderstate/RenderState.cpp29
-rw-r--r--libs/hwui/renderstate/RenderState.h12
-rw-r--r--libs/hwui/renderstate/TextureState.cpp103
-rw-r--r--libs/hwui/renderstate/TextureState.h88
-rw-r--r--libs/hwui/renderthread/EglManager.cpp3
-rw-r--r--location/java/android/location/Location.java7
-rw-r--r--media/java/android/media/AudioManager.java166
-rw-r--r--media/java/android/media/AudioManagerInternal.java3
-rw-r--r--media/java/android/media/AudioService.java297
-rw-r--r--media/java/android/media/IAudioService.aidl6
-rw-r--r--media/java/android/media/session/MediaSessionLegacyHelper.java6
-rw-r--r--media/java/android/media/session/MediaSessionManager.java8
-rw-r--r--packages/SystemUI/res/layout/recents.xml13
-rw-r--r--packages/SystemUI/res/layout/recents_multistack_debug.xml46
-rw-r--r--packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml70
-rw-r--r--packages/SystemUI/res/layout/recents_task_view_header.xml10
-rw-r--r--packages/SystemUI/res/values/strings.xml13
-rw-r--r--packages/SystemUI/res/values/styles.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Constants.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Recents.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsMultiStackDialog.java339
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java101
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java179
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/Task.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java291
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java54
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java36
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java7
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java15
-rw-r--r--services/core/java/com/android/server/am/EventLogTags.logtags5
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java4
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java64
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java24
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java98
-rw-r--r--telephony/java/android/telephony/PhoneNumberUtils.java8
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java4
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java477
111 files changed, 3858 insertions, 1421 deletions
diff --git a/api/current.txt b/api/current.txt
index dd2549d..74a97b6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1316,6 +1316,8 @@ package android {
field public static final int topRightRadius = 16843178; // 0x10101aa
field public static final int touchscreenBlocksFocus = 16843919; // 0x101048f
field public static final int track = 16843631; // 0x101036f
+ field public static final int trackTint = 16843993; // 0x10104d9
+ field public static final int trackTintMode = 16843994; // 0x10104da
field public static final int transcriptMode = 16843008; // 0x1010100
field public static final int transformPivotX = 16843552; // 0x1010320
field public static final int transformPivotY = 16843553; // 0x1010321
@@ -14166,6 +14168,7 @@ package android.media {
method public boolean isMicrophoneMute();
method public boolean isMusicActive();
method public boolean isSpeakerphoneOn();
+ method public boolean isStreamMute(int);
method public boolean isVolumeFixed();
method public deprecated boolean isWiredHeadsetOn();
method public void loadSoundEffects();
@@ -14184,8 +14187,8 @@ package android.media {
method public void setRingerMode(int);
method public deprecated void setRouting(int, int, int);
method public void setSpeakerphoneOn(boolean);
- method public void setStreamMute(int, boolean);
- method public void setStreamSolo(int, boolean);
+ method public deprecated void setStreamMute(int, boolean);
+ method public deprecated void setStreamSolo(int, boolean);
method public void setStreamVolume(int, int, int);
method public deprecated void setVibrateSetting(int, int);
method public deprecated void setWiredHeadsetOn(boolean);
@@ -14203,8 +14206,11 @@ package android.media {
field public static final deprecated java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
field public static final java.lang.String ACTION_SCO_AUDIO_STATE_UPDATED = "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
field public static final int ADJUST_LOWER = -1; // 0xffffffff
+ field public static final int ADJUST_MUTE = -100; // 0xffffff9c
field public static final int ADJUST_RAISE = 1; // 0x1
field public static final int ADJUST_SAME = 0; // 0x0
+ field public static final int ADJUST_TOGGLE_MUTE = 101; // 0x65
+ field public static final int ADJUST_UNMUTE = 100; // 0x64
field public static final int AUDIOFOCUS_GAIN = 1; // 0x1
field public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2; // 0x2
field public static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE = 4; // 0x4
@@ -38453,6 +38459,8 @@ package android.widget {
method public void setClippingEnabled(boolean);
method public void setContentView(android.view.View);
method public void setElevation(float);
+ method public void setEnterTransition(android.transition.Transition);
+ method public void setExitTransition(android.transition.Transition);
method public void setFocusable(boolean);
method public void setHeight(int);
method public void setIgnoreCheekPress();
@@ -39054,7 +39062,11 @@ package android.widget {
method public java.lang.CharSequence getTextOn();
method public android.graphics.drawable.Drawable getThumbDrawable();
method public int getThumbTextPadding();
+ method public android.content.res.ColorStateList getThumbTintList();
+ method public android.graphics.PorterDuff.Mode getThumbTintMode();
method public android.graphics.drawable.Drawable getTrackDrawable();
+ method public android.content.res.ColorStateList getTrackTintList();
+ method public android.graphics.PorterDuff.Mode getTrackTintMode();
method public void onMeasure(int, int);
method public void setShowText(boolean);
method public void setSplitTrack(boolean);
@@ -39068,8 +39080,12 @@ package android.widget {
method public void setThumbDrawable(android.graphics.drawable.Drawable);
method public void setThumbResource(int);
method public void setThumbTextPadding(int);
+ method public void setThumbTintList(android.content.res.ColorStateList);
+ method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
method public void setTrackDrawable(android.graphics.drawable.Drawable);
method public void setTrackResource(int);
+ method public void setTrackTintList(android.content.res.ColorStateList);
+ method public void setTrackTintMode(android.graphics.PorterDuff.Mode);
}
public class TabHost extends android.widget.FrameLayout implements android.view.ViewTreeObserver.OnTouchModeChangeListener {
diff --git a/api/system-current.txt b/api/system-current.txt
index 8d503d6..27b7a91 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1392,6 +1392,8 @@ package android {
field public static final int topRightRadius = 16843178; // 0x10101aa
field public static final int touchscreenBlocksFocus = 16843919; // 0x101048f
field public static final int track = 16843631; // 0x101036f
+ field public static final int trackTint = 16843993; // 0x10104d9
+ field public static final int trackTintMode = 16843994; // 0x10104da
field public static final int transcriptMode = 16843008; // 0x1010100
field public static final int transformPivotX = 16843552; // 0x1010320
field public static final int transformPivotY = 16843553; // 0x1010321
@@ -15144,6 +15146,7 @@ package android.media {
method public boolean isMicrophoneMute();
method public boolean isMusicActive();
method public boolean isSpeakerphoneOn();
+ method public boolean isStreamMute(int);
method public boolean isVolumeFixed();
method public deprecated boolean isWiredHeadsetOn();
method public void loadSoundEffects();
@@ -15165,8 +15168,8 @@ package android.media {
method public void setRingerMode(int);
method public deprecated void setRouting(int, int, int);
method public void setSpeakerphoneOn(boolean);
- method public void setStreamMute(int, boolean);
- method public void setStreamSolo(int, boolean);
+ method public deprecated void setStreamMute(int, boolean);
+ method public deprecated void setStreamSolo(int, boolean);
method public void setStreamVolume(int, int, int);
method public deprecated void setVibrateSetting(int, int);
method public deprecated void setWiredHeadsetOn(boolean);
@@ -15185,8 +15188,11 @@ package android.media {
field public static final deprecated java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
field public static final java.lang.String ACTION_SCO_AUDIO_STATE_UPDATED = "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
field public static final int ADJUST_LOWER = -1; // 0xffffffff
+ field public static final int ADJUST_MUTE = -100; // 0xffffff9c
field public static final int ADJUST_RAISE = 1; // 0x1
field public static final int ADJUST_SAME = 0; // 0x0
+ field public static final int ADJUST_TOGGLE_MUTE = 101; // 0x65
+ field public static final int ADJUST_UNMUTE = 100; // 0x64
field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
@@ -40948,6 +40954,8 @@ package android.widget {
method public void setClippingEnabled(boolean);
method public void setContentView(android.view.View);
method public void setElevation(float);
+ method public void setEnterTransition(android.transition.Transition);
+ method public void setExitTransition(android.transition.Transition);
method public void setFocusable(boolean);
method public void setHeight(int);
method public void setIgnoreCheekPress();
@@ -41549,7 +41557,11 @@ package android.widget {
method public java.lang.CharSequence getTextOn();
method public android.graphics.drawable.Drawable getThumbDrawable();
method public int getThumbTextPadding();
+ method public android.content.res.ColorStateList getThumbTintList();
+ method public android.graphics.PorterDuff.Mode getThumbTintMode();
method public android.graphics.drawable.Drawable getTrackDrawable();
+ method public android.content.res.ColorStateList getTrackTintList();
+ method public android.graphics.PorterDuff.Mode getTrackTintMode();
method public void onMeasure(int, int);
method public void setShowText(boolean);
method public void setSplitTrack(boolean);
@@ -41563,8 +41575,12 @@ package android.widget {
method public void setThumbDrawable(android.graphics.drawable.Drawable);
method public void setThumbResource(int);
method public void setThumbTextPadding(int);
+ method public void setThumbTintList(android.content.res.ColorStateList);
+ method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
method public void setTrackDrawable(android.graphics.drawable.Drawable);
method public void setTrackResource(int);
+ method public void setTrackTintList(android.content.res.ColorStateList);
+ method public void setTrackTintMode(android.graphics.PorterDuff.Mode);
}
public class TabHost extends android.widget.FrameLayout implements android.view.ViewTreeObserver.OnTouchModeChangeListener {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 2a0ed90..fb3d423 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1735,6 +1735,22 @@ public class Am extends BaseCommand {
int right = Integer.valueOf(rightStr);
String bottomStr = nextArgRequired();
int bottom = Integer.valueOf(bottomStr);
+ if (left < 0) {
+ System.err.println("Error: bad left arg: " + leftStr);
+ return;
+ }
+ if (top < 0) {
+ System.err.println("Error: bad top arg: " + topStr);
+ return;
+ }
+ if (right <= 0) {
+ System.err.println("Error: bad right arg: " + rightStr);
+ return;
+ }
+ if (bottom <= 0) {
+ System.err.println("Error: bad bottom arg: " + bottomStr);
+ return;
+ }
try {
mAm.resizeStack(stackId, new Rect(left, top, right, bottom));
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 379fe11..97b9f4c 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -766,6 +766,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case GET_FOCUSED_STACK_ID_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int focusedStackId = getFocusedStackId();
+ reply.writeNoException();
+ reply.writeInt(focusedStackId);
+ return true;
+ }
+
case REGISTER_TASK_STACK_LISTENER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
@@ -3290,6 +3298,18 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
@Override
+ public int getFocusedStackId() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_FOCUSED_STACK_ID_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int focusedStackId = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return focusedStackId;
+ }
+ @Override
public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException
{
Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/IActivityContainer.aidl b/core/java/android/app/IActivityContainer.aidl
index 52884f7..ff1175f 100644
--- a/core/java/android/app/IActivityContainer.aidl
+++ b/core/java/android/app/IActivityContainer.aidl
@@ -32,6 +32,7 @@ interface IActivityContainer {
void checkEmbeddedAllowed(in Intent intent);
void checkEmbeddedAllowedIntentSender(in IIntentSender intentSender);
int getDisplayId();
+ int getStackId();
boolean injectEvent(in InputEvent event);
void release();
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index f152c6f..efc4543 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -139,6 +139,7 @@ public interface IActivityManager extends IInterface {
public StackInfo getStackInfo(int stackId) throws RemoteException;
public boolean isInHomeStack(int taskId) throws RemoteException;
public void setFocusedStack(int stackId) throws RemoteException;
+ public int getFocusedStackId() throws RemoteException;
public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException;
public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException;
public ContentProviderHolder getContentProvider(IApplicationThread caller,
@@ -800,4 +801,5 @@ public interface IActivityManager extends IInterface {
// Start of M transactions
int NOTIFY_CLEARTEXT_NETWORK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+280;
int CREATE_STACK_ON_DISPLAY = IBinder.FIRST_CALL_TRANSACTION+281;
+ int GET_FOCUSED_STACK_ID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+282;
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 55c3960..bedec72 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -694,7 +694,7 @@ public class DevicePolicyManager {
public void setPasswordQuality(ComponentName admin, int quality) {
if (mService != null) {
try {
- mService.setPasswordQuality(admin, quality, UserHandle.myUserId());
+ mService.setPasswordQuality(admin, quality);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -747,7 +747,7 @@ public class DevicePolicyManager {
public void setPasswordMinimumLength(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordMinimumLength(admin, length, UserHandle.myUserId());
+ mService.setPasswordMinimumLength(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -801,7 +801,7 @@ public class DevicePolicyManager {
public void setPasswordMinimumUpperCase(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordMinimumUpperCase(admin, length, UserHandle.myUserId());
+ mService.setPasswordMinimumUpperCase(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -862,7 +862,7 @@ public class DevicePolicyManager {
public void setPasswordMinimumLowerCase(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordMinimumLowerCase(admin, length, UserHandle.myUserId());
+ mService.setPasswordMinimumLowerCase(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -922,7 +922,7 @@ public class DevicePolicyManager {
public void setPasswordMinimumLetters(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordMinimumLetters(admin, length, UserHandle.myUserId());
+ mService.setPasswordMinimumLetters(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -980,7 +980,7 @@ public class DevicePolicyManager {
public void setPasswordMinimumNumeric(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordMinimumNumeric(admin, length, UserHandle.myUserId());
+ mService.setPasswordMinimumNumeric(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1039,7 +1039,7 @@ public class DevicePolicyManager {
public void setPasswordMinimumSymbols(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordMinimumSymbols(admin, length, UserHandle.myUserId());
+ mService.setPasswordMinimumSymbols(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1097,7 +1097,7 @@ public class DevicePolicyManager {
public void setPasswordMinimumNonLetter(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordMinimumNonLetter(admin, length, UserHandle.myUserId());
+ mService.setPasswordMinimumNonLetter(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1157,7 +1157,7 @@ public class DevicePolicyManager {
public void setPasswordHistoryLength(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordHistoryLength(admin, length, UserHandle.myUserId());
+ mService.setPasswordHistoryLength(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1189,7 +1189,7 @@ public class DevicePolicyManager {
public void setPasswordExpirationTimeout(ComponentName admin, long timeout) {
if (mService != null) {
try {
- mService.setPasswordExpirationTimeout(admin, timeout, UserHandle.myUserId());
+ mService.setPasswordExpirationTimeout(admin, timeout);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1334,7 +1334,7 @@ public class DevicePolicyManager {
public void setMaximumFailedPasswordsForWipe(ComponentName admin, int num) {
if (mService != null) {
try {
- mService.setMaximumFailedPasswordsForWipe(admin, num, UserHandle.myUserId());
+ mService.setMaximumFailedPasswordsForWipe(admin, num);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1418,7 +1418,7 @@ public class DevicePolicyManager {
public boolean resetPassword(String password, int flags) {
if (mService != null) {
try {
- return mService.resetPassword(password, flags, UserHandle.myUserId());
+ return mService.resetPassword(password, flags);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1442,7 +1442,7 @@ public class DevicePolicyManager {
public void setMaximumTimeToLock(ComponentName admin, long timeMs) {
if (mService != null) {
try {
- mService.setMaximumTimeToLock(admin, timeMs, UserHandle.myUserId());
+ mService.setMaximumTimeToLock(admin, timeMs);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1592,7 +1592,7 @@ public class DevicePolicyManager {
!= android.net.Proxy.PROXY_VALID)
throw new IllegalArgumentException();
}
- return mService.setGlobalProxy(admin, hostSpec, exclSpec, UserHandle.myUserId());
+ return mService.setGlobalProxy(admin, hostSpec, exclSpec);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1758,7 +1758,7 @@ public class DevicePolicyManager {
public int setStorageEncryption(ComponentName admin, boolean encrypt) {
if (mService != null) {
try {
- return mService.setStorageEncryption(admin, encrypt, UserHandle.myUserId());
+ return mService.setStorageEncryption(admin, encrypt);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1977,7 +1977,7 @@ public class DevicePolicyManager {
public void setCameraDisabled(ComponentName admin, boolean disabled) {
if (mService != null) {
try {
- mService.setCameraDisabled(admin, disabled, UserHandle.myUserId());
+ mService.setCameraDisabled(admin, disabled);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2021,7 +2021,7 @@ public class DevicePolicyManager {
public void setScreenCaptureDisabled(ComponentName admin, boolean disabled) {
if (mService != null) {
try {
- mService.setScreenCaptureDisabled(admin, UserHandle.myUserId(), disabled);
+ mService.setScreenCaptureDisabled(admin, disabled);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2065,7 +2065,7 @@ public class DevicePolicyManager {
public void setAutoTimeRequired(ComponentName admin, boolean required) {
if (mService != null) {
try {
- mService.setAutoTimeRequired(admin, UserHandle.myUserId(), required);
+ mService.setAutoTimeRequired(admin, required);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2106,7 +2106,7 @@ public class DevicePolicyManager {
public void setKeyguardDisabledFeatures(ComponentName admin, int which) {
if (mService != null) {
try {
- mService.setKeyguardDisabledFeatures(admin, which, UserHandle.myUserId());
+ mService.setKeyguardDisabledFeatures(admin, which);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2688,8 +2688,7 @@ public class DevicePolicyManager {
PersistableBundle configuration) {
if (mService != null) {
try {
- mService.setTrustAgentConfiguration(admin, target, configuration,
- UserHandle.myUserId());
+ mService.setTrustAgentConfiguration(admin, target, configuration);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 0ca60c0..67bca4e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -32,34 +32,34 @@ import java.util.List;
* {@hide}
*/
interface IDevicePolicyManager {
- void setPasswordQuality(in ComponentName who, int quality, int userHandle);
+ void setPasswordQuality(in ComponentName who, int quality);
int getPasswordQuality(in ComponentName who, int userHandle);
- void setPasswordMinimumLength(in ComponentName who, int length, int userHandle);
+ void setPasswordMinimumLength(in ComponentName who, int length);
int getPasswordMinimumLength(in ComponentName who, int userHandle);
- void setPasswordMinimumUpperCase(in ComponentName who, int length, int userHandle);
+ void setPasswordMinimumUpperCase(in ComponentName who, int length);
int getPasswordMinimumUpperCase(in ComponentName who, int userHandle);
- void setPasswordMinimumLowerCase(in ComponentName who, int length, int userHandle);
+ void setPasswordMinimumLowerCase(in ComponentName who, int length);
int getPasswordMinimumLowerCase(in ComponentName who, int userHandle);
- void setPasswordMinimumLetters(in ComponentName who, int length, int userHandle);
+ void setPasswordMinimumLetters(in ComponentName who, int length);
int getPasswordMinimumLetters(in ComponentName who, int userHandle);
- void setPasswordMinimumNumeric(in ComponentName who, int length, int userHandle);
+ void setPasswordMinimumNumeric(in ComponentName who, int length);
int getPasswordMinimumNumeric(in ComponentName who, int userHandle);
- void setPasswordMinimumSymbols(in ComponentName who, int length, int userHandle);
+ void setPasswordMinimumSymbols(in ComponentName who, int length);
int getPasswordMinimumSymbols(in ComponentName who, int userHandle);
- void setPasswordMinimumNonLetter(in ComponentName who, int length, int userHandle);
+ void setPasswordMinimumNonLetter(in ComponentName who, int length);
int getPasswordMinimumNonLetter(in ComponentName who, int userHandle);
- void setPasswordHistoryLength(in ComponentName who, int length, int userHandle);
+ void setPasswordHistoryLength(in ComponentName who, int length);
int getPasswordHistoryLength(in ComponentName who, int userHandle);
- void setPasswordExpirationTimeout(in ComponentName who, long expiration, int userHandle);
+ void setPasswordExpirationTimeout(in ComponentName who, long expiration);
long getPasswordExpirationTimeout(in ComponentName who, int userHandle);
long getPasswordExpiration(in ComponentName who, int userHandle);
@@ -68,33 +68,33 @@ interface IDevicePolicyManager {
int getCurrentFailedPasswordAttempts(int userHandle);
int getProfileWithMinimumFailedPasswordsForWipe(int userHandle);
- void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num, int userHandle);
+ void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num);
int getMaximumFailedPasswordsForWipe(in ComponentName admin, int userHandle);
- boolean resetPassword(String password, int flags, int userHandle);
+ boolean resetPassword(String password, int flags);
- void setMaximumTimeToLock(in ComponentName who, long timeMs, int userHandle);
+ void setMaximumTimeToLock(in ComponentName who, long timeMs);
long getMaximumTimeToLock(in ComponentName who, int userHandle);
void lockNow();
void wipeData(int flags, int userHandle);
- ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList, int userHandle);
+ ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList);
ComponentName getGlobalProxyAdmin(int userHandle);
void setRecommendedGlobalProxy(in ComponentName admin, in ProxyInfo proxyInfo);
- int setStorageEncryption(in ComponentName who, boolean encrypt, int userHandle);
+ int setStorageEncryption(in ComponentName who, boolean encrypt);
boolean getStorageEncryption(in ComponentName who, int userHandle);
int getStorageEncryptionStatus(int userHandle);
- void setCameraDisabled(in ComponentName who, boolean disabled, int userHandle);
+ void setCameraDisabled(in ComponentName who, boolean disabled);
boolean getCameraDisabled(in ComponentName who, int userHandle);
- void setScreenCaptureDisabled(in ComponentName who, int userHandle, boolean disabled);
+ void setScreenCaptureDisabled(in ComponentName who, boolean disabled);
boolean getScreenCaptureDisabled(in ComponentName who, int userHandle);
- void setKeyguardDisabledFeatures(in ComponentName who, int which, int userHandle);
+ void setKeyguardDisabledFeatures(in ComponentName who, int which);
int getKeyguardDisabledFeatures(in ComponentName who, int userHandle);
void setActiveAdmin(in ComponentName policyReceiver, boolean refreshing, int userHandle);
@@ -186,7 +186,7 @@ interface IDevicePolicyManager {
boolean getCrossProfileCallerIdDisabledForUser(int userId);
void setTrustAgentConfiguration(in ComponentName admin, in ComponentName agent,
- in PersistableBundle args, int userId);
+ in PersistableBundle args);
List<PersistableBundle> getTrustAgentConfiguration(in ComponentName admin,
in ComponentName agent, int userId);
@@ -194,7 +194,7 @@ interface IDevicePolicyManager {
boolean removeCrossProfileWidgetProvider(in ComponentName admin, String packageName);
List<String> getCrossProfileWidgetProviders(in ComponentName admin);
- void setAutoTimeRequired(in ComponentName who, int userHandle, boolean required);
+ void setAutoTimeRequired(in ComponentName who, boolean required);
boolean getAutoTimeRequired();
boolean isRemovingAdmin(in ComponentName adminReceiver, int userHandle);
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 0450150..767f59e 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -22,6 +22,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.media.AudioManager;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
@@ -415,9 +416,16 @@ public final class BluetoothA2dp implements BluetoothProfile {
}
/**
- * Tells remote device to adjust volume. Only if absolute volume is supported.
+ * Tells remote device to adjust volume. Only if absolute volume is
+ * supported. Uses the following values:
+ * <ul>
+ * <li>{@link AudioManager#ADJUST_LOWER}</li>
+ * <li>{@link AudioManager#ADJUST_RAISE}</li>
+ * <li>{@link AudioManager#ADJUST_MUTE}</li>
+ * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
+ * </ul>
*
- * @param direction 1 to increase volume, or -1 to decrease volume
+ * @param direction One of the supported adjust values.
* @hide
*/
public void adjustAvrcpAbsoluteVolume(int direction) {
diff --git a/core/java/android/midi/MidiDevice.java b/core/java/android/midi/MidiDevice.java
index 8aaa86d..b91aedf 100644
--- a/core/java/android/midi/MidiDevice.java
+++ b/core/java/android/midi/MidiDevice.java
@@ -30,6 +30,7 @@ import java.util.ArrayList;
* This class is used for sending and receiving data to and from an MIDI device
* Instances of this class are created by {@link MidiManager#openDevice}.
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
public final class MidiDevice {
diff --git a/core/java/android/midi/MidiDeviceInfo.java b/core/java/android/midi/MidiDeviceInfo.java
index 5cf62b5..dde2669 100644
--- a/core/java/android/midi/MidiDeviceInfo.java
+++ b/core/java/android/midi/MidiDeviceInfo.java
@@ -28,6 +28,7 @@ import android.os.Parcelable;
* This class is just an immutable object to encapsulate the MIDI device description.
* Use the MidiDevice class to actually communicate with devices.
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
public class MidiDeviceInfo implements Parcelable {
@@ -45,7 +46,7 @@ public class MidiDeviceInfo implements Parcelable {
public static final int TYPE_VIRTUAL = 2;
private final int mType; // USB or virtual
- private final int mId; // unique ID generated by MidiService
+ private final int mId; // unique ID generated by MidiService
private final int mInputPortCount;
private final int mOutputPortCount;
private final Bundle mProperties;
diff --git a/core/java/android/midi/MidiDeviceServer.java b/core/java/android/midi/MidiDeviceServer.java
index ccb2e0c..7499934 100644
--- a/core/java/android/midi/MidiDeviceServer.java
+++ b/core/java/android/midi/MidiDeviceServer.java
@@ -25,7 +25,14 @@ import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
-/** @hide */
+/**
+ * This class is used to provide the implemention of MIDI device.
+ * Applications may call {@link MidiManager#createDeviceServer}
+ * to create an instance of this class to implement a virtual MIDI device.
+ *
+ * CANDIDATE FOR PUBLIC API
+ * @hide
+ */
public final class MidiDeviceServer implements Closeable {
private static final String TAG = "MidiDeviceServer";
diff --git a/core/java/android/midi/MidiInputPort.java b/core/java/android/midi/MidiInputPort.java
index 88ace5f..51c47dd 100644
--- a/core/java/android/midi/MidiInputPort.java
+++ b/core/java/android/midi/MidiInputPort.java
@@ -26,6 +26,7 @@ import java.io.IOException;
/**
* This class is used for sending data to a port on a MIDI device
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
public class MidiInputPort extends MidiPort implements MidiReceiver {
diff --git a/core/java/android/midi/MidiManager.java b/core/java/android/midi/MidiManager.java
index 2c1c7bf..8aa8395 100644
--- a/core/java/android/midi/MidiManager.java
+++ b/core/java/android/midi/MidiManager.java
@@ -35,6 +35,7 @@ import java.util.HashMap;
* {@samplecode
* MidiManager manager = (MidiManager) getSystemService(Context.MIDI_SERVICE);}
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
public class MidiManager {
@@ -184,7 +185,7 @@ public class MidiManager {
* @param properties a {@link android.os.Bundle} containing properties describing the device
* @param isPrivate true if this device should only be visible and accessible to apps
* with the same UID as the caller
- * @return a {@link MidiVirtualDevice} object to locally represent the device
+ * @return a {@link MidiDeviceServer} object to locally represent the device
*/
public MidiDeviceServer createDeviceServer(int numInputPorts, int numOutputPorts,
Bundle properties, boolean isPrivate) {
diff --git a/core/java/android/midi/MidiOutputPort.java b/core/java/android/midi/MidiOutputPort.java
index 00b7bad..332b431 100644
--- a/core/java/android/midi/MidiOutputPort.java
+++ b/core/java/android/midi/MidiOutputPort.java
@@ -26,8 +26,9 @@ import java.io.IOException;
import java.util.ArrayList;
/**
- * This class is used for receiving data to a port on a MIDI device
+ * This class is used for receiving data from a port on a MIDI device
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
public class MidiOutputPort extends MidiPort implements MidiSender {
@@ -85,8 +86,9 @@ public class MidiOutputPort extends MidiPort implements MidiSender {
}
}
} catch (IOException e) {
- Log.e(TAG, "read failed");
// report I/O failure
+ Log.e(TAG, "read failed");
+ } finally {
IoUtils.closeQuietly(mInputStream);
onIOException();
}
diff --git a/core/java/android/midi/MidiPort.java b/core/java/android/midi/MidiPort.java
index 44d1a88..7512a90 100644
--- a/core/java/android/midi/MidiPort.java
+++ b/core/java/android/midi/MidiPort.java
@@ -24,6 +24,7 @@ import java.io.Closeable;
* This class represents a MIDI input or output port.
* Base class for {@link MidiInputPort} and {@link MidiOutputPort}
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
abstract public class MidiPort implements Closeable {
diff --git a/core/java/android/midi/MidiReceiver.java b/core/java/android/midi/MidiReceiver.java
index a4e1a10..fdfe51a 100644
--- a/core/java/android/midi/MidiReceiver.java
+++ b/core/java/android/midi/MidiReceiver.java
@@ -19,13 +19,14 @@ package android.midi;
import java.io.IOException;
/**
- * Interface for receiving events from a MIDI device.
+ * Interface for receiving data from a MIDI device.
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
public interface MidiReceiver {
/**
- * Called to pass a MIDI event to the receiver.
+ * Called to pass MIDI data to the receiver.
*
* NOTE: the msg array parameter is only valid within the context of this call.
* The msg bytes should be copied by the receiver rather than retaining a reference
@@ -33,9 +34,9 @@ public interface MidiReceiver {
* Also, modifying the contents of the msg array parameter may result in other receivers
* in the same application receiving incorrect values in their onPost() method.
*
- * @param msg a byte array containing the MIDI message
- * @param offset the offset of the first byte of the message in the byte array
- * @param count the number of bytes in the message
+ * @param msg a byte array containing the MIDI data
+ * @param offset the offset of the first byte of the data in the byte array
+ * @param count the number of bytes of MIDI data in the array
* @param timestamp the timestamp of the message (based on {@link java.lang.System#nanoTime}
* @throws IOException
*/
diff --git a/core/java/android/midi/MidiSender.java b/core/java/android/midi/MidiSender.java
index 7958a06..2b7afad 100644
--- a/core/java/android/midi/MidiSender.java
+++ b/core/java/android/midi/MidiSender.java
@@ -20,6 +20,7 @@ package android.midi;
* Interface provided by a device to allow attaching
* MidiReceivers to a MIDI device.
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
public interface MidiSender {
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 365f2b6..37ee961 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -188,6 +188,7 @@ public class StaticIpConfiguration implements Parcelable {
for (InetAddress dnsServer : dnsServers) {
NetworkUtils.parcelInetAddress(dest, dnsServer, flags);
}
+ dest.writeString(domains);
}
protected static void readFromParcel(StaticIpConfiguration s, Parcel in) {
@@ -198,5 +199,6 @@ public class StaticIpConfiguration implements Parcelable {
for (int i = 0; i < size; i++) {
s.dnsServers.add(NetworkUtils.unparcelInetAddress(in));
}
+ s.domains = in.readString();
}
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 7dd559c..cd86a3c 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -2194,6 +2194,16 @@ public final class ContactsContract {
public static final String CONTACT_ID = "contact_id";
/**
+ * Persistent unique id for each raw_contact within its account.
+ * This id is provided by its own data source, and can be used to backup metadata
+ * to the server.
+ * This should be unique within each set of account_name/account_type/data_set
+ *
+ * @hide
+ */
+ public static final String BACKUP_ID = "backup_id";
+
+ /**
* The data set within the account that this row belongs to. This allows
* multiple sync adapters for the same account type to distinguish between
* each others' data.
@@ -3986,6 +3996,13 @@ public final class ContactsContract {
public static final String MIMETYPE = "mimetype";
/**
+ * Hash id on the data fields, used for backup and restore.
+ *
+ * @hide
+ */
+ public static final String HASH_ID = "hash_id";
+
+ /**
* A reference to the {@link RawContacts#_ID}
* that this data belongs to.
*/
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 259367e..19c9271 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14885,10 +14885,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
void setDisplayListProperties(RenderNode renderNode) {
if (renderNode != null) {
renderNode.setHasOverlappingRendering(hasOverlappingRendering());
- if (mParent instanceof ViewGroup) {
- renderNode.setClipToBounds(
- (((ViewGroup) mParent).mGroupFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0);
- }
+ renderNode.setClipToBounds(mParent instanceof ViewGroup
+ && ((ViewGroup) mParent).getClipChildren());
+
float alpha = 1;
if (mParent instanceof ViewGroup && (((ViewGroup) mParent).mGroupFlags &
ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 151ff83..15e7060 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -472,8 +472,10 @@ public final class ViewRootImpl implements ViewParent,
// Compute surface insets required to draw at specified Z value.
// TODO: Use real shadow insets for a constant max Z.
- final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
- attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
+ if (!attrs.hasManualSurfaceInsets) {
+ final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
+ attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
+ }
CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
mTranslator = compatibilityInfo.getTranslator();
@@ -760,6 +762,7 @@ public final class ViewRootImpl implements ViewParent,
final int oldInsetRight = mWindowAttributes.surfaceInsets.right;
final int oldInsetBottom = mWindowAttributes.surfaceInsets.bottom;
final int oldSoftInputMode = mWindowAttributes.softInputMode;
+ final boolean oldHasManualSurfaceInsets = mWindowAttributes.hasManualSurfaceInsets;
// Keep track of the actual window flags supplied by the client.
mClientWindowLayoutFlags = attrs.flags;
@@ -786,6 +789,7 @@ public final class ViewRootImpl implements ViewParent,
// Restore old surface insets.
mWindowAttributes.surfaceInsets.set(
oldInsetLeft, oldInsetTop, oldInsetRight, oldInsetBottom);
+ mWindowAttributes.hasManualSurfaceInsets = oldHasManualSurfaceInsets;
applyKeepScreenOnFlag(mWindowAttributes);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 12b310f..740cb5d 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1325,6 +1325,16 @@ public interface WindowManager extends ViewManager {
* @hide
*/
public final Rect surfaceInsets = new Rect();
+
+ /**
+ * Whether the surface insets have been manually set. When set to
+ * {@code false}, the view root will automatically determine the
+ * appropriate surface insets.
+ *
+ * @see #surfaceInsets
+ * @hide
+ */
+ public boolean hasManualSurfaceInsets;
/**
* The desired bitmap format. May be one of the constants in
@@ -1621,6 +1631,7 @@ public interface WindowManager extends ViewManager {
out.writeInt(surfaceInsets.top);
out.writeInt(surfaceInsets.right);
out.writeInt(surfaceInsets.bottom);
+ out.writeInt(hasManualSurfaceInsets ? 1 : 0);
out.writeInt(needsMenuKey);
}
@@ -1669,6 +1680,7 @@ public interface WindowManager extends ViewManager {
surfaceInsets.top = in.readInt();
surfaceInsets.right = in.readInt();
surfaceInsets.bottom = in.readInt();
+ hasManualSurfaceInsets = in.readInt() != 0;
needsMenuKey = in.readInt();
}
@@ -1851,6 +1863,11 @@ public interface WindowManager extends ViewManager {
changes |= SURFACE_INSETS_CHANGED;
}
+ if (hasManualSurfaceInsets != o.hasManualSurfaceInsets) {
+ hasManualSurfaceInsets = o.hasManualSurfaceInsets;
+ changes |= SURFACE_INSETS_CHANGED;
+ }
+
if (needsMenuKey != o.needsMenuKey) {
needsMenuKey = o.needsMenuKey;
changes |= NEEDS_MENU_KEY_CHANGED;
@@ -1959,8 +1976,11 @@ public interface WindowManager extends ViewManager {
if (userActivityTimeout >= 0) {
sb.append(" userActivityTimeout=").append(userActivityTimeout);
}
- if (!surfaceInsets.equals(Insets.NONE)) {
+ if (!surfaceInsets.equals(Insets.NONE) || hasManualSurfaceInsets) {
sb.append(" surfaceInsets=").append(surfaceInsets);
+ if (hasManualSurfaceInsets) {
+ sb.append(" (manual)");
+ }
}
if (needsMenuKey != NEEDS_MENU_UNSET) {
sb.append(" needsMenuKey=");
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index f5cd915..7cf3eed 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -27,6 +27,11 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Build;
import android.os.IBinder;
+import android.transition.Transition;
+import android.transition.Transition.EpicenterCallback;
+import android.transition.TransitionInflater;
+import android.transition.TransitionManager;
+import android.transition.TransitionSet;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -39,12 +44,13 @@ import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.view.WindowManager;
import java.lang.ref.WeakReference;
+import java.util.List;
/**
* <p>A popup window that can be used to display an arbitrary view. The popup
* window is a floating container that appears on top of the current
* activity.</p>
- *
+ *
* @see android.widget.AutoCompleteTextView
* @see android.widget.Spinner
*/
@@ -56,7 +62,7 @@ public class PopupWindow {
* it doesn't.
*/
public static final int INPUT_METHOD_FROM_FOCUSABLE = 0;
-
+
/**
* Mode for {@link #setInputMethodMode(int)}: this popup always needs to
* work with an input method, regardless of whether it is focusable. This
@@ -64,7 +70,7 @@ public class PopupWindow {
* the input method while it is shown.
*/
public static final int INPUT_METHOD_NEEDED = 1;
-
+
/**
* Mode for {@link #setInputMethodMode(int)}: this popup never needs to
* work with an input method, regardless of whether it is focusable. This
@@ -75,14 +81,32 @@ public class PopupWindow {
private static final int DEFAULT_ANCHORED_GRAVITY = Gravity.TOP | Gravity.START;
+ /**
+ * Default animation style indicating that separate animations should be
+ * used for top/bottom anchoring states.
+ */
+ private static final int ANIMATION_STYLE_DEFAULT = -1;
+
+ private final int[] mDrawingLocation = new int[2];
+ private final int[] mScreenLocation = new int[2];
+ private final Rect mTempRect = new Rect();
+ private final Rect mAnchorBounds = new Rect();
+
private Context mContext;
private WindowManager mWindowManager;
-
+
private boolean mIsShowing;
private boolean mIsDropdown;
+ /** View that handles event dispatch and content transitions. */
+ private PopupDecorView mDecorView;
+
+ /** View that holds the popup background. May be the content view. */
+ private View mBackgroundView;
+
+ /** The contents of the popup. */
private View mContentView;
- private View mPopupView;
+
private boolean mFocusable;
private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE;
private int mSoftInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
@@ -112,49 +136,52 @@ public class PopupWindow {
private float mElevation;
- private int[] mDrawingLocation = new int[2];
- private int[] mScreenLocation = new int[2];
- private Rect mTempRect = new Rect();
-
private Drawable mBackground;
private Drawable mAboveAnchorBackgroundDrawable;
private Drawable mBelowAnchorBackgroundDrawable;
- // Temporary animation centers. Should be moved into window params?
- private int mAnchorRelativeX;
- private int mAnchorRelativeY;
+ private Transition mEnterTransition;
+ private Transition mExitTransition;
private boolean mAboveAnchor;
private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
-
+
private OnDismissListener mOnDismissListener;
private boolean mIgnoreCheekPress = false;
- private int mAnimationStyle = -1;
-
+ private int mAnimationStyle = ANIMATION_STYLE_DEFAULT;
+
private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] {
com.android.internal.R.attr.state_above_anchor
};
private WeakReference<View> mAnchor;
- private final OnScrollChangedListener mOnScrollChangedListener =
- new OnScrollChangedListener() {
- @Override
- public void onScrollChanged() {
- final View anchor = mAnchor != null ? mAnchor.get() : null;
- if (anchor != null && mPopupView != null) {
- final WindowManager.LayoutParams p = (WindowManager.LayoutParams)
- mPopupView.getLayoutParams();
-
- updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
- mAnchoredGravity));
- update(p.x, p.y, -1, -1, true);
- }
+ private final EpicenterCallback mEpicenterCallback = new EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ return mAnchorBounds;
+ }
+ };
+
+ private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() {
+ @Override
+ public void onScrollChanged() {
+ final View anchor = mAnchor != null ? mAnchor.get() : null;
+ if (anchor != null && mDecorView != null) {
+ final WindowManager.LayoutParams p = (WindowManager.LayoutParams)
+ mDecorView.getLayoutParams();
+
+ updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
+ mAnchoredGravity));
+ update(p.x, p.y, -1, -1, true);
}
- };
+ }
+ };
- private int mAnchorXoff, mAnchorYoff, mAnchoredGravity;
+ private int mAnchorXoff;
+ private int mAnchorYoff;
+ private int mAnchoredGravity;
private boolean mOverlapAnchor;
private boolean mPopupViewInitialLayoutDirectionInherited;
@@ -185,10 +212,10 @@ public class PopupWindow {
public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
-
+
/**
* <p>Create a new, empty, non focusable popup window of dimension (0,0).</p>
- *
+ *
* <p>The popup does not provide a background.</p>
*/
public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
@@ -201,11 +228,34 @@ public class PopupWindow {
mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0);
mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
- final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, -1);
- mAnimationStyle = animStyle == R.style.Animation_PopupWindow ? -1 : animStyle;
+ // Preserve default behavior from Gingerbread. If the animation is
+ // undefined or explicitly specifies the Gingerbread animation style,
+ // use a sentinel value.
+ if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupAnimationStyle)) {
+ final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, 0);
+ if (animStyle == R.style.Animation_PopupWindow) {
+ mAnimationStyle = ANIMATION_STYLE_DEFAULT;
+ } else {
+ mAnimationStyle = animStyle;
+ }
+ } else {
+ mAnimationStyle = ANIMATION_STYLE_DEFAULT;
+ }
+
+ final Transition enterTransition = getTransition(a.getResourceId(
+ R.styleable.PopupWindow_popupEnterTransition, 0));
+ final Transition exitTransition;
+ if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupExitTransition)) {
+ exitTransition = getTransition(a.getResourceId(
+ R.styleable.PopupWindow_popupExitTransition, 0));
+ } else {
+ exitTransition = enterTransition == null ? null : enterTransition.clone();
+ }
a.recycle();
+ setEnterTransition(enterTransition);
+ setExitTransition(exitTransition);
setBackgroundDrawable(bg);
}
@@ -286,6 +336,37 @@ public class PopupWindow {
setFocusable(focusable);
}
+ public void setEnterTransition(Transition enterTransition) {
+ mEnterTransition = enterTransition;
+
+ if (mEnterTransition != null) {
+ mEnterTransition.setEpicenterCallback(mEpicenterCallback);
+ }
+ }
+
+ public void setExitTransition(Transition exitTransition) {
+ mExitTransition = exitTransition;
+
+ if (mExitTransition != null) {
+ mExitTransition.setEpicenterCallback(mEpicenterCallback);
+ }
+ }
+
+ private Transition getTransition(int resId) {
+ if (resId != 0 && resId != R.transition.no_transition) {
+ final TransitionInflater inflater = TransitionInflater.from(mContext);
+ final Transition transition = inflater.inflateTransition(resId);
+ if (transition != null) {
+ final boolean isEmpty = transition instanceof TransitionSet
+ && ((TransitionSet) transition).getTransitionCount() == 0;
+ if (!isEmpty) {
+ return transition;
+ }
+ }
+ }
+ return null;
+ }
+
/**
* Return the drawable used as the popup window's background.
*
@@ -379,7 +460,7 @@ public class PopupWindow {
* Set the flag on popup to ignore cheek press events; by default this flag
* is set to false
* which means the popup will not ignore cheek press dispatch events.
- *
+ *
* <p>If the popup is showing, calling this method will take effect only
* the next time the popup is shown or through a manual call to one of
* the {@link #update()} methods.</p>
@@ -389,7 +470,7 @@ public class PopupWindow {
public void setIgnoreCheekPress() {
mIgnoreCheekPress = true;
}
-
+
/**
* <p>Change the animation style resource for this popup.</p>
@@ -401,13 +482,13 @@ public class PopupWindow {
* @param animationStyle animation style to use when the popup appears
* and disappears. Set to -1 for the default animation, 0 for no
* animation, or a resource identifier for an explicit animation.
- *
+ *
* @see #update()
*/
public void setAnimationStyle(int animationStyle) {
mAnimationStyle = animationStyle;
}
-
+
/**
* <p>Return the view used as the content of the popup window.</p>
*
@@ -491,7 +572,7 @@ public class PopupWindow {
* @param focusable true if the popup should grab focus, false otherwise.
*
* @see #isFocusable()
- * @see #isShowing()
+ * @see #isShowing()
* @see #update()
*/
public void setFocusable(boolean focusable) {
@@ -500,23 +581,23 @@ public class PopupWindow {
/**
* Return the current value in {@link #setInputMethodMode(int)}.
- *
+ *
* @see #setInputMethodMode(int)
*/
public int getInputMethodMode() {
return mInputMethodMode;
-
+
}
-
+
/**
* Control how the popup operates with an input method: one of
* {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
* or {@link #INPUT_METHOD_NOT_NEEDED}.
- *
+ *
* <p>If the popup is showing, calling this method will take effect only
* the next time the popup is shown or through a manual call to one of
* the {@link #update()} methods.</p>
- *
+ *
* @see #getInputMethodMode()
* @see #update()
*/
@@ -547,12 +628,12 @@ public class PopupWindow {
public int getSoftInputMode() {
return mSoftInputMode;
}
-
+
/**
* <p>Indicates whether the popup window receives touch events.</p>
- *
+ *
* @return true if the popup is touchable, false otherwise
- *
+ *
* @see #setTouchable(boolean)
*/
public boolean isTouchable() {
@@ -571,7 +652,7 @@ public class PopupWindow {
* @param touchable true if the popup should receive touch events, false otherwise
*
* @see #isTouchable()
- * @see #isShowing()
+ * @see #isShowing()
* @see #update()
*/
public void setTouchable(boolean touchable) {
@@ -581,9 +662,9 @@ public class PopupWindow {
/**
* <p>Indicates whether the popup window will be informed of touch events
* outside of its window.</p>
- *
+ *
* @return true if the popup is outside touchable, false otherwise
- *
+ *
* @see #setOutsideTouchable(boolean)
*/
public boolean isOutsideTouchable() {
@@ -604,7 +685,7 @@ public class PopupWindow {
* touch events, false otherwise
*
* @see #isOutsideTouchable()
- * @see #isShowing()
+ * @see #isShowing()
* @see #update()
*/
public void setOutsideTouchable(boolean touchable) {
@@ -613,9 +694,9 @@ public class PopupWindow {
/**
* <p>Indicates whether clipping of the popup window is enabled.</p>
- *
+ *
* @return true if the clipping is enabled, false otherwise
- *
+ *
* @see #setClippingEnabled(boolean)
*/
public boolean isClippingEnabled() {
@@ -626,13 +707,13 @@ public class PopupWindow {
* <p>Allows the popup window to extend beyond the bounds of the screen. By default the
* window is clipped to the screen boundaries. Setting this to false will allow windows to be
* accurately positioned.</p>
- *
+ *
* <p>If the popup is showing, calling this method will take effect only
* the next time the popup is shown or through a manual call to one of
* the {@link #update()} methods.</p>
*
* @param enabled false if the window should be allowed to extend outside of the screen
- * @see #isShowing()
+ * @see #isShowing()
* @see #isClippingEnabled()
* @see #update()
*/
@@ -660,12 +741,12 @@ public class PopupWindow {
void setAllowScrollingAnchorParent(boolean enabled) {
mAllowScrollingAnchorParent = enabled;
}
-
+
/**
* <p>Indicates whether the popup window supports splitting touches.</p>
- *
+ *
* @return true if the touch splitting is enabled, false otherwise
- *
+ *
* @see #setSplitTouchEnabled(boolean)
*/
public boolean isSplitTouchEnabled() {
@@ -794,7 +875,7 @@ public class PopupWindow {
* window manager by the popup. By default these are 0, meaning that
* the current width or height is requested as an explicit size from
* the window manager. You can supply
- * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
+ * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
* {@link ViewGroup.LayoutParams#MATCH_PARENT} to have that measure
* spec supplied instead, replacing the absolute width and height that
* has been set in the popup.</p>
@@ -815,7 +896,7 @@ public class PopupWindow {
mWidthMode = widthSpec;
mHeightMode = heightSpec;
}
-
+
/**
* <p>Return this popup's height MeasureSpec</p>
*
@@ -836,7 +917,7 @@ public class PopupWindow {
* @param height the height MeasureSpec of the popup
*
* @see #getHeight()
- * @see #isShowing()
+ * @see #isShowing()
*/
public void setHeight(int height) {
mHeight = height;
@@ -847,7 +928,7 @@ public class PopupWindow {
*
* @return the width MeasureSpec of the popup
*
- * @see #setWidth(int)
+ * @see #setWidth(int)
*/
public int getWidth() {
return mWidth;
@@ -913,7 +994,7 @@ public class PopupWindow {
* a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
* <code>Gravity.LEFT | Gravity.TOP</code>.
* </p>
- *
+ *
* @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
* @param gravity the gravity which controls the placement of the popup window
* @param x the popup's x location offset
@@ -946,7 +1027,7 @@ public class PopupWindow {
WindowManager.LayoutParams p = createPopupLayout(token);
p.windowAnimations = computeAnimationResource();
-
+
preparePopup(p);
if (gravity == Gravity.NO_GRAVITY) {
gravity = Gravity.TOP | Gravity.START;
@@ -1049,12 +1130,12 @@ public class PopupWindow {
// do the job.
if (mAboveAnchorBackgroundDrawable != null) {
if (mAboveAnchor) {
- mPopupView.setBackground(mAboveAnchorBackgroundDrawable);
+ mDecorView.setBackground(mAboveAnchorBackgroundDrawable);
} else {
- mPopupView.setBackground(mBelowAnchorBackgroundDrawable);
+ mDecorView.setBackground(mBelowAnchorBackgroundDrawable);
}
} else {
- mPopupView.refreshDrawableState();
+ mDecorView.refreshDrawableState();
}
}
}
@@ -1089,36 +1170,79 @@ public class PopupWindow {
+ "calling setContentView() before attempting to show the popup.");
}
+ // When a background is available, we embed the content view within
+ // another view that owns the background drawable.
if (mBackground != null) {
- final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
- int height = ViewGroup.LayoutParams.MATCH_PARENT;
- if (layoutParams != null &&
- layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
- height = ViewGroup.LayoutParams.WRAP_CONTENT;
- }
-
- // when a background is available, we embed the content view
- // within another view that owns the background drawable
- PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
- PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, height
- );
- popupViewContainer.setBackground(mBackground);
- popupViewContainer.addView(mContentView, listParams);
-
- mPopupView = popupViewContainer;
+ mBackgroundView = createBackgroundView(mContentView);
+ mBackgroundView.setBackground(mBackground);
} else {
- mPopupView = mContentView;
+ mBackgroundView = mContentView;
}
- mPopupView.setElevation(mElevation);
+ mDecorView = createDecorView(mBackgroundView);
+
+ // The background owner should be elevated so that it casts a shadow.
+ mBackgroundView.setElevation(mElevation);
+
+ // We may wrap that in another view, so we'll need to manually specify
+ // the surface insets.
+ final int surfaceInset = (int) Math.ceil(mBackgroundView.getZ() * 2);
+ p.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
+ p.hasManualSurfaceInsets = true;
+
mPopupViewInitialLayoutDirectionInherited =
- (mPopupView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
+ (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
mPopupWidth = p.width;
mPopupHeight = p.height;
}
/**
+ * Wraps a content view in a PopupViewContainer.
+ *
+ * @param contentView the content view to wrap
+ * @return a PopupViewContainer that wraps the content view
+ */
+ private PopupBackgroundView createBackgroundView(View contentView) {
+ final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
+ final int height;
+ if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ } else {
+ height = ViewGroup.LayoutParams.MATCH_PARENT;
+ }
+
+ final PopupBackgroundView backgroundView = new PopupBackgroundView(mContext);
+ final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, height);
+ backgroundView.addView(contentView, listParams);
+
+ return backgroundView;
+ }
+
+ /**
+ * Wraps a content view in a FrameLayout.
+ *
+ * @param contentView the content view to wrap
+ * @return a FrameLayout that wraps the content view
+ */
+ private PopupDecorView createDecorView(View contentView) {
+ final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
+ final int height;
+ if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ } else {
+ height = ViewGroup.LayoutParams.MATCH_PARENT;
+ }
+
+ final PopupDecorView decorView = new PopupDecorView(mContext);
+ decorView.addView(contentView, ViewGroup.LayoutParams.MATCH_PARENT, height);
+ decorView.setClipChildren(false);
+ decorView.setClipToPadding(false);
+
+ return decorView;
+ }
+
+ /**
* <p>Invoke the popup window by adding the content view to the window
* manager.</p>
*
@@ -1130,16 +1254,34 @@ public class PopupWindow {
if (mContext != null) {
p.packageName = mContext.getPackageName();
}
- mPopupView.setFitsSystemWindows(mLayoutInsetDecor);
+
+ final View rootView = mContentView.getRootView();
+ rootView.setFitsSystemWindows(mLayoutInsetDecor);
setLayoutDirectionFromAnchor();
- mWindowManager.addView(mPopupView, p);
+
+ mWindowManager.addView(rootView, p);
+
+ // Postpone enter transition until the scene root has been laid out.
+ if (mEnterTransition != null) {
+ mEnterTransition.addTarget(mBackgroundView);
+ mEnterTransition.addListener(new Transition.TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ transition.removeListener(this);
+ transition.removeTarget(mBackgroundView);
+ }
+ });
+
+ mDecorView.getViewTreeObserver().addOnGlobalLayoutListener(
+ new PostLayoutTransitionListener(mDecorView, mEnterTransition));
+ }
}
private void setLayoutDirectionFromAnchor() {
if (mAnchor != null) {
View anchor = mAnchor.get();
if (anchor != null && mPopupViewInitialLayoutDirectionInherited) {
- mPopupView.setLayoutDirection(anchor.getLayoutDirection());
+ mDecorView.setLayoutDirection(anchor.getLayoutDirection());
}
}
}
@@ -1224,7 +1366,7 @@ public class PopupWindow {
}
private int computeAnimationResource() {
- if (mAnimationStyle == -1) {
+ if (mAnimationStyle == ANIMATION_STYLE_DEFAULT) {
if (mIsDropdown) {
return mAboveAnchor
? com.android.internal.R.style.Animation_DropDownUp
@@ -1243,7 +1385,7 @@ public class PopupWindow {
* <p>
* The height must have been set on the layout parameters prior to calling
* this method.
- *
+ *
* @param anchor the view on which the popup window must be anchored
* @param p the layout parameters used to display the drop down
* @param xoff horizontal offset used to adjust for background padding
@@ -1342,18 +1484,18 @@ public class PopupWindow {
p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
// Compute the position of the anchor relative to the popup.
- mAnchorRelativeX = mDrawingLocation[0] - p.x + anchorHeight / 2;
- mAnchorRelativeY = mDrawingLocation[1] - p.y + anchorWidth / 2;
+ mAnchorBounds.set(0, 0, anchorWidth, anchorHeight);
+ mAnchorBounds.offset(mDrawingLocation[0] - p.x, mDrawingLocation[1] - p.y);
return onTop;
}
-
+
/**
* Returns the maximum height that is available for the popup to be
* completely shown. It is recommended that this height be the maximum for
* the popup's height, otherwise it is possible that the popup will be
* clipped.
- *
+ *
* @param anchor The view on which the popup window must be anchored.
* @return The maximum available height for the popup to be completely
* shown.
@@ -1376,14 +1518,14 @@ public class PopupWindow {
public int getMaxAvailableHeight(View anchor, int yOffset) {
return getMaxAvailableHeight(anchor, yOffset, false);
}
-
+
/**
* Returns the maximum height that is available for the popup to be
* completely shown, optionally ignoring any bottom decorations such as
* the input method. It is recommended that this height be the maximum for
* the popup's height, otherwise it is possible that the popup will be
* clipped.
- *
+ *
* @param anchor The view on which the popup window must be anchored.
* @param yOffset y offset from the view's bottom edge
* @param ignoreBottomDecorations if true, the height returned will be
@@ -1391,7 +1533,7 @@ public class PopupWindow {
* bottom decorations
* @return The maximum available height for the popup to be completely
* shown.
- *
+ *
* @hide Pending API council approval.
*/
public int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) {
@@ -1400,7 +1542,7 @@ public class PopupWindow {
final int[] anchorPos = mDrawingLocation;
anchor.getLocationOnScreen(anchorPos);
-
+
int bottomEdge = displayFrame.bottom;
if (ignoreBottomDecorations) {
Resources res = anchor.getContext().getResources();
@@ -1413,49 +1555,78 @@ public class PopupWindow {
int returnedHeight = Math.max(distanceToBottom, distanceToTop);
if (mBackground != null) {
mBackground.getPadding(mTempRect);
- returnedHeight -= mTempRect.top + mTempRect.bottom;
+ returnedHeight -= mTempRect.top + mTempRect.bottom;
}
-
+
return returnedHeight;
}
-
+
/**
* <p>Dispose of the popup window. This method can be invoked only after
* {@link #showAsDropDown(android.view.View)} has been executed. Failing that, calling
* this method will have no effect.</p>
*
- * @see #showAsDropDown(android.view.View)
+ * @see #showAsDropDown(android.view.View)
*/
public void dismiss() {
- if (isShowing() && mPopupView != null) {
+ if (isShowing() && mDecorView != null) {
mIsShowing = false;
unregisterForScrollChanged();
- try {
- mWindowManager.removeViewImmediate(mPopupView);
- } finally {
- if (mPopupView != mContentView && mPopupView instanceof ViewGroup) {
- ((ViewGroup) mPopupView).removeView(mContentView);
- }
- mPopupView = null;
+ if (mExitTransition != null) {
+ mExitTransition.addTarget(mBackgroundView);
+ mExitTransition.addListener(new Transition.TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ transition.removeListener(this);
+ transition.removeTarget(mBackgroundView);
- if (mOnDismissListener != null) {
- mOnDismissListener.onDismiss();
- }
+ dismissImmediate();
+ }
+ });
+
+ TransitionManager.beginDelayedTransition(mDecorView, mExitTransition);
+
+ // Transition to invisible.
+ mBackgroundView.setVisibility(View.INVISIBLE);
+ } else {
+ dismissImmediate();
+ }
+ }
+ }
+
+ /**
+ * Removes the popup from the window manager and tears down the supporting
+ * view hierarchy, if necessary.
+ */
+ private void dismissImmediate() {
+ try {
+ mWindowManager.removeViewImmediate(mDecorView);
+ } finally {
+ mDecorView.removeView(mBackgroundView);
+ mDecorView = null;
+
+ if (mBackgroundView != mContentView) {
+ ((ViewGroup) mBackgroundView).removeView(mContentView);
+ }
+ mBackgroundView = null;
+
+ if (mOnDismissListener != null) {
+ mOnDismissListener.onDismiss();
}
}
}
/**
* Sets the listener to be called when the window is dismissed.
- *
+ *
* @param onDismissListener The listener.
*/
public void setOnDismissListener(OnDismissListener onDismissListener) {
mOnDismissListener = onDismissListener;
}
-
+
/**
* Updates the state of the popup window, if it is currently being displayed,
* from the currently set state. This includes:
@@ -1467,12 +1638,12 @@ public class PopupWindow {
if (!isShowing() || mContentView == null) {
return;
}
-
- WindowManager.LayoutParams p = (WindowManager.LayoutParams)
- mPopupView.getLayoutParams();
-
+
+ final WindowManager.LayoutParams p =
+ (WindowManager.LayoutParams) mDecorView.getLayoutParams();
+
boolean update = false;
-
+
final int newAnim = computeAnimationResource();
if (newAnim != p.windowAnimations) {
p.windowAnimations = newAnim;
@@ -1487,7 +1658,7 @@ public class PopupWindow {
if (update) {
setLayoutDirectionFromAnchor();
- mWindowManager.updateViewLayout(mPopupView, p);
+ mWindowManager.updateViewLayout(mDecorView, p);
}
}
@@ -1500,11 +1671,11 @@ public class PopupWindow {
* @param height the new height
*/
public void update(int width, int height) {
- WindowManager.LayoutParams p = (WindowManager.LayoutParams)
- mPopupView.getLayoutParams();
+ final WindowManager.LayoutParams p =
+ (WindowManager.LayoutParams) mDecorView.getLayoutParams();
update(p.x, p.y, width, height, false);
}
-
+
/**
* <p>Updates the position and the dimension of the popup window. Width and
* height can be set to -1 to update location only. Calling this function
@@ -1548,7 +1719,8 @@ public class PopupWindow {
return;
}
- WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams();
+ final WindowManager.LayoutParams p =
+ (WindowManager.LayoutParams) mDecorView.getLayoutParams();
boolean update = force;
@@ -1588,7 +1760,7 @@ public class PopupWindow {
if (update) {
setLayoutDirectionFromAnchor();
- mWindowManager.updateViewLayout(mPopupView, p);
+ mWindowManager.updateViewLayout(mDecorView, p);
}
}
@@ -1655,7 +1827,7 @@ public class PopupWindow {
}
final WindowManager.LayoutParams p =
- (WindowManager.LayoutParams) mPopupView.getLayoutParams();
+ (WindowManager.LayoutParams) mDecorView.getLayoutParams();
final int x = p.x;
final int y = p.y;
if (updateLocation) {
@@ -1694,7 +1866,7 @@ public class PopupWindow {
private void registerForScrollChanged(View anchor, int xoff, int yoff, int gravity) {
unregisterForScrollChanged();
- mAnchor = new WeakReference<View>(anchor);
+ mAnchor = new WeakReference<>(anchor);
ViewTreeObserver vto = anchor.getViewTreeObserver();
if (vto != null) {
vto.addOnScrollChangedListener(mOnScrollChangedListener);
@@ -1705,24 +1877,50 @@ public class PopupWindow {
mAnchoredGravity = gravity;
}
- private class PopupViewContainer extends FrameLayout {
- private static final String TAG = "PopupWindow.PopupViewContainer";
+ /**
+ * Layout listener used to run a transition immediately after a view is
+ * laid out. Forces the view to transition from invisible to visible.
+ */
+ private static class PostLayoutTransitionListener implements
+ ViewTreeObserver.OnGlobalLayoutListener {
+ private final ViewGroup mSceneRoot;
+ private final Transition mTransition;
- public PopupViewContainer(Context context) {
- super(context);
+ public PostLayoutTransitionListener(ViewGroup sceneRoot, Transition transition) {
+ mSceneRoot = sceneRoot;
+ mTransition = transition;
}
@Override
- protected int[] onCreateDrawableState(int extraSpace) {
- if (mAboveAnchor) {
- // 1 more needed for the above anchor state
- final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
- View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
- return drawableState;
- } else {
- return super.onCreateDrawableState(extraSpace);
+ public void onGlobalLayout() {
+ final ViewTreeObserver observer = mSceneRoot.getViewTreeObserver();
+ if (observer == null) {
+ // View has been detached.
+ return;
+ }
+
+ observer.removeOnGlobalLayoutListener(this);
+
+ // Set all targets to be initially invisible.
+ final List<View> targets = mTransition.getTargets();
+ final int N = targets.size();
+ for (int i = 0; i < N; i++) {
+ targets.get(i).setVisibility(View.INVISIBLE);
+ }
+
+ TransitionManager.beginDelayedTransition(mSceneRoot, mTransition);
+
+ // Transition targets to visible.
+ for (int i = 0; i < N; i++) {
+ targets.get(i).setVisibility(View.VISIBLE);
}
}
+ }
+
+ private class PopupDecorView extends FrameLayout {
+ public PopupDecorView(Context context) {
+ super(context);
+ }
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
@@ -1731,15 +1929,14 @@ public class PopupWindow {
return super.dispatchKeyEvent(event);
}
- if (event.getAction() == KeyEvent.ACTION_DOWN
- && event.getRepeatCount() == 0) {
- KeyEvent.DispatcherState state = getKeyDispatcherState();
+ if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
+ final KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null) {
state.startTracking(event, this);
}
return true;
} else if (event.getAction() == KeyEvent.ACTION_UP) {
- KeyEvent.DispatcherState state = getKeyDispatcherState();
+ final KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null && state.isTracking(event) && !event.isCanceled()) {
dismiss();
return true;
@@ -1763,7 +1960,7 @@ public class PopupWindow {
public boolean onTouchEvent(MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
-
+
if ((event.getAction() == MotionEvent.ACTION_DOWN)
&& ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
dismiss();
@@ -1775,17 +1972,22 @@ public class PopupWindow {
return super.onTouchEvent(event);
}
}
+ }
+
+ private class PopupBackgroundView extends FrameLayout {
+ public PopupBackgroundView(Context context) {
+ super(context);
+ }
- /** @hide */
@Override
- public void sendAccessibilityEventInternal(int eventType) {
- // clinets are interested in the content not the container, make it event source
- if (mContentView != null) {
- mContentView.sendAccessibilityEvent(eventType);
+ protected int[] onCreateDrawableState(int extraSpace) {
+ if (mAboveAnchor) {
+ final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
+ View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
+ return drawableState;
} else {
- super.sendAccessibilityEventInternal(eventType);
+ return super.onCreateDrawableState(extraSpace);
}
}
}
-
}
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 13d6b42..a282cf5 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -17,6 +17,7 @@
package android.widget;
import android.animation.ObjectAnimator;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -24,6 +25,7 @@ import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Paint;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.Region.Op;
@@ -84,7 +86,17 @@ public class Switch extends CompoundButton {
private static final int MONOSPACE = 3;
private Drawable mThumbDrawable;
+ private ColorStateList mThumbTintList = null;
+ private PorterDuff.Mode mThumbTintMode = null;
+ private boolean mHasThumbTint = false;
+ private boolean mHasThumbTintMode = false;
+
private Drawable mTrackDrawable;
+ private ColorStateList mTrackTintList = null;
+ private PorterDuff.Mode mTrackTintMode = null;
+ private boolean mHasTrackTint = false;
+ private boolean mHasTrackTintMode = false;
+
private int mThumbTextPadding;
private int mSwitchMinWidth;
private int mSwitchPadding;
@@ -473,6 +485,86 @@ public class Switch extends CompoundButton {
}
/**
+ * Applies a tint to the track drawable. Does not modify the current
+ * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+ * <p>
+ * Subsequent calls to {@link #setTrackDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and tint
+ * mode using {@link Drawable#setTintList(ColorStateList)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#Switch_trackTint
+ * @see #getTrackTintList()
+ * @see Drawable#setTintList(ColorStateList)
+ */
+ public void setTrackTintList(@Nullable ColorStateList tint) {
+ mTrackTintList = tint;
+ mHasTrackTint = true;
+
+ applyTrackTint();
+ }
+
+ /**
+ * @return the tint applied to the track drawable
+ * @attr ref android.R.styleable#Switch_trackTint
+ * @see #setTrackTintList(ColorStateList)
+ */
+ @Nullable
+ public ColorStateList getTrackTintList() {
+ return mTrackTintList;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setTrackTintList(ColorStateList)}} to the track drawable.
+ * The default mode is {@link PorterDuff.Mode#SRC_IN}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#Switch_trackTintMode
+ * @see #getTrackTintMode()
+ * @see Drawable#setTintMode(PorterDuff.Mode)
+ */
+ public void setTrackTintMode(@Nullable PorterDuff.Mode tintMode) {
+ mTrackTintMode = tintMode;
+ mHasTrackTintMode = true;
+
+ applyTrackTint();
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the track
+ * drawable
+ * @attr ref android.R.styleable#Switch_trackTintMode
+ * @see #setTrackTintMode(PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getTrackTintMode() {
+ return mTrackTintMode;
+ }
+
+ private void applyTrackTint() {
+ if (mTrackDrawable != null && (mHasTrackTint || mHasTrackTintMode)) {
+ mTrackDrawable = mTrackDrawable.mutate();
+
+ if (mHasTrackTint) {
+ mTrackDrawable.setTintList(mTrackTintList);
+ }
+
+ if (mHasTrackTintMode) {
+ mTrackDrawable.setTintMode(mTrackTintMode);
+ }
+
+ // The drawable (or one of its children) may not have been
+ // stateful before applying the tint, so let's try again.
+ if (mTrackDrawable.isStateful()) {
+ mTrackDrawable.setState(getDrawableState());
+ }
+ }
+ }
+
+ /**
* Set the drawable used for the switch "thumb" - the piece that the user
* can physically touch and drag along the track.
*
@@ -516,6 +608,86 @@ public class Switch extends CompoundButton {
}
/**
+ * Applies a tint to the thumb drawable. Does not modify the current
+ * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+ * <p>
+ * Subsequent calls to {@link #setThumbDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and tint
+ * mode using {@link Drawable#setTintList(ColorStateList)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#Switch_thumbTint
+ * @see #getThumbTintList()
+ * @see Drawable#setTintList(ColorStateList)
+ */
+ public void setThumbTintList(@Nullable ColorStateList tint) {
+ mThumbTintList = tint;
+ mHasThumbTint = true;
+
+ applyThumbTint();
+ }
+
+ /**
+ * @return the tint applied to the thumb drawable
+ * @attr ref android.R.styleable#Switch_thumbTint
+ * @see #setThumbTintList(ColorStateList)
+ */
+ @Nullable
+ public ColorStateList getThumbTintList() {
+ return mThumbTintList;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setThumbTintList(ColorStateList)}} to the thumb drawable.
+ * The default mode is {@link PorterDuff.Mode#SRC_IN}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#Switch_thumbTintMode
+ * @see #getThumbTintMode()
+ * @see Drawable#setTintMode(PorterDuff.Mode)
+ */
+ public void setThumbTintMode(@Nullable PorterDuff.Mode tintMode) {
+ mThumbTintMode = tintMode;
+ mHasThumbTintMode = true;
+
+ applyThumbTint();
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the thumb
+ * drawable
+ * @attr ref android.R.styleable#Switch_thumbTintMode
+ * @see #setThumbTintMode(PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getThumbTintMode() {
+ return mThumbTintMode;
+ }
+
+ private void applyThumbTint() {
+ if (mThumbDrawable != null && (mHasThumbTint || mHasThumbTintMode)) {
+ mThumbDrawable = mThumbDrawable.mutate();
+
+ if (mHasThumbTint) {
+ mThumbDrawable.setTintList(mThumbTintList);
+ }
+
+ if (mHasThumbTintMode) {
+ mThumbDrawable.setTintMode(mThumbTintMode);
+ }
+
+ // The drawable (or one of its children) may not have been
+ // stateful before applying the tint, so let's try again.
+ if (mThumbDrawable.isStateful()) {
+ mThumbDrawable.setState(getDrawableState());
+ }
+ }
+ }
+
+ /**
* Specifies whether the track should be split by the thumb. When true,
* the thumb's optical bounds will be clipped out of the track drawable,
* then the thumb will be drawn into the resulting gap.
diff --git a/core/java/com/android/internal/transition/EpicenterClipReveal.java b/core/java/com/android/internal/transition/EpicenterClipReveal.java
new file mode 100644
index 0000000..d8a7f16
--- /dev/null
+++ b/core/java/com/android/internal/transition/EpicenterClipReveal.java
@@ -0,0 +1,115 @@
+/*
+ * 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 com.android.internal.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.RectEvaluator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * EpicenterClipReveal captures the {@link View#getClipBounds()} before and
+ * after the scene change and animates between those and the epicenter bounds
+ * during a visibility transition.
+ */
+public class EpicenterClipReveal extends Visibility {
+ private static final String PROPNAME_CLIP = "android:epicenterReveal:clip";
+ private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
+
+ public EpicenterClipReveal() {}
+
+ public EpicenterClipReveal(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ captureValues(transitionValues);
+ }
+
+ private void captureValues(TransitionValues values) {
+ final View view = values.view;
+ if (view.getVisibility() == View.GONE) {
+ return;
+ }
+
+ final Rect clip = view.getClipBounds();
+ values.values.put(PROPNAME_CLIP, clip);
+
+ if (clip == null) {
+ final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
+ values.values.put(PROPNAME_BOUNDS, bounds);
+ }
+ }
+
+ @Override
+ public Animator onAppear(ViewGroup sceneRoot, View view,
+ TransitionValues startValues, TransitionValues endValues) {
+ if (endValues == null) {
+ return null;
+ }
+
+ final Rect start = getEpicenter();
+ final Rect end = getBestRect(endValues);
+
+ // Prepare the view.
+ view.setClipBounds(start);
+
+ return createRectAnimator(view, start, end);
+ }
+
+ @Override
+ public Animator onDisappear(ViewGroup sceneRoot, View view,
+ TransitionValues startValues, TransitionValues endValues) {
+ if (startValues == null) {
+ return null;
+ }
+
+ final Rect start = getBestRect(startValues);
+ final Rect end = getEpicenter();
+
+ // Prepare the view.
+ view.setClipBounds(start);
+
+ return createRectAnimator(view, start, end);
+ }
+
+ private Rect getBestRect(TransitionValues values) {
+ final Rect clipRect = (Rect) values.values.get(PROPNAME_CLIP);
+ if (clipRect == null) {
+ return (Rect) values.values.get(PROPNAME_BOUNDS);
+ }
+ return clipRect;
+ }
+
+ private Animator createRectAnimator(View view, Rect start, Rect end) {
+ final RectEvaluator evaluator = new RectEvaluator(new Rect());
+ return ObjectAnimator.ofObject(view, "clipBounds", evaluator, start, end);
+ }
+}
diff --git a/core/res/res/transition/popup_window_enter.xml b/core/res/res/transition/popup_window_enter.xml
new file mode 100644
index 0000000..92d4c1e
--- /dev/null
+++ b/core/res/res/transition/popup_window_enter.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
+ android:transitionOrdering="together">
+ <transition class="com.android.internal.transition.EpicenterClipReveal"
+ android:interpolator="@android:interpolator/accelerate_cubic"
+ android:startDelay="50"
+ android:duration="300" />
+ <fade
+ android:interpolator="@android:interpolator/linear"
+ android:duration="100" />
+</transitionSet>
diff --git a/core/res/res/transition/popup_window_exit.xml b/core/res/res/transition/popup_window_exit.xml
new file mode 100644
index 0000000..5cb9f0b
--- /dev/null
+++ b/core/res/res/transition/popup_window_exit.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<fade xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:interpolator/linear" />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 39c42ee..559d750 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4338,6 +4338,10 @@
<attr name="popupAnimationStyle" format="reference" />
<!-- Whether the popup window should overlap its anchor view. -->
<attr name="overlapAnchor" format="boolean" />
+ <!-- Transition used to move views into the popup window. -->
+ <attr name="popupEnterTransition" format="reference" />
+ <!-- Transition used to move views out of the popup window. -->
+ <attr name="popupExitTransition" format="reference" />
</declare-styleable>
<declare-styleable name="ListPopupWindow">
<!-- Amount of pixels by which the drop down should be offset vertically. -->
@@ -7257,8 +7261,34 @@
<declare-styleable name="Switch">
<!-- Drawable to use as the "thumb" that switches back and forth. -->
<attr name="thumb" />
+ <!-- Tint to apply to the thumb. -->
+ <attr name="thumbTint" />
+ <!-- Blending mode used to apply the thumb tint. -->
+ <attr name="thumbTintMode" />
<!-- Drawable to use as the "track" that the switch thumb slides within. -->
<attr name="track" format="reference" />
+ <!-- Tint to apply to the track. -->
+ <attr name="trackTint" format="color" />
+ <!-- Blending mode used to apply the track tint. -->
+ <attr name="trackTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
<!-- Text to use when the switch is in the checked/"on" state. -->
<attr name="textOn" />
<!-- Text to use when the switch is in the unchecked/"off" state. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9945c63..461f9a0 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2608,6 +2608,9 @@
=============================================================== -->
<eat-comment />
+ <public type="attr" name="trackTint" />
+ <public type="attr" name="trackTintMode" />
+
<public type="style" name="Widget.Material.Button.Colored" />
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 4329809..48645ed 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -770,6 +770,9 @@ please see styles_device_defaults.xml.
<item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="popupBackground">@drawable/popup_background_material</item>
<item name="popupElevation">@dimen/floating_window_z</item>
+ <item name="popupAnimationStyle">@empty</item>
+ <item name="popupEnterTransition">@transition/popup_window_enter</item>
+ <item name="popupExitTransition">@transition/popup_window_exit</item>
<item name="dropDownVerticalOffset">0dip</item>
<item name="dropDownHorizontalOffset">0dip</item>
<item name="overlapAnchor">true</item>
@@ -780,11 +783,7 @@ please see styles_device_defaults.xml.
</style>
<style name="Widget.Material.Spinner.DropDown"/>
-
- <style name="Widget.Material.Spinner.DropDown.ActionBar">
- <item name="background">@drawable/spinner_background_material</item>
- <item name="overlapAnchor">true</item>
- </style>
+ <style name="Widget.Material.Spinner.DropDown.ActionBar" />
<style name="Widget.Material.Spinner.Underlined">
<item name="background">@drawable/spinner_textfield_background_material</item>
@@ -847,7 +846,9 @@ please see styles_device_defaults.xml.
<item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="popupBackground">@drawable/popup_background_material</item>
<item name="popupElevation">@dimen/floating_window_z</item>
- <item name="popupAnimationStyle">@style/Animation.Material.Popup</item>
+ <item name="popupAnimationStyle">@empty</item>
+ <item name="popupEnterTransition">@transition/popup_window_enter</item>
+ <item name="popupExitTransition">@transition/popup_window_exit</item>
<item name="dropDownVerticalOffset">0dip</item>
<item name="dropDownHorizontalOffset">0dip</item>
<item name="dropDownWidth">wrap_content</item>
diff --git a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
index e7f4bad..e5a92bf 100644
--- a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
+++ b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
@@ -11,7 +11,7 @@ public class ParceledListSliceTest extends TestCase {
public void testSmallList() throws Exception {
final int objectCount = 100;
- List<SmallObject> list = new ArrayList<>();
+ List<SmallObject> list = new ArrayList<SmallObject>();
for (int i = 0; i < objectCount; i++) {
list.add(new SmallObject(i * 2, (i * 2) + 1));
}
@@ -20,7 +20,7 @@ public class ParceledListSliceTest extends TestCase {
Parcel parcel = Parcel.obtain();
try {
- parcel.writeParcelable(new ParceledListSlice<>(list), 0);
+ parcel.writeParcelable(new ParceledListSlice<SmallObject>(list), 0);
parcel.setDataPosition(0);
slice = parcel.readParcelable(getClass().getClassLoader());
} finally {
@@ -56,7 +56,7 @@ public class ParceledListSliceTest extends TestCase {
final int thresholdBytes = 256 * 1024;
final int objectCount = thresholdBytes / measureLargeObject();
- List<LargeObject> list = new ArrayList<>();
+ List<LargeObject> list = new ArrayList<LargeObject>();
for (int i = 0; i < objectCount; i++) {
list.add(new LargeObject(
i * 5,
@@ -71,7 +71,7 @@ public class ParceledListSliceTest extends TestCase {
Parcel parcel = Parcel.obtain();
try {
- parcel.writeParcelable(new ParceledListSlice<>(list), 0);
+ parcel.writeParcelable(new ParceledListSlice<LargeObject>(list), 0);
parcel.setDataPosition(0);
slice = parcel.readParcelable(getClass().getClassLoader());
} finally {
@@ -95,7 +95,7 @@ public class ParceledListSliceTest extends TestCase {
* Test that only homogeneous elements may be unparceled.
*/
public void testHomogeneousElements() throws Exception {
- List<BaseObject> list = new ArrayList<>();
+ List<BaseObject> list = new ArrayList<BaseObject>();
list.add(new LargeObject(0, 1, 2, 3, 4));
list.add(new SmallObject(5, 6));
list.add(new SmallObject(7, 8));
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index fa9af2a..39272b9 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -122,11 +122,6 @@ public class Picture {
* @param canvas The picture is drawn to this canvas
*/
public void draw(Canvas canvas) {
- if (canvas.isHardwareAccelerated()) {
- throw new IllegalArgumentException(
- "Picture playback is only supported on software canvas.");
- }
-
if (mRecordingCanvas != null) {
endRecording();
}
diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk
index 7c1a724..a05217f 100644
--- a/libs/hwui/Android.common.mk
+++ b/libs/hwui/Android.common.mk
@@ -7,11 +7,13 @@ LOCAL_CLANG_CFLAGS += \
LOCAL_SRC_FILES := \
font/CacheTexture.cpp \
font/Font.cpp \
+ renderstate/Blend.cpp \
renderstate/MeshState.cpp \
renderstate/PixelBufferState.cpp \
renderstate/RenderState.cpp \
renderstate/Scissor.cpp \
renderstate/Stencil.cpp \
+ renderstate/TextureState.cpp \
renderthread/CanvasContext.cpp \
renderthread/DrawFrameTask.cpp \
renderthread/EglManager.cpp \
@@ -63,6 +65,7 @@ LOCAL_SRC_FILES := \
ResourceCache.cpp \
ShadowTessellator.cpp \
SkiaCanvas.cpp \
+ SkiaCanvasProxy.cpp \
SkiaShader.cpp \
Snapshot.cpp \
SpotShadow.cpp \
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 1fb8092..cef2c84 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -49,6 +49,7 @@ Caches* Caches::sInstance = nullptr;
Caches::Caches(RenderState& renderState)
: patchCache(renderState)
+ , dither(*this)
, mRenderState(&renderState)
, mExtensions(Extensions::getInstance())
, mInitialized(false) {
@@ -71,13 +72,8 @@ bool Caches::init() {
ATRACE_NAME("Caches::init");
- glActiveTexture(gTextureUnits[0]);
- mTextureUnit = 0;
mRegionMesh = nullptr;
- blend = false;
- lastSrcMode = GL_ZERO;
- lastDstMode = GL_ZERO;
currentProgram = nullptr;
mFunctorsCount = 0;
@@ -90,8 +86,8 @@ bool Caches::init() {
mInitialized = true;
- resetBoundTextures();
- mPixelBufferState.reset(new PixelBufferState());
+ mPixelBufferState = new PixelBufferState();
+ mTextureState = new TextureState();
return true;
}
@@ -122,12 +118,6 @@ void Caches::initExtensions() {
}
void Caches::initConstraints() {
- GLint maxTextureUnits;
- glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
- if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) {
- ALOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT);
- }
-
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
}
@@ -216,8 +206,10 @@ void Caches::terminate() {
clearGarbage();
- mPixelBufferState.release();
-
+ delete mPixelBufferState;
+ mPixelBufferState = nullptr;
+ delete mTextureState;
+ mTextureState = nullptr;
mInitialized = false;
}
@@ -348,70 +340,6 @@ void Caches::flush(FlushMode mode) {
}
///////////////////////////////////////////////////////////////////////////////
-// Textures
-///////////////////////////////////////////////////////////////////////////////
-
-void Caches::activeTexture(GLuint textureUnit) {
- if (mTextureUnit != textureUnit) {
- glActiveTexture(gTextureUnits[textureUnit]);
- mTextureUnit = textureUnit;
- }
-}
-
-void Caches::resetActiveTexture() {
- mTextureUnit = -1;
-}
-
-void Caches::bindTexture(GLuint texture) {
- if (mBoundTextures[mTextureUnit] != texture) {
- glBindTexture(GL_TEXTURE_2D, texture);
- mBoundTextures[mTextureUnit] = texture;
- }
-}
-
-void Caches::bindTexture(GLenum target, GLuint texture) {
- if (target == GL_TEXTURE_2D) {
- bindTexture(texture);
- } else {
- // GLConsumer directly calls glBindTexture() with
- // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target
- // since the cached state could be stale
- glBindTexture(target, texture);
- }
-}
-
-void Caches::deleteTexture(GLuint texture) {
- // When glDeleteTextures() is called on a currently bound texture,
- // OpenGL ES specifies that the texture is then considered unbound
- // Consider the following series of calls:
- //
- // glGenTextures -> creates texture name 2
- // glBindTexture(2)
- // glDeleteTextures(2) -> 2 is now unbound
- // glGenTextures -> can return 2 again
- //
- // If we don't call glBindTexture(2) after the second glGenTextures
- // call, any texture operation will be performed on the default
- // texture (name=0)
-
- unbindTexture(texture);
-
- glDeleteTextures(1, &texture);
-}
-
-void Caches::resetBoundTextures() {
- memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint));
-}
-
-void Caches::unbindTexture(GLuint texture) {
- for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
- if (mBoundTextures[i] == texture) {
- mBoundTextures[i] = 0;
- }
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
// Tiling
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 8d23833..f6d3476 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -33,6 +33,7 @@
#include "PathCache.h"
#include "RenderBufferCache.h"
#include "renderstate/PixelBufferState.h"
+#include "renderstate/TextureState.h"
#include "ResourceCache.h"
#include "TessellationCache.h"
#include "TextDropShadowCache.h"
@@ -59,20 +60,6 @@ namespace uirenderer {
class GammaFontRenderer;
///////////////////////////////////////////////////////////////////////////////
-// Globals
-///////////////////////////////////////////////////////////////////////////////
-
-// GL ES 2.0 defines that at least 16 texture units must be supported
-#define REQUIRED_TEXTURE_UNITS_COUNT 3
-
-// Must define as many texture units as specified by REQUIRED_TEXTURE_UNITS_COUNT
-static const GLenum gTextureUnits[] = {
- GL_TEXTURE0,
- GL_TEXTURE1,
- GL_TEXTURE2
-};
-
-///////////////////////////////////////////////////////////////////////////////
// Caches
///////////////////////////////////////////////////////////////////////////////
@@ -155,49 +142,6 @@ public:
void deleteLayerDeferred(Layer* layer);
- /**
- * Activate the specified texture unit. The texture unit must
- * be specified using an integer number (0 for GL_TEXTURE0 etc.)
- */
- void activeTexture(GLuint textureUnit);
-
- /**
- * Invalidate the cached value of the active texture unit.
- */
- void resetActiveTexture();
-
- /**
- * Binds the specified texture as a GL_TEXTURE_2D texture.
- * All texture bindings must be performed with this method or
- * bindTexture(GLenum, GLuint).
- */
- void bindTexture(GLuint texture);
-
- /**
- * Binds the specified texture with the specified render target.
- * All texture bindings must be performed with this method or
- * bindTexture(GLuint).
- */
- void bindTexture(GLenum target, GLuint texture);
-
- /**
- * Deletes the specified texture and clears it from the cache
- * of bound textures.
- * All textures must be deleted using this method.
- */
- void deleteTexture(GLuint texture);
-
- /**
- * Signals that the cache of bound textures should be cleared.
- * Other users of the context may have altered which textures are bound.
- */
- void resetBoundTextures();
-
- /**
- * Clear the cache of bound textures.
- */
- void unbindTexture(GLuint texture);
-
void startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard);
void endTiling();
@@ -218,9 +162,6 @@ public:
void registerFunctors(uint32_t functorCount);
void unregisterFunctors(uint32_t functorCount);
- bool blend;
- GLenum lastSrcMode;
- GLenum lastDstMode;
Program* currentProgram;
bool drawDeferDisabled;
@@ -278,7 +219,8 @@ public:
int propertyAmbientShadowStrength;
int propertySpotShadowStrength;
- PixelBufferState& pixelBuffer() { return *mPixelBufferState; }
+ PixelBufferState& pixelBufferState() { return *mPixelBufferState; }
+ TextureState& textureState() { return *mTextureState; }
private:
enum OverdrawColorSet {
@@ -305,9 +247,8 @@ private:
RenderState* mRenderState;
- std::unique_ptr<PixelBufferState> mPixelBufferState; // TODO: move to RenderState
-
- GLuint mTextureUnit;
+ PixelBufferState* mPixelBufferState = nullptr; // TODO: move to RenderState
+ TextureState* mTextureState = nullptr; // TODO: move to RenderState
Extensions& mExtensions;
@@ -322,9 +263,6 @@ private:
uint32_t mFunctorsCount;
- // Caches texture bindings for the GL_TEXTURE_2D target
- GLuint mBoundTextures[REQUIRED_TEXTURE_UNITS_COUNT];
-
OverdrawColorSet mOverdrawDebugColorSet;
}; // class Caches
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index 45dc03a..7ad0683 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -42,14 +42,14 @@ public:
*/
static Canvas* create_canvas(SkCanvas* skiaCanvas);
- // TODO: enable HWUI to either create similar canvas wrapper or subclass
- // directly from Canvas
- //static Canvas* create_canvas(uirenderer::Renderer* renderer);
-
- // TODO: this is a temporary affordance until all necessary logic can be
- // moved within this interface! Further, the return value should
- // NOT be unref'd and is valid until this canvas is destroyed or a
- // new bitmap is set.
+ /**
+ * Provides a Skia SkCanvas interface that acts as a proxy to this Canvas.
+ * It is useful for testing and clients (e.g. Picture/Movie) that expect to
+ * draw their contents into an SkCanvas.
+ *
+ * Further, the returned SkCanvas should NOT be unref'd and is valid until
+ * this canvas is destroyed or a new bitmap is set.
+ */
virtual SkCanvas* asSkCanvas() = 0;
virtual void setBitmap(SkBitmap* bitmap, bool copyState) = 0;
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index d98d744..23181bc 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -59,6 +59,7 @@ DisplayListData* DisplayListRenderer::finishRecording() {
mPathMap.clear();
DisplayListData* data = mDisplayListData;
mDisplayListData = nullptr;
+ mSkiaCanvasProxy.reset(nullptr);
return data;
}
@@ -94,6 +95,15 @@ void DisplayListRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) {
mDisplayListData->functors.add(functor);
}
+SkCanvas* DisplayListRenderer::asSkCanvas() {
+ LOG_ALWAYS_FATAL_IF(!mDisplayListData,
+ "attempting to get an SkCanvas when we are not recording!");
+ if (!mSkiaCanvasProxy) {
+ mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this));
+ }
+ return mSkiaCanvasProxy.get();
+}
+
int DisplayListRenderer::save(SkCanvas::SaveFlags flags) {
addStateOp(new (alloc()) SaveOp((int) flags));
return mState.save((int) flags);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 527a0e5..bd0b3b7 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -28,6 +28,7 @@
#include "Canvas.h"
#include "CanvasState.h"
#include "DisplayList.h"
+#include "SkiaCanvasProxy.h"
#include "RenderNode.h"
#include "Renderer.h"
#include "ResourceCache.h"
@@ -136,10 +137,8 @@ public:
// ----------------------------------------------------------------------------
// android/graphics/Canvas interface
// ----------------------------------------------------------------------------
- virtual SkCanvas* asSkCanvas() override {
- LOG_ALWAYS_FATAL("DisplayListRenderer has no SkCanvas");
- return nullptr;
- }
+ virtual SkCanvas* asSkCanvas() override;
+
virtual void setBitmap(SkBitmap* bitmap, bool copyState) override {
LOG_ALWAYS_FATAL("DisplayListRenderer is not backed by a bitmap.");
}
@@ -244,6 +243,7 @@ public:
private:
CanvasState mState;
+ std::unique_ptr<SkiaCanvasProxy> mSkiaCanvasProxy;
enum DeferredBarrierType {
kBarrier_None,
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
index 12d9389..d637ec1 100644
--- a/libs/hwui/Dither.cpp
+++ b/libs/hwui/Dither.cpp
@@ -24,7 +24,10 @@ namespace uirenderer {
// Lifecycle
///////////////////////////////////////////////////////////////////////////////
-Dither::Dither(): mCaches(nullptr), mInitialized(false), mDitherTexture(0) {
+Dither::Dither(Caches& caches)
+ : mCaches(caches)
+ , mInitialized(false)
+ , mDitherTexture(0) {
}
void Dither::bindDitherTexture() {
@@ -32,7 +35,7 @@ void Dither::bindDitherTexture() {
bool useFloatTexture = Extensions::getInstance().hasFloatTextures();
glGenTextures(1, &mDitherTexture);
- mCaches->bindTexture(mDitherTexture);
+ mCaches.textureState().bindTexture(mDitherTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -71,13 +74,13 @@ void Dither::bindDitherTexture() {
mInitialized = true;
} else {
- mCaches->bindTexture(mDitherTexture);
+ mCaches.textureState().bindTexture(mDitherTexture);
}
}
void Dither::clear() {
if (mInitialized) {
- mCaches->deleteTexture(mDitherTexture);
+ mCaches.textureState().deleteTexture(mDitherTexture);
mInitialized = false;
}
}
@@ -87,10 +90,8 @@ void Dither::clear() {
///////////////////////////////////////////////////////////////////////////////
void Dither::setupProgram(Program* program, GLuint* textureUnit) {
- if (!mCaches) mCaches = &Caches::getInstance();
-
GLuint textureSlot = (*textureUnit)++;
- mCaches->activeTexture(textureSlot);
+ mCaches.textureState().activateTexture(textureSlot);
bindDitherTexture();
diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h
index 092ebf2..38633af 100644
--- a/libs/hwui/Dither.h
+++ b/libs/hwui/Dither.h
@@ -36,7 +36,7 @@ class Program;
*/
class Dither {
public:
- Dither();
+ Dither(Caches& caches);
void clear();
void setupProgram(Program* program, GLuint* textureUnit);
@@ -44,7 +44,7 @@ public:
private:
void bindDitherTexture();
- Caches* mCaches;
+ Caches& mCaches;
bool mInitialized;
GLuint mDitherTexture;
};
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 193474f..6dcd3e1 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -284,7 +284,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
uint32_t cacheWidth = cacheTexture->getWidth();
if (!cacheTexture->getPixelBuffer()) {
- Caches::getInstance().activeTexture(0);
+ Caches::getInstance().textureState().activateTexture(0);
// Large-glyph texture memory is allocated only as needed
cacheTexture->allocateTexture();
}
@@ -397,7 +397,7 @@ CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum for
CacheTexture* cacheTexture = new CacheTexture(width, height, format, kMaxNumberOfQuads);
if (allocate) {
- Caches::getInstance().activeTexture(0);
+ Caches::getInstance().textureState().activateTexture(0);
cacheTexture->allocateTexture();
cacheTexture->allocateMesh();
}
@@ -443,8 +443,8 @@ void checkTextureUpdateForCache(Caches& caches, Vector<CacheTexture*>& cacheText
if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
if (cacheTexture->getTextureId() != lastTextureId) {
lastTextureId = cacheTexture->getTextureId();
- caches.activeTexture(0);
- caches.bindTexture(lastTextureId);
+ caches.textureState().activateTexture(0);
+ caches.textureState().bindTexture(lastTextureId);
}
if (cacheTexture->upload()) {
@@ -470,7 +470,7 @@ void FontRenderer::checkTextureUpdate() {
checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId);
// Unbind any PBO we might have used to update textures
- caches.pixelBuffer().unbind();
+ caches.pixelBufferState().unbind();
// Reset to default unpack row length to avoid affecting texture
// uploads in other parts of the renderer
@@ -507,11 +507,11 @@ void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) {
forceRebind = renderState.meshState().unbindMeshBuffer();
}
- caches.activeTexture(0);
+ caches.textureState().activateTexture(0);
first = false;
}
- caches.bindTexture(texture->getTextureId());
+ caches.textureState().bindTexture(texture->getTextureId());
texture->setLinearFiltering(mLinearFiltering, false);
TextureVertex* mesh = texture->mesh();
@@ -649,7 +649,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co
Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions);
// Unbind any PBO we might have used
- Caches::getInstance().pixelBuffer().unbind();
+ Caches::getInstance().pixelBufferState().unbind();
blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
}
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 0987d9b..416b0b3 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -285,7 +285,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, Texture*
memcpy(pixels + rowBytes, pixels, rowBytes);
glGenTextures(1, &texture->id);
- Caches::getInstance().bindTexture(texture->id);
+ Caches::getInstance().textureState().bindTexture(texture->id);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
if (mUseFloatTexture) {
diff --git a/libs/hwui/Image.cpp b/libs/hwui/Image.cpp
index edf3930..a31c546 100644
--- a/libs/hwui/Image.cpp
+++ b/libs/hwui/Image.cpp
@@ -39,7 +39,7 @@ Image::Image(sp<GraphicBuffer> buffer) {
} else {
// Create a 2D texture to sample from the EGLImage
glGenTextures(1, &mTexture);
- Caches::getInstance().bindTexture(mTexture);
+ Caches::getInstance().textureState().bindTexture(mTexture);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, mImage);
GLenum status = GL_NO_ERROR;
@@ -54,7 +54,7 @@ Image::~Image() {
eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), mImage);
mImage = EGL_NO_IMAGE_KHR;
- Caches::getInstance().deleteTexture(mTexture);
+ Caches::getInstance().textureState().deleteTexture(mTexture);
mTexture = 0;
}
}
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 7a4b830..7a026ef 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -134,7 +134,7 @@ bool Layer::resize(const uint32_t width, const uint32_t height) {
setSize(desiredWidth, desiredHeight);
if (fbo) {
- caches.activeTexture(0);
+ caches.textureState().activateTexture(0);
bindTexture();
allocateTexture();
@@ -195,7 +195,7 @@ void Layer::setColorFilter(SkColorFilter* filter) {
void Layer::bindTexture() const {
if (texture.id) {
- caches.bindTexture(renderTarget, texture.id);
+ caches.textureState().bindTexture(renderTarget, texture.id);
}
}
@@ -219,7 +219,7 @@ void Layer::deleteTexture() {
}
void Layer::clearTexture() {
- caches.unbindTexture(texture.id);
+ caches.textureState().unbindTexture(texture.id);
texture.id = 0;
}
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 076251f..d2f9a94 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -196,7 +196,7 @@ Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width
return nullptr;
}
- caches.activeTexture(0);
+ caches.textureState().activateTexture(0);
Layer* layer = caches.layerCache.get(renderState, width, height);
if (!layer) {
ALOGW("Could not obtain a layer");
@@ -283,7 +283,7 @@ Layer* LayerRenderer::createTextureLayer(RenderState& renderState) {
layer->region.clear();
layer->setRenderTarget(GL_NONE); // see ::updateTextureLayer()
- Caches::getInstance().activeTexture(0);
+ Caches::getInstance().textureState().activateTexture(0);
layer->generateTexture();
return layer;
@@ -412,8 +412,8 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap*
glGenTextures(1, &texture);
if ((error = glGetError()) != GL_NO_ERROR) goto error;
- caches.activeTexture(0);
- caches.bindTexture(texture);
+ caches.textureState().activateTexture(0);
+ caches.textureState().bindTexture(texture);
glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
@@ -475,7 +475,7 @@ error:
renderState.bindFramebuffer(previousFbo);
layer->setAlpha(alpha, mode);
layer->setFbo(previousLayerFbo);
- caches.deleteTexture(texture);
+ caches.textureState().deleteTexture(texture);
caches.fboCache.put(fbo);
renderState.setViewport(previousViewportWidth, previousViewportHeight);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 76525e0..3ee9808 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -70,55 +70,6 @@ static GLenum getFilter(const SkPaint* paint) {
// Globals
///////////////////////////////////////////////////////////////////////////////
-/**
- * Structure mapping Skia xfermodes to OpenGL blending factors.
- */
-struct Blender {
- SkXfermode::Mode mode;
- GLenum src;
- GLenum dst;
-}; // struct Blender
-
-// In this array, the index of each Blender equals the value of the first
-// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
-static const Blender gBlends[] = {
- { SkXfermode::kClear_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO },
- { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE },
- { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
- { SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO },
- { SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA },
- { SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
- { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
- { SkXfermode::kModulate_Mode, GL_ZERO, GL_SRC_COLOR },
- { SkXfermode::kScreen_Mode, GL_ONE, GL_ONE_MINUS_SRC_COLOR }
-};
-
-// This array contains the swapped version of each SkXfermode. For instance
-// this array's SrcOver blending mode is actually DstOver. You can refer to
-// createLayer() for more information on the purpose of this array.
-static const Blender gBlendsSwap[] = {
- { SkXfermode::kClear_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE },
- { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO },
- { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
- { SkXfermode::kDstOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrcIn_Mode, GL_ZERO, GL_SRC_ALPHA },
- { SkXfermode::kDstIn_Mode, GL_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrcOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
- { SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
- { SkXfermode::kModulate_Mode, GL_DST_COLOR, GL_ZERO },
- { SkXfermode::kScreen_Mode, GL_ONE_MINUS_DST_COLOR, GL_ONE }
-};
///////////////////////////////////////////////////////////////////////////////
// Functions
@@ -233,7 +184,7 @@ void OpenGLRenderer::prepareDirty(float left, float top,
// for each layer and wait until the first drawing command
// to start the frame
if (currentSnapshot()->fbo == 0) {
- syncState();
+ mRenderState.blend().syncEnabled();
updateLayers();
} else {
startFrame();
@@ -266,14 +217,6 @@ void OpenGLRenderer::clear(float left, float top, float right, float bottom, boo
mRenderState.scissor().reset();
}
-void OpenGLRenderer::syncState() {
- if (mCaches.blend) {
- glEnable(GL_BLEND);
- } else {
- glDisable(GL_BLEND);
- }
-}
-
void OpenGLRenderer::startTilingCurrentClip(bool opaque, bool expand) {
if (!mSuppressTiling) {
const Snapshot* snapshot = currentSnapshot();
@@ -558,7 +501,7 @@ void OpenGLRenderer::cancelLayerUpdate(Layer* layer) {
void OpenGLRenderer::flushLayerUpdates() {
ATRACE_NAME("Update HW Layers");
- syncState();
+ mRenderState.blend().syncEnabled();
updateLayers();
flushLayers();
// Wait for all the layer updates to be executed
@@ -755,7 +698,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto
return false;
}
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Layer* layer = mCaches.layerCache.get(mRenderState, bounds.getWidth(), bounds.getHeight());
if (!layer) {
return false;
@@ -895,7 +838,7 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto
mRenderState.meshState().unbindMeshBuffer();
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
// When the layer is stored in an FBO, we can save a bit of fillrate by
// drawing only the dirty region
@@ -1897,13 +1840,13 @@ void OpenGLRenderer::setupDrawSimpleMesh() {
}
void OpenGLRenderer::setupDrawTexture(GLuint texture) {
- if (texture) bindTexture(texture);
+ if (texture) mCaches.textureState().bindTexture(texture);
mTextureUnit++;
mRenderState.meshState().enableTexCoordsVertexArray();
}
void OpenGLRenderer::setupDrawExternalTexture(GLuint texture) {
- bindExternalTexture(texture);
+ mCaches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
mTextureUnit++;
mRenderState.meshState().enableTexCoordsVertexArray();
}
@@ -2049,7 +1992,7 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top,
void OpenGLRenderer::drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
int bitmapCount, TextureVertex* vertices, bool pureTranslate,
const Rect& bounds, const SkPaint* paint) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
if (!texture) return;
@@ -2080,7 +2023,7 @@ void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
return;
}
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Texture* texture = getTexture(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -2121,7 +2064,7 @@ void OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int m
colors = tempColors.get();
}
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap);
const UvMapper& mapper(getMapper(texture));
@@ -2216,7 +2159,7 @@ void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap,
return;
}
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Texture* texture = getTexture(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -2316,7 +2259,7 @@ void OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Patch* mesh,
}
if (CC_LIKELY(mesh && mesh->verticesCount > 0)) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -2370,7 +2313,7 @@ void OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Patch* mesh,
*/
void OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
TextureVertex* vertices, uint32_t indexCount, const SkPaint* paint) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -2555,7 +2498,7 @@ void OpenGLRenderer::drawRoundRect(float left, float top, float right, float bot
}
if (p->getPathEffect() != nullptr) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
const PathTexture* texture = mCaches.pathCache.getRoundRect(
right - left, bottom - top, rx, ry, p);
drawShape(left, top, texture, p);
@@ -2573,7 +2516,7 @@ void OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p
return;
}
if (p->getPathEffect() != nullptr) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
const PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
drawShape(x - radius, y - radius, texture, p);
} else {
@@ -2596,7 +2539,7 @@ void OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
}
if (p->getPathEffect() != nullptr) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
const PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p);
drawShape(left, top, texture, p);
} else {
@@ -2620,7 +2563,7 @@ void OpenGLRenderer::drawArc(float left, float top, float right, float bottom,
// TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != nullptr || useCenter) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
const PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top,
startAngle, sweepAngle, useCenter, p);
drawShape(left, top, texture, p);
@@ -2657,7 +2600,7 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
// only fill style is supported by drawConvexPath, since others have to handle joins
if (p->getPathEffect() != nullptr || p->getStrokeJoin() != SkPaint::kMiter_Join ||
p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
const PathTexture* texture =
mCaches.pathCache.getRect(right - left, bottom - top, p);
drawShape(left, top, texture, p);
@@ -2686,7 +2629,7 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
int bytesCount, int count, const float* positions,
FontRenderer& fontRenderer, int alpha, float x, float y) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
TextShadow textShadow;
if (!getTextShadow(paint, &textShadow)) {
@@ -3000,7 +2943,7 @@ void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
void OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) {
if (mState.currentlyIgnored()) return;
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
const PathTexture* texture = mCaches.pathCache.get(path, paint);
if (!texture) return;
@@ -3045,7 +2988,7 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
updateLayer(layer, true);
mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
if (CC_LIKELY(!layer->region.isEmpty())) {
if (layer->region.isRect()) {
@@ -3484,33 +3427,16 @@ void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
description.framebufferMode = mode;
description.swapSrcDst = swapSrcDst;
- if (mCaches.blend) {
- glDisable(GL_BLEND);
- mCaches.blend = false;
- }
-
+ mRenderState.blend().disable();
return;
} else {
mode = SkXfermode::kSrcOver_Mode;
}
}
-
- if (!mCaches.blend) {
- glEnable(GL_BLEND);
- }
-
- GLenum sourceMode = swapSrcDst ? gBlendsSwap[mode].src : gBlends[mode].src;
- GLenum destMode = swapSrcDst ? gBlendsSwap[mode].dst : gBlends[mode].dst;
-
- if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) {
- glBlendFunc(sourceMode, destMode);
- mCaches.lastSrcMode = sourceMode;
- mCaches.lastDstMode = destMode;
- }
- } else if (mCaches.blend) {
- glDisable(GL_BLEND);
+ mRenderState.blend().enable(mode, swapSrcDst);
+ } else {
+ mRenderState.blend().disable();
}
- mCaches.blend = blend;
}
bool OpenGLRenderer::useProgram(Program* program) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 94054ff..cf6f0c8 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -541,12 +541,6 @@ private:
void discardFramebuffer(float left, float top, float right, float bottom);
/**
- * Ensures the state of the renderer is the same as the state of
- * the GL context.
- */
- void syncState();
-
- /**
* Tells the GPU what part of the screen is about to be redrawn.
* This method will use the current layer space clip rect.
* This method needs to be invoked every time getTargetFbo() is
@@ -852,22 +846,6 @@ private:
bool canSkipText(const SkPaint* paint) const;
/**
- * Binds the specified texture. The texture unit must have been selected
- * prior to calling this method.
- */
- inline void bindTexture(GLuint texture) {
- mCaches.bindTexture(texture);
- }
-
- /**
- * Binds the specified EGLImage texture. The texture unit must have been selected
- * prior to calling this method.
- */
- inline void bindExternalTexture(GLuint texture) {
- mCaches.bindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
- }
-
- /**
* Enable or disable blending as necessary. This function sets the appropriate
* blend function based on the specified xfermode.
*/
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index cc7f88d..d6eff85 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -230,7 +230,7 @@ void PathCache::removeTexture(PathTexture* texture) {
}
if (texture->id) {
- Caches::getInstance().deleteTexture(texture->id);
+ Caches::getInstance().textureState().deleteTexture(texture->id);
}
delete texture;
}
@@ -312,7 +312,7 @@ void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) {
glGenTextures(1, &texture->id);
- Caches::getInstance().bindTexture(texture->id);
+ Caches::getInstance().textureState().bindTexture(texture->id);
// Textures are Alpha8
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp
index 62eb68c..9665a68 100644
--- a/libs/hwui/PixelBuffer.cpp
+++ b/libs/hwui/PixelBuffer.cpp
@@ -101,9 +101,9 @@ GpuPixelBuffer::GpuPixelBuffer(GLenum format,
, mCaches(Caches::getInstance()){
glGenBuffers(1, &mBuffer);
- mCaches.pixelBuffer().bind(mBuffer);
+ mCaches.pixelBufferState().bind(mBuffer);
glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), nullptr, GL_DYNAMIC_DRAW);
- mCaches.pixelBuffer().unbind();
+ mCaches.pixelBufferState().unbind();
}
GpuPixelBuffer::~GpuPixelBuffer() {
@@ -112,7 +112,7 @@ GpuPixelBuffer::~GpuPixelBuffer() {
uint8_t* GpuPixelBuffer::map(AccessMode mode) {
if (mAccessMode == kAccessMode_None) {
- mCaches.pixelBuffer().bind(mBuffer);
+ mCaches.pixelBufferState().bind(mBuffer);
mMappedPointer = (uint8_t*) glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode);
#if DEBUG_OPENGL
if (!mMappedPointer) {
@@ -131,7 +131,7 @@ uint8_t* GpuPixelBuffer::map(AccessMode mode) {
void GpuPixelBuffer::unmap() {
if (mAccessMode != kAccessMode_None) {
if (mMappedPointer) {
- mCaches.pixelBuffer().bind(mBuffer);
+ mCaches.pixelBufferState().bind(mBuffer);
GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
if (status == GL_FALSE) {
ALOGE("Corrupted GPU pixel buffer");
@@ -148,7 +148,7 @@ uint8_t* GpuPixelBuffer::getMappedPointer() const {
void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
// If the buffer is not mapped, unmap() will not bind it
- mCaches.pixelBuffer().bind(mBuffer);
+ mCaches.pixelBufferState().bind(mBuffer);
unmap();
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat,
GL_UNSIGNED_BYTE, reinterpret_cast<void*>(offset));
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
new file mode 100644
index 0000000..de5f91c
--- /dev/null
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -0,0 +1,347 @@
+/*
+ * 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.
+ */
+
+#include "SkiaCanvasProxy.h"
+
+#include <cutils/log.h>
+#include <SkPatchUtils.h>
+
+namespace android {
+namespace uirenderer {
+
+SkiaCanvasProxy::SkiaCanvasProxy(Canvas* canvas)
+ : INHERITED(canvas->width(), canvas->height())
+ , mCanvas(canvas) {}
+
+void SkiaCanvasProxy::onDrawPaint(const SkPaint& paint) {
+ mCanvas->drawPaint(paint);
+}
+
+void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPoint pts[],
+ const SkPaint& paint) {
+ // convert the SkPoints into floats
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ const size_t floatCount = count << 1;
+ const float* floatArray = &pts[0].fX;
+
+ switch (pointMode) {
+ case kPoints_PointMode: {
+ mCanvas->drawPoints(floatArray, floatCount, paint);
+ break;
+ }
+ case kLines_PointMode: {
+ mCanvas->drawLines(floatArray, floatCount, paint);
+ break;
+ }
+ case kPolygon_PointMode: {
+ SkPaint strokedPaint(paint);
+ strokedPaint.setStyle(SkPaint::kStroke_Style);
+
+ SkPath path;
+ for (size_t i = 0; i < count - 1; i++) {
+ path.moveTo(pts[i]);
+ path.lineTo(pts[i+1]);
+ this->drawPath(path, strokedPaint);
+ path.rewind();
+ }
+ break;
+ }
+ default:
+ LOG_ALWAYS_FATAL("Unknown point type");
+ }
+}
+
+void SkiaCanvasProxy::onDrawOval(const SkRect& rect, const SkPaint& paint) {
+ mCanvas->drawOval(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
+}
+
+void SkiaCanvasProxy::onDrawRect(const SkRect& rect, const SkPaint& paint) {
+ mCanvas->drawRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
+}
+
+void SkiaCanvasProxy::onDrawRRect(const SkRRect& roundRect, const SkPaint& paint) {
+ if (!roundRect.isComplex()) {
+ const SkRect& rect = roundRect.rect();
+ SkVector radii = roundRect.getSimpleRadii();
+ mCanvas->drawRoundRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
+ radii.fX, radii.fY, paint);
+ } else {
+ SkPath path;
+ path.addRRect(roundRect);
+ mCanvas->drawPath(path, paint);
+ }
+}
+
+void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) {
+ mCanvas->drawPath(path, paint);
+}
+
+void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+ const SkPaint* paint) {
+ mCanvas->drawBitmap(bitmap, left, top, paint);
+}
+
+void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* srcPtr,
+ const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags) {
+ SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(bitmap.width(), bitmap.height());
+ mCanvas->drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
+ dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
+}
+
+void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+ const SkRect& dst, const SkPaint*) {
+ //TODO make nine-patch drawing a method on Canvas.h
+ SkDEBUGFAIL("SkiaCanvasProxy::onDrawBitmapNine is not yet supported");
+}
+
+void SkiaCanvasProxy::onDrawSprite(const SkBitmap& bitmap, int left, int top,
+ const SkPaint* paint) {
+ mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
+ mCanvas->setMatrix(SkMatrix::I());
+ mCanvas->drawBitmap(bitmap, left, top, paint);
+ mCanvas->restore();
+}
+
+void SkiaCanvasProxy::onDrawVertices(VertexMode mode, int vertexCount, const SkPoint vertices[],
+ const SkPoint texs[], const SkColor colors[], SkXfermode*, const uint16_t indices[],
+ int indexCount, const SkPaint& paint) {
+ // convert the SkPoints into floats
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ const int floatCount = vertexCount << 1;
+ const float* vArray = &vertices[0].fX;
+ const float* tArray = (texs) ? &texs[0].fX : NULL;
+ const int* cArray = (colors) ? (int*)colors : NULL;
+ mCanvas->drawVertices(mode, floatCount, vArray, tArray, cArray, indices, indexCount, paint);
+}
+
+SkSurface* SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
+ SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported");
+ return NULL;
+}
+
+void SkiaCanvasProxy::willSave() {
+ mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
+}
+
+SkCanvas::SaveLayerStrategy SkiaCanvasProxy::willSaveLayer(const SkRect* rectPtr,
+ const SkPaint* paint, SaveFlags flags) {
+ SkRect rect;
+ if (rectPtr) {
+ rect = *rectPtr;
+ } else if(!mCanvas->getClipBounds(&rect)) {
+ rect = SkRect::MakeEmpty();
+ }
+ mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint, flags);
+ return SkCanvas::kNoLayer_SaveLayerStrategy;
+}
+
+void SkiaCanvasProxy::willRestore() {
+ mCanvas->restore();
+}
+
+void SkiaCanvasProxy::didConcat(const SkMatrix& matrix) {
+ mCanvas->concat(matrix);
+}
+
+void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) {
+ mCanvas->setMatrix(matrix);
+}
+
+void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) {
+ SkPath path;
+ path.addRRect(outer);
+ path.addRRect(inner);
+ path.setFillType(SkPath::kEvenOdd_FillType);
+ this->drawPath(path, paint);
+}
+
+/**
+ * Utility class that converts the incoming text & paint from the given encoding
+ * into glyphIDs.
+ */
+class GlyphIDConverter {
+public:
+ GlyphIDConverter(const void* text, size_t byteLength, const SkPaint& origPaint) {
+ paint = origPaint;
+ if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) {
+ glyphIDs = (uint16_t*)text;
+ count = byteLength >> 1;
+ } else {
+ storage.reset(byteLength); // ensures space for one glyph per ID given UTF8 encoding.
+ glyphIDs = storage.get();
+ count = paint.textToGlyphs(text, byteLength, storage.get());
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ }
+ }
+
+ SkPaint paint;
+ uint16_t* glyphIDs;
+ int count;
+private:
+ SkAutoSTMalloc<32, uint16_t> storage;
+};
+
+void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+ const SkPaint& origPaint) {
+ // convert to glyphIDs if necessary
+ GlyphIDConverter glyphs(text, byteLength, origPaint);
+
+ // compute the glyph positions
+ SkAutoSTMalloc<32, SkPoint> pointStorage(glyphs.count);
+ SkAutoSTMalloc<32, SkScalar> glyphWidths(glyphs.count);
+ glyphs.paint.getTextWidths(glyphs.glyphIDs, glyphs.count << 1, glyphWidths.get());
+
+ // compute conservative bounds
+ // NOTE: We could call the faster paint.getFontBounds for a less accurate,
+ // but even more conservative bounds if this is too slow.
+ SkRect bounds;
+ glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
+
+ // adjust for non-left alignment
+ if (glyphs.paint.getTextAlign() != SkPaint::kLeft_Align) {
+ SkScalar stop = 0;
+ for (int i = 0; i < glyphs.count; i++) {
+ stop += glyphWidths[i];
+ }
+ if (glyphs.paint.getTextAlign() == SkPaint::kCenter_Align) {
+ stop = SkScalarHalf(stop);
+ }
+ if (glyphs.paint.isVerticalText()) {
+ y -= stop;
+ } else {
+ x -= stop;
+ }
+ }
+
+ // setup the first glyph position and adjust bounds if needed
+ if (mCanvas->drawTextAbsolutePos()) {
+ bounds.offset(x,y);
+ pointStorage[0].set(x, y);
+ } else {
+ pointStorage[0].set(0, 0);
+ }
+
+ // setup the remaining glyph positions
+ if (glyphs.paint.isVerticalText()) {
+ for (int i = 1; i < glyphs.count; i++) {
+ pointStorage[i].set(x, glyphWidths[i-1] + pointStorage[i-1].fY);
+ }
+ } else {
+ for (int i = 1; i < glyphs.count; i++) {
+ pointStorage[i].set(glyphWidths[i-1] + pointStorage[i-1].fX, y);
+ }
+ }
+
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ mCanvas->drawText(glyphs.glyphIDs, &pointStorage[0].fX, glyphs.count, glyphs.paint,
+ x, y, bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
+}
+
+void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+ const SkPaint& origPaint) {
+ // convert to glyphIDs if necessary
+ GlyphIDConverter glyphs(text, byteLength, origPaint);
+
+ // convert to relative positions if necessary
+ int x, y;
+ const SkPoint* posArray;
+ SkAutoSTMalloc<32, SkPoint> pointStorage;
+ if (mCanvas->drawTextAbsolutePos()) {
+ x = 0;
+ y = 0;
+ posArray = pos;
+ } else {
+ x = pos[0].fX;
+ y = pos[0].fY;
+ posArray = pointStorage.reset(glyphs.count);
+ for (int i = 0; i < glyphs.count; i++) {
+ pointStorage[i].fX = pos[i].fX- x;
+ pointStorage[i].fY = pos[i].fY- y;
+ }
+ }
+
+ // compute conservative bounds
+ // NOTE: We could call the faster paint.getFontBounds for a less accurate,
+ // but even more conservative bounds if this is too slow.
+ SkRect bounds;
+ glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
+
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ mCanvas->drawText(glyphs.glyphIDs, &posArray[0].fX, glyphs.count, glyphs.paint, x, y,
+ bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
+}
+
+void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
+ SkScalar constY, const SkPaint& paint) {
+ const size_t pointCount = byteLength >> 1;
+ SkAutoSTMalloc<32, SkPoint> storage(pointCount);
+ SkPoint* pts = storage.get();
+ for (size_t i = 0; i < pointCount; i++) {
+ pts[i].set(xpos[i], constY);
+ }
+ this->onDrawPosText(text, byteLength, pts, paint);
+}
+
+void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+ const SkMatrix* matrix, const SkPaint& origPaint) {
+ // convert to glyphIDs if necessary
+ GlyphIDConverter glyphs(text, byteLength, origPaint);
+ mCanvas->drawTextOnPath(glyphs.glyphIDs, glyphs.count, path, 0, 0, glyphs.paint);
+}
+
+void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+ const SkPaint& paint) {
+ SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextBlob is not supported");
+}
+
+void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
+ const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
+ SkPatchUtils::VertexData data;
+
+ SkMatrix matrix;
+ mCanvas->getMatrix(&matrix);
+ SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix);
+
+ // It automatically adjusts lodX and lodY in case it exceeds the number of indices.
+ // If it fails to generate the vertices, then we do not draw.
+ if (SkPatchUtils::getVertexData(&data, cubics, colors, texCoords, lod.width(), lod.height())) {
+ this->drawVertices(SkCanvas::kTriangles_VertexMode, data.fVertexCount, data.fPoints,
+ data.fTexCoords, data.fColors, xmode, data.fIndices, data.fIndexCount,
+ paint);
+ }
+}
+
+void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle) {
+ mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op);
+}
+
+void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkRegion::Op op, ClipEdgeStyle) {
+ SkPath path;
+ path.addRRect(roundRect);
+ mCanvas->clipPath(&path, op);
+}
+
+void SkiaCanvasProxy::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle) {
+ mCanvas->clipPath(&path, op);
+}
+
+void SkiaCanvasProxy::onClipRegion(const SkRegion& region, SkRegion::Op op) {
+ mCanvas->clipRegion(&region, op);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
new file mode 100644
index 0000000..4322fcf
--- /dev/null
+++ b/libs/hwui/SkiaCanvasProxy.h
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+#ifndef SkiaCanvasProxy_DEFINED
+#define SkiaCanvasProxy_DEFINED
+
+#include <cutils/compiler.h>
+#include <SkCanvas.h>
+
+#include "Canvas.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * This class serves as a proxy between Skia's SkCanvas and Android Framework's
+ * Canvas. The class does not maintain any state and will pass through any request
+ * directly to the Canvas provided in the constructor.
+ *
+ * Upon construction it is expected that the provided Canvas has already been
+ * prepared for recording and will continue to be in the recording state while
+ * this proxy class is being used.
+ */
+class ANDROID_API SkiaCanvasProxy : public SkCanvas {
+public:
+ SkiaCanvasProxy(Canvas* canvas);
+ virtual ~SkiaCanvasProxy() {}
+
+protected:
+
+ virtual SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
+
+ virtual void willSave() override;
+ virtual SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
+ virtual void willRestore() override;
+
+ virtual void didConcat(const SkMatrix&) override;
+ virtual void didSetMatrix(const SkMatrix&) override;
+
+ virtual void onDrawPaint(const SkPaint& paint) override;
+ virtual void onDrawPoints(PointMode, size_t count, const SkPoint pts[],
+ const SkPaint&) override;
+ virtual void onDrawOval(const SkRect&, const SkPaint&) override;
+ virtual void onDrawRect(const SkRect&, const SkPaint&) override;
+ virtual void onDrawRRect(const SkRRect&, const SkPaint&) override;
+ virtual void onDrawPath(const SkPath& path, const SkPaint&) override;
+ virtual void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
+ const SkPaint*) override;
+ virtual void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst,
+ const SkPaint* paint, DrawBitmapRectFlags flags) override;
+ virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+ const SkRect& dst, const SkPaint*) override;
+ virtual void onDrawSprite(const SkBitmap&, int left, int top,
+ const SkPaint*) override;
+ virtual void onDrawVertices(VertexMode, int vertexCount, const SkPoint vertices[],
+ const SkPoint texs[], const SkColor colors[], SkXfermode*,
+ const uint16_t indices[], int indexCount,
+ const SkPaint&) override;
+
+ virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
+
+ virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+ const SkPaint&) override;
+ virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+ const SkPaint&) override;
+ virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
+ SkScalar constY, const SkPaint&) override;
+ virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+ const SkMatrix* matrix, const SkPaint&) override;
+ virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+ const SkPaint& paint) override;
+
+ virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
+ const SkPoint texCoords[4], SkXfermode* xmode,
+ const SkPaint& paint) override;
+
+ virtual void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
+ virtual void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
+ virtual void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
+ virtual void onClipRegion(const SkRegion&, SkRegion::Op) override;
+
+private:
+ Canvas* mCanvas;
+
+ typedef SkCanvas INHERITED;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // SkiaCanvasProxy_DEFINED
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 2c09344..e13c861 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -57,7 +57,7 @@ static inline void bindUniformColor(int slot, uint32_t color) {
}
static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
- caches->bindTexture(texture->id);
+ caches->textureState().bindTexture(texture->id);
texture->setWrapST(wrapS, wrapT);
}
@@ -176,7 +176,7 @@ void SkiaLayerShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
}
GLuint textureSlot = (*textureUnit)++;
- caches->activeTexture(textureSlot);
+ caches->textureState().activateTexture(textureSlot);
const float width = layer->getWidth();
const float height = layer->getHeight();
@@ -270,7 +270,7 @@ void SkiaBitmapShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
}
GLuint textureSlot = (*textureUnit)++;
- Caches::getInstance().activeTexture(textureSlot);
+ Caches::getInstance().textureState().activateTexture(textureSlot);
BitmapShaderInfo shaderInfo;
if (!bitmapShaderHelper(caches, nullptr, &shaderInfo, extensions, bitmap, xy)) {
@@ -392,7 +392,7 @@ void SkiaGradientShader::setupProgram(Caches* caches, const mat4& modelViewMatri
shader.asAGradient(&gradInfo);
}
GLuint textureSlot = (*textureUnit)++;
- caches->activeTexture(textureSlot);
+ caches->textureState().activateTexture(textureSlot);
#ifndef SK_SCALAR_IS_FLOAT
#error Need to convert gradInfo.fColorOffsets to float!
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index 4ec298d..c2e88f3 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -207,7 +207,7 @@ ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* text,
glGenTextures(1, &texture->id);
- caches.bindTexture(texture->id);
+ caches.textureState().bindTexture(texture->id);
// Textures are Alpha8
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 58fd972..512f5cf 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -24,18 +24,44 @@
namespace android {
namespace uirenderer {
-Texture::Texture(): id(0), generation(0), blend(false), width(0), height(0),
- cleanup(false), bitmapSize(0), mipMap(false), uvMapper(nullptr), isInUse(false),
- mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
- mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
- mFirstFilter(true), mFirstWrap(true), mCaches(Caches::getInstance()) {
+Texture::Texture()
+ : id(0)
+ , generation(0)
+ , blend(false)
+ , width(0)
+ , height(0)
+ , cleanup(false)
+ , bitmapSize(0)
+ , mipMap(false)
+ , uvMapper(nullptr)
+ , isInUse(false)
+ , mWrapS(GL_CLAMP_TO_EDGE)
+ , mWrapT(GL_CLAMP_TO_EDGE)
+ , mMinFilter(GL_NEAREST)
+ , mMagFilter(GL_NEAREST)
+ , mFirstFilter(true)
+ , mFirstWrap(true)
+ , mCaches(Caches::getInstance()) {
}
-Texture::Texture(Caches& caches): id(0), generation(0), blend(false), width(0), height(0),
- cleanup(false), bitmapSize(0), mipMap(false), uvMapper(nullptr), isInUse(false),
- mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
- mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
- mFirstFilter(true), mFirstWrap(true), mCaches(caches) {
+Texture::Texture(Caches& caches)
+ : id(0)
+ , generation(0)
+ , blend(false)
+ , width(0)
+ , height(0)
+ , cleanup(false)
+ , bitmapSize(0)
+ , mipMap(false)
+ , uvMapper(nullptr)
+ , isInUse(false)
+ , mWrapS(GL_CLAMP_TO_EDGE)
+ , mWrapT(GL_CLAMP_TO_EDGE)
+ , mMinFilter(GL_NEAREST)
+ , mMagFilter(GL_NEAREST)
+ , mFirstFilter(true)
+ , mFirstWrap(true)
+ , mCaches(caches) {
}
void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force,
@@ -48,7 +74,7 @@ void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force
mWrapT = wrapT;
if (bindTexture) {
- mCaches.bindTexture(renderTarget, id);
+ mCaches.textureState().bindTexture(renderTarget, id);
}
glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
@@ -66,7 +92,7 @@ void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool for
mMagFilter = mag;
if (bindTexture) {
- mCaches.bindTexture(renderTarget, id);
+ mCaches.textureState().bindTexture(renderTarget, id);
}
if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
@@ -77,7 +103,7 @@ void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool for
}
void Texture::deleteTexture() const {
- mCaches.deleteTexture(id);
+ mCaches.textureState().deleteTexture(id);
}
}; // namespace uirenderer
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 524f206..fe8fb5b 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -296,7 +296,7 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo
texture->width = bitmap->width();
texture->height = bitmap->height();
- Caches::getInstance().bindTexture(texture->id);
+ Caches::getInstance().textureState().bindTexture(texture->id);
switch (bitmap->colorType()) {
case kAlpha_8_SkColorType:
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index 128e392..53fa0dc 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -157,7 +157,7 @@ void CacheTexture::releaseTexture() {
mTexture = nullptr;
}
if (mTextureId) {
- mCaches.deleteTexture(mTextureId);
+ mCaches.textureState().deleteTexture(mTextureId);
mTextureId = 0;
}
mDirty = false;
@@ -169,7 +169,7 @@ void CacheTexture::setLinearFiltering(bool linearFiltering, bool bind) {
mLinearFiltering = linearFiltering;
const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
- if (bind) mCaches.bindTexture(getTextureId());
+ if (bind) mCaches.textureState().bindTexture(getTextureId());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
}
@@ -189,7 +189,7 @@ void CacheTexture::allocateTexture() {
if (!mTextureId) {
glGenTextures(1, &mTextureId);
- mCaches.bindTexture(mTextureId);
+ mCaches.textureState().bindTexture(mTextureId);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Initialize texture dimensions
glTexImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
diff --git a/libs/hwui/renderstate/Blend.cpp b/libs/hwui/renderstate/Blend.cpp
new file mode 100644
index 0000000..3e7b721
--- /dev/null
+++ b/libs/hwui/renderstate/Blend.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+#include <renderstate/Blend.h>
+#include "Program.h"
+
+#include "ShadowTessellator.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Structure mapping Skia xfermodes to OpenGL blending factors.
+ */
+struct Blender {
+ SkXfermode::Mode mode;
+ GLenum src;
+ GLenum dst;
+};
+
+// In this array, the index of each Blender equals the value of the first
+// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
+const Blender kBlends[] = {
+ { SkXfermode::kClear_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO },
+ { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE },
+ { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
+ { SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA },
+ { SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
+ { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
+ { SkXfermode::kModulate_Mode, GL_ZERO, GL_SRC_COLOR },
+ { SkXfermode::kScreen_Mode, GL_ONE, GL_ONE_MINUS_SRC_COLOR }
+};
+
+// This array contains the swapped version of each SkXfermode. For instance
+// this array's SrcOver blending mode is actually DstOver. You can refer to
+// createLayer() for more information on the purpose of this array.
+const Blender kBlendsSwap[] = {
+ { SkXfermode::kClear_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE },
+ { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO },
+ { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
+ { SkXfermode::kDstOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kSrcIn_Mode, GL_ZERO, GL_SRC_ALPHA },
+ { SkXfermode::kDstIn_Mode, GL_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kSrcOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kDstOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
+ { SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
+ { SkXfermode::kModulate_Mode, GL_DST_COLOR, GL_ZERO },
+ { SkXfermode::kScreen_Mode, GL_ONE_MINUS_DST_COLOR, GL_ONE }
+};
+
+Blend::Blend()
+ : mEnabled(false)
+ , mSrcMode(GL_ZERO)
+ , mDstMode(GL_ZERO) {
+ // gl blending off by default
+}
+
+void Blend::enable(SkXfermode::Mode mode, bool swapSrcDst) {
+ // enable
+ if (!mEnabled) {
+ glEnable(GL_BLEND);
+ mEnabled = true;
+ }
+
+ // select blend mode
+ GLenum sourceMode = swapSrcDst ? kBlendsSwap[mode].src : kBlends[mode].src;
+ GLenum destMode = swapSrcDst ? kBlendsSwap[mode].dst : kBlends[mode].dst;
+
+ if (sourceMode != mSrcMode || destMode != mSrcMode) {
+ glBlendFunc(sourceMode, destMode);
+ mSrcMode = sourceMode;
+ mDstMode = destMode;
+ }
+}
+
+void Blend::disable() {
+ if (mEnabled) {
+ glDisable(GL_BLEND);
+ mEnabled = false;
+ }
+}
+
+void Blend::invalidate() {
+ syncEnabled();
+ mSrcMode = mDstMode = GL_ZERO;
+}
+
+void Blend::syncEnabled() {
+ if (mEnabled) {
+ glEnable(GL_BLEND);
+ } else {
+ glDisable(GL_BLEND);
+ }
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h
new file mode 100644
index 0000000..b82b477
--- /dev/null
+++ b/libs/hwui/renderstate/Blend.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+#ifndef RENDERSTATE_BLEND_H
+#define RENDERSTATE_BLEND_H
+
+#include "Vertex.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <SkXfermode.h>
+#include <memory>
+
+namespace android {
+namespace uirenderer {
+
+class Blend {
+ friend class RenderState;
+public:
+ void enable(SkXfermode::Mode mode, bool swapSrcDst);
+ void disable();
+ void syncEnabled();
+private:
+ Blend();
+ void invalidate();
+ bool mEnabled;
+ GLenum mSrcMode;
+ GLenum mDstMode;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // RENDERSTATE_BLEND_H
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index e4c8745..58ec321 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -23,10 +23,6 @@ namespace uirenderer {
RenderState::RenderState(renderthread::RenderThread& thread)
: mRenderThread(thread)
- , mCaches(nullptr)
- , mMeshState(nullptr)
- , mScissor(nullptr)
- , mStencil(nullptr)
, mViewportWidth(0)
, mViewportHeight(0)
, mFramebuffer(0) {
@@ -34,19 +30,22 @@ RenderState::RenderState(renderthread::RenderThread& thread)
}
RenderState::~RenderState() {
- LOG_ALWAYS_FATAL_IF(mMeshState || mScissor || mStencil,
+ LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil,
"State object lifecycle not managed correctly");
}
void RenderState::onGLContextCreated() {
- LOG_ALWAYS_FATAL_IF(mMeshState || mScissor || mStencil,
+ LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil,
"State object lifecycle not managed correctly");
+ mBlend = new Blend();
mMeshState = new MeshState();
mScissor = new Scissor();
mStencil = new Stencil();
// This is delayed because the first access of Caches makes GL calls
- mCaches = &Caches::createInstance(*this);
+ if (!mCaches) {
+ mCaches = &Caches::createInstance(*this);
+ }
mCaches->init();
mCaches->textureCache.setAssetAtlas(&mAssetAtlas);
}
@@ -92,6 +91,10 @@ void RenderState::onGLContextDestroyed() {
std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext);
mAssetAtlas.terminate();
+ mCaches->terminate();
+
+ delete mBlend;
+ mBlend = nullptr;
delete mMeshState;
mMeshState = nullptr;
delete mScissor;
@@ -132,7 +135,7 @@ void RenderState::interruptForFunctorInvoke() {
mCaches->currentProgram = nullptr;
}
}
- mCaches->resetActiveTexture();
+ mCaches->textureState().resetActiveTexture();
meshState().unbindMeshBuffer();
meshState().unbindIndicesBuffer();
meshState().resetVertexPointers();
@@ -148,14 +151,10 @@ void RenderState::resumeFromFunctorInvoke() {
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
scissor().invalidate();
+ blend().invalidate();
- mCaches->activeTexture(0);
- mCaches->resetBoundTextures();
-
- mCaches->blend = true;
- glEnable(GL_BLEND);
- glBlendFunc(mCaches->lastSrcMode, mCaches->lastDstMode);
- glBlendEquation(GL_FUNC_ADD);
+ mCaches->textureState().activateTexture(0);
+ mCaches->textureState().resetBoundTextures();
}
void RenderState::debugOverdraw(bool enable, bool clear) {
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index d1ee64a..4180f44 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -24,7 +24,7 @@
#include <utils/RefBase.h>
#include <private/hwui/DrawGlInfo.h>
-
+#include <renderstate/Blend.h>
#include "AssetAtlas.h"
#include "Caches.h"
#include "renderstate/MeshState.h"
@@ -84,6 +84,7 @@ public:
void postDecStrong(VirtualLightRefBase* object);
AssetAtlas& assetAtlas() { return mAssetAtlas; }
+ Blend& blend() { return *mBlend; }
MeshState& meshState() { return *mMeshState; }
Scissor& scissor() { return *mScissor; }
Stencil& stencil() { return *mStencil; }
@@ -100,11 +101,12 @@ private:
renderthread::RenderThread& mRenderThread;
- Caches* mCaches;
+ Caches* mCaches = nullptr;
- MeshState* mMeshState;
- Scissor* mScissor;
- Stencil* mStencil;
+ Blend* mBlend = nullptr;
+ MeshState* mMeshState = nullptr;
+ Scissor* mScissor = nullptr;
+ Stencil* mStencil = nullptr;
AssetAtlas mAssetAtlas;
std::set<Layer*> mActiveLayers;
diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp
new file mode 100644
index 0000000..1a638d2
--- /dev/null
+++ b/libs/hwui/renderstate/TextureState.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+#include <renderstate/TextureState.h>
+
+namespace android {
+namespace uirenderer {
+
+// Must define as many texture units as specified by kTextureUnitsCount
+const GLenum kTextureUnits[] = {
+ GL_TEXTURE0,
+ GL_TEXTURE1,
+ GL_TEXTURE2
+};
+
+TextureState::TextureState()
+ : mTextureUnit(0) {
+ glActiveTexture(kTextureUnits[0]);
+ resetBoundTextures();
+
+ GLint maxTextureUnits;
+ glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
+ LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount,
+ "At least %d texture units are required!", kTextureUnitsCount);
+}
+
+void TextureState::activateTexture(GLuint textureUnit) {
+ if (mTextureUnit != textureUnit) {
+ glActiveTexture(kTextureUnits[textureUnit]);
+ mTextureUnit = textureUnit;
+ }
+}
+
+void TextureState::resetActiveTexture() {
+ mTextureUnit = -1;
+}
+
+void TextureState::bindTexture(GLuint texture) {
+ if (mBoundTextures[mTextureUnit] != texture) {
+ glBindTexture(GL_TEXTURE_2D, texture);
+ mBoundTextures[mTextureUnit] = texture;
+ }
+}
+
+void TextureState::bindTexture(GLenum target, GLuint texture) {
+ if (target == GL_TEXTURE_2D) {
+ bindTexture(texture);
+ } else {
+ // GLConsumer directly calls glBindTexture() with
+ // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target
+ // since the cached state could be stale
+ glBindTexture(target, texture);
+ }
+}
+
+void TextureState::deleteTexture(GLuint texture) {
+ // When glDeleteTextures() is called on a currently bound texture,
+ // OpenGL ES specifies that the texture is then considered unbound
+ // Consider the following series of calls:
+ //
+ // glGenTextures -> creates texture name 2
+ // glBindTexture(2)
+ // glDeleteTextures(2) -> 2 is now unbound
+ // glGenTextures -> can return 2 again
+ //
+ // If we don't call glBindTexture(2) after the second glGenTextures
+ // call, any texture operation will be performed on the default
+ // texture (name=0)
+
+ unbindTexture(texture);
+
+ glDeleteTextures(1, &texture);
+}
+
+void TextureState::resetBoundTextures() {
+ for (int i = 0; i < kTextureUnitsCount; i++) {
+ mBoundTextures[i] = 0;
+ }
+}
+
+void TextureState::unbindTexture(GLuint texture) {
+ for (int i = 0; i < kTextureUnitsCount; i++) {
+ if (mBoundTextures[i] == texture) {
+ mBoundTextures[i] = 0;
+ }
+ }
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h
new file mode 100644
index 0000000..5a57b9f
--- /dev/null
+++ b/libs/hwui/renderstate/TextureState.h
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+#ifndef RENDERSTATE_TEXTURESTATE_H
+#define RENDERSTATE_TEXTURESTATE_H
+
+#include "Vertex.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <SkXfermode.h>
+#include <memory>
+
+namespace android {
+namespace uirenderer {
+
+class TextureState {
+ friend class Caches; // TODO: move to RenderState
+public:
+ /**
+ * Activate the specified texture unit. The texture unit must
+ * be specified using an integer number (0 for GL_TEXTURE0 etc.)
+ */
+ void activateTexture(GLuint textureUnit);
+
+ /**
+ * Invalidate the cached value of the active texture unit.
+ */
+ void resetActiveTexture();
+
+ /**
+ * Binds the specified texture as a GL_TEXTURE_2D texture.
+ * All texture bindings must be performed with this method or
+ * bindTexture(GLenum, GLuint).
+ */
+ void bindTexture(GLuint texture);
+
+ /**
+ * Binds the specified texture with the specified render target.
+ * All texture bindings must be performed with this method or
+ * bindTexture(GLuint).
+ */
+ void bindTexture(GLenum target, GLuint texture);
+
+ /**
+ * Deletes the specified texture and clears it from the cache
+ * of bound textures.
+ * All textures must be deleted using this method.
+ */
+ void deleteTexture(GLuint texture);
+
+ /**
+ * Signals that the cache of bound textures should be cleared.
+ * Other users of the context may have altered which textures are bound.
+ */
+ void resetBoundTextures();
+
+ /**
+ * Clear the cache of bound textures.
+ */
+ void unbindTexture(GLuint texture);
+private:
+ // total number of texture units available for use
+ static const int kTextureUnitsCount = 3;
+
+ TextureState();
+ GLuint mTextureUnit;
+
+ // Caches texture bindings for the GL_TEXTURE_2D target
+ GLuint mBoundTextures[kTextureUnitsCount];
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // RENDERSTATE_BLEND_H
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 28aa938..3afca2f 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -217,9 +217,6 @@ void EglManager::destroy() {
if (mEglDisplay == EGL_NO_DISPLAY) return;
usePBufferSurface();
- if (Caches::hasInstance()) {
- Caches::getInstance().terminate();
- }
mRenderThread.renderState().onGLContextDestroyed();
eglDestroyContext(mEglDisplay, mEglContext);
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index fcf222b..bf3387b 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -170,6 +170,9 @@ public class Location implements Parcelable {
* Converts a coordinate to a String representation. The outputType
* may be one of FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS.
* The coordinate must be a valid double between -180.0 and 180.0.
+ * This conversion is performed in a method that is dependent on the
+ * default locale, and so is not guaranteed to round-trip with
+ * {@link #convert(String)}.
*
* @throws IllegalArgumentException if coordinate is less than
* -180.0, greater than 180.0, or is not a number.
@@ -217,7 +220,9 @@ public class Location implements Parcelable {
/**
* Converts a String in one of the formats described by
* FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS into a
- * double.
+ * double. This conversion is performed in a locale agnostic
+ * method, and so is not guaranteed to round-trip with
+ * {@link #convert(double, int)}.
*
* @throws NullPointerException if coordinate is null
* @throws IllegalArgumentException if the coordinate is not
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9bcf3c8..f448dc2 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -346,6 +346,31 @@ public class AudioManager {
*/
public static final int ADJUST_SAME = 0;
+ /**
+ * Mute the volume. Has no effect if the stream is already muted.
+ *
+ * @see #adjustVolume(int, int)
+ * @see #adjustStreamVolume(int, int, int)
+ */
+ public static final int ADJUST_MUTE = -100;
+
+ /**
+ * Unmute the volume. Has no effect if the stream is not muted.
+ *
+ * @see #adjustVolume(int, int)
+ * @see #adjustStreamVolume(int, int, int)
+ */
+ public static final int ADJUST_UNMUTE = 100;
+
+ /**
+ * Toggle the mute state. If muted the stream will be unmuted. If not muted
+ * the stream will be muted.
+ *
+ * @see #adjustVolume(int, int)
+ * @see #adjustStreamVolume(int, int, int)
+ */
+ public static final int ADJUST_TOGGLE_MUTE = 101;
+
// Flags should be powers of 2!
/**
@@ -777,13 +802,17 @@ public class AudioManager {
* screen is showing. Another example, if music is playing in the background
* and a call is not active, the music stream will be adjusted.
* <p>
- * This method should only be used by applications that replace the platform-wide
- * management of audio settings or the main telephony application.
- * <p>This method has no effect if the device implements a fixed volume policy
+ * This method should only be used by applications that replace the
+ * platform-wide management of audio settings or the main telephony
+ * application.
+ * <p>
+ * This method has no effect if the device implements a fixed volume policy
* as indicated by {@link #isVolumeFixed()}.
+ *
* @param direction The direction to adjust the volume. One of
- * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
- * {@link #ADJUST_SAME}.
+ * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE},
+ * {@link #ADJUST_SAME}, {@link #ADJUST_MUTE},
+ * {@link #ADJUST_UNMUTE}, or {@link #ADJUST_TOGGLE_MUTE}.
* @param flags One or more flags.
* @see #adjustSuggestedStreamVolume(int, int, int)
* @see #adjustStreamVolume(int, int, int)
@@ -808,16 +837,20 @@ public class AudioManager {
* Adjusts the volume of the most relevant stream, or the given fallback
* stream.
* <p>
- * This method should only be used by applications that replace the platform-wide
- * management of audio settings or the main telephony application.
- *
- * <p>This method has no effect if the device implements a fixed volume policy
+ * This method should only be used by applications that replace the
+ * platform-wide management of audio settings or the main telephony
+ * application.
+ * <p>
+ * This method has no effect if the device implements a fixed volume policy
* as indicated by {@link #isVolumeFixed()}.
+ *
* @param direction The direction to adjust the volume. One of
- * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
- * {@link #ADJUST_SAME}.
+ * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE},
+ * {@link #ADJUST_SAME}, {@link #ADJUST_MUTE},
+ * {@link #ADJUST_UNMUTE}, or {@link #ADJUST_TOGGLE_MUTE}.
* @param suggestedStreamType The stream type that will be used if there
- * isn't a relevant stream. {@link #USE_DEFAULT_STREAM_TYPE} is valid here.
+ * isn't a relevant stream. {@link #USE_DEFAULT_STREAM_TYPE} is
+ * valid here.
* @param flags One or more flags.
* @see #adjustVolume(int, int)
* @see #adjustStreamVolume(int, int, int)
@@ -1088,72 +1121,72 @@ public class AudioManager {
}
/**
- * Solo or unsolo a particular stream. All other streams are muted.
- * <p>
- * The solo command is protected against client process death: if a process
- * with an active solo request on a stream dies, all streams that were muted
- * because of this request will be unmuted automatically.
- * <p>
- * The solo requests for a given stream are cumulative: the AudioManager
- * can receive several solo requests from one or more clients and the stream
- * will be unsoloed only when the same number of unsolo requests are received.
+ * Solo or unsolo a particular stream.
* <p>
- * For a better user experience, applications MUST unsolo a soloed stream
- * in onPause() and solo is again in onResume() if appropriate.
- * <p>This method has no effect if the device implements a fixed volume policy
- * as indicated by {@link #isVolumeFixed()}.
+ * Do not use. This method has been deprecated and is now a no-op.
+ * {@link #requestAudioFocus} should be used for exclusive audio playback.
*
* @param streamType The stream to be soloed/unsoloed.
- * @param state The required solo state: true for solo ON, false for solo OFF
- *
+ * @param state The required solo state: true for solo ON, false for solo
+ * OFF
* @see #isVolumeFixed()
+ * @deprecated Do not use. If you need exclusive audio playback use
+ * {@link #requestAudioFocus}.
*/
+ @Deprecated
public void setStreamSolo(int streamType, boolean state) {
- IAudioService service = getService();
- try {
- service.setStreamSolo(streamType, state, mICallBack);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setStreamSolo", e);
- }
+ Log.w(TAG, "setStreamSolo has been deprecated. Do not use.");
}
/**
* Mute or unmute an audio stream.
* <p>
- * The mute command is protected against client process death: if a process
- * with an active mute request on a stream dies, this stream will be unmuted
- * automatically.
- * <p>
- * The mute requests for a given stream are cumulative: the AudioManager
- * can receive several mute requests from one or more clients and the stream
- * will be unmuted only when the same number of unmute requests are received.
- * <p>
- * For a better user experience, applications MUST unmute a muted stream
- * in onPause() and mute is again in onResume() if appropriate.
+ * This method should only be used by applications that replace the
+ * platform-wide management of audio settings or the main telephony
+ * application.
* <p>
- * This method should only be used by applications that replace the platform-wide
- * management of audio settings or the main telephony application.
- * <p>This method has no effect if the device implements a fixed volume policy
+ * This method has no effect if the device implements a fixed volume policy
* as indicated by {@link #isVolumeFixed()}.
+ * <p>
+ * This method was deprecated in API level 22. Prior to API level 22 this
+ * method had significantly different behavior and should be used carefully.
+ * The following applies only to pre-22 platforms:
+ * <ul>
+ * <li>The mute command is protected against client process death: if a
+ * process with an active mute request on a stream dies, this stream will be
+ * unmuted automatically.</li>
+ * <li>The mute requests for a given stream are cumulative: the AudioManager
+ * can receive several mute requests from one or more clients and the stream
+ * will be unmuted only when the same number of unmute requests are
+ * received.</li>
+ * <li>For a better user experience, applications MUST unmute a muted stream
+ * in onPause() and mute is again in onResume() if appropriate.</li>
+ * </ul>
*
* @param streamType The stream to be muted/unmuted.
- * @param state The required mute state: true for mute ON, false for mute OFF
- *
+ * @param state The required mute state: true for mute ON, false for mute
+ * OFF
* @see #isVolumeFixed()
+ * @deprecated Use {@link #adjustStreamVolume(int, int, int)} with
+ * {@link #ADJUST_MUTE} or {@link #ADJUST_UNMUTE} instead.
*/
+ @Deprecated
public void setStreamMute(int streamType, boolean state) {
- IAudioService service = getService();
- try {
- service.setStreamMute(streamType, state, mICallBack);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setStreamMute", e);
+ Log.w(TAG, "setStreamMute is deprecated. adjustStreamVolume should be used instead.");
+ int direction = state ? ADJUST_MUTE : ADJUST_UNMUTE;
+ if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
+ adjustSuggestedStreamVolume(direction, streamType, 0);
+ } else {
+ adjustStreamVolume(streamType, direction, 0);
}
}
/**
- * get stream mute state.
+ * Returns the current mute state for a particular stream.
*
- * @hide
+ * @param streamType The stream to get mute state for.
+ * @return The mute state for the given stream.
+ * @see #adjustStreamVolume(int, int, int)
*/
public boolean isStreamMute(int streamType) {
IAudioService service = getService();
@@ -1166,29 +1199,6 @@ public class AudioManager {
}
/**
- * set master mute state.
- *
- * @hide
- */
- public void setMasterMute(boolean state) {
- setMasterMute(state, FLAG_SHOW_UI);
- }
-
- /**
- * set master mute state with optional flags.
- *
- * @hide
- */
- public void setMasterMute(boolean state, int flags) {
- IAudioService service = getService();
- try {
- service.setMasterMute(state, flags, mContext.getOpPackageName(), mICallBack);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setMasterMute", e);
- }
- }
-
- /**
* get master mute state.
*
* @hide
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
index 616bdd1..873c142 100644
--- a/media/java/android/media/AudioManagerInternal.java
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -41,9 +41,6 @@ public abstract class AudioManagerInternal {
public abstract void adjustMasterVolumeForUid(int steps, int flags, String callingPackage,
int uid);
- public abstract void setMasterMuteForUid(boolean state, int flags, String callingPackage,
- IBinder cb, int uid);
-
public abstract void setRingerModeDelegate(RingerModeDelegate delegate);
public abstract int getRingerModeInternal();
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 0ffa5fc..d96fee6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -748,7 +748,7 @@ public class AudioService extends IAudioService.Stub {
setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]]);
}
// apply stream volume
- if (!mStreamStates[streamType].isMuted_syncVSS()) {
+ if (!mStreamStates[streamType].mIsMuted) {
mStreamStates[streamType].applyAllVolumes();
}
}
@@ -970,6 +970,7 @@ public class AudioService extends IAudioService.Stub {
if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType
+ ", flags=" + flags);
int streamType;
+ boolean isMute = isMuteAdjust(direction);
if (mVolumeControlStream != -1) {
streamType = mVolumeControlStream;
} else {
@@ -984,7 +985,8 @@ public class AudioService extends IAudioService.Stub {
}
// For notifications/ring, show the ui before making any adjustments
- if (mVolumeController.suppressAdjustment(resolvedStream, flags)) {
+ // Don't suppress mute/unmute requests
+ if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)) {
direction = 0;
flags &= ~AudioManager.FLAG_PLAY_SOUND;
flags &= ~AudioManager.FLAG_VIBRATE;
@@ -1011,10 +1013,17 @@ public class AudioService extends IAudioService.Stub {
ensureValidDirection(direction);
ensureValidStreamType(streamType);
+ boolean isMuteAdjust = isMuteAdjust(direction);
+
// use stream type alias here so that streams with same alias have the same behavior,
// including with regard to silent mode control (e.g the use of STREAM_RING below and in
// checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
int streamTypeAlias = mStreamVolumeAlias[streamType];
+
+ if (isMuteAdjust && !isStreamAffectedByMute(streamTypeAlias)) {
+ return;
+ }
+
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
final int device = getDeviceForStream(streamTypeAlias);
@@ -1100,13 +1109,37 @@ public class AudioService extends IAudioService.Stub {
}
}
- if ((direction == AudioManager.ADJUST_RAISE) &&
+ if (isMuteAdjust) {
+ boolean state;
+ if (direction == AudioManager.ADJUST_TOGGLE_MUTE) {
+ state = !streamState.mIsMuted;
+ } else {
+ state = direction == AudioManager.ADJUST_MUTE;
+ }
+ if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
+ setSystemAudioMute(state);
+ }
+ for (int stream = 0; stream < mStreamStates.length; stream++) {
+ if (streamTypeAlias == mStreamVolumeAlias[stream]) {
+ mStreamStates[stream].mute(state);
+
+ Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION);
+ intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, stream);
+ intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, state);
+ sendBroadcastToAll(intent);
+ }
+ }
+ } else if ((direction == AudioManager.ADJUST_RAISE) &&
!checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
- Log.e(TAG, "adjustStreamVolume() safe volume index = "+oldIndex);
+ Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);
mVolumeController.postDisplaySafeVolumeWarning(flags);
- } else if (streamState.adjustIndex(direction * step, device)) {
- // Post message to set system volume (it in turn will post a message
- // to persist). Do not change volume if stream is muted.
+ } else if (streamState.adjustIndex(direction * step, device) || streamState.mIsMuted) {
+ // Post message to set system volume (it in turn will post a
+ // message to persist).
+ if (streamState.mIsMuted) {
+ // Unmute the stream if it was previously muted
+ streamState.mute(false);
+ }
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
@@ -1116,7 +1149,7 @@ public class AudioService extends IAudioService.Stub {
0);
}
- // Check if volume update should be send to Hdmi system audio.
+ // Check if volume update should be sent to Hdmi system audio.
int newIndex = mStreamStates[streamType].getIndex(device);
if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);
@@ -1129,7 +1162,7 @@ public class AudioService extends IAudioService.Stub {
oldIndex != newIndex) {
synchronized (mHdmiPlaybackClient) {
int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN :
- KeyEvent.KEYCODE_VOLUME_UP;
+ KeyEvent.KEYCODE_VOLUME_UP;
mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
}
@@ -1172,6 +1205,10 @@ public class AudioService extends IAudioService.Stub {
if (mUseFixedVolume) {
return;
}
+ if (isMuteAdjust(steps)) {
+ setMasterMuteInternal(steps, flags, callingPackage, uid);
+ return;
+ }
ensureValidSteps(steps);
int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
int delta = 0;
@@ -1500,46 +1537,6 @@ public class AudioService extends IAudioService.Stub {
}
}
- /** @see AudioManager#setStreamSolo(int, boolean) */
- public void setStreamSolo(int streamType, boolean state, IBinder cb) {
- if (mUseFixedVolume) {
- return;
- }
- int streamAlias = mStreamVolumeAlias[streamType];
- for (int stream = 0; stream < mStreamStates.length; stream++) {
- if (!isStreamAffectedByMute(streamAlias) || streamAlias == mStreamVolumeAlias[stream]) {
- continue;
- }
- mStreamStates[stream].mute(cb, state);
- }
- }
-
- /** @see AudioManager#setStreamMute(int, boolean) */
- public void setStreamMute(int streamType, boolean state, IBinder cb) {
- if (mUseFixedVolume) {
- return;
- }
- if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
- streamType = getActiveStreamType(streamType);
- }
- int streamAlias = mStreamVolumeAlias[streamType];
- if (isStreamAffectedByMute(streamAlias)) {
- if (streamAlias == AudioSystem.STREAM_MUSIC) {
- setSystemAudioMute(state);
- }
- for (int stream = 0; stream < mStreamStates.length; stream++) {
- if (streamAlias == mStreamVolumeAlias[stream]) {
- mStreamStates[stream].mute(cb, state);
-
- Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION);
- intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, stream);
- intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, state);
- sendBroadcastToAll(intent);
- }
- }
- }
- }
-
private void setSystemAudioMute(boolean state) {
if (mHdmiManager == null || mHdmiTvClient == null) return;
synchronized (mHdmiManager) {
@@ -1561,7 +1558,7 @@ public class AudioService extends IAudioService.Stub {
streamType = getActiveStreamType(streamType);
}
synchronized (VolumeStreamState.class) {
- return mStreamStates[streamType].isMuted_syncVSS();
+ return mStreamStates[streamType].mIsMuted;
}
}
@@ -1665,20 +1662,17 @@ public class AudioService extends IAudioService.Stub {
}
}
- /** @see AudioManager#setMasterMute(boolean, int) */
- public void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb) {
- setMasterMuteInternal(state, flags, callingPackage, cb, Binder.getCallingUid());
- }
-
- private void setMasterMuteInternal(boolean state, int flags, String callingPackage, IBinder cb,
- int uid) {
- if (mUseFixedVolume) {
- return;
- }
+ private void setMasterMuteInternal(int adjust, int flags, String callingPackage, int uid) {
if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)
!= AppOpsManager.MODE_ALLOWED) {
return;
}
+ boolean state;
+ if (adjust == AudioManager.ADJUST_TOGGLE_MUTE) {
+ state = !AudioSystem.getMasterMute();
+ } else {
+ state = adjust == AudioManager.ADJUST_MUTE;
+ }
if (state != AudioSystem.getMasterMute()) {
setSystemAudioMute(state);
AudioSystem.setMasterMute(state);
@@ -1714,7 +1708,7 @@ public class AudioService extends IAudioService.Stub {
int index = mStreamStates[streamType].getIndex(device);
// by convention getStreamVolume() returns 0 when a stream is muted.
- if (mStreamStates[streamType].isMuted_syncVSS()) {
+ if (mStreamStates[streamType].mIsMuted) {
index = 0;
}
if (index != 0 && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
@@ -1934,11 +1928,11 @@ public class AudioService extends IAudioService.Stub {
}
}
}
- mStreamStates[streamType].mute(null, false);
+ mStreamStates[streamType].mute(false);
mRingerModeMutedStreams &= ~(1 << streamType);
} else {
// mute
- mStreamStates[streamType].mute(null, true);
+ mStreamStates[streamType].mute(true);
mRingerModeMutedStreams |= (1 << streamType);
}
}
@@ -2426,13 +2420,9 @@ public class AudioService extends IAudioService.Stub {
streamState.readSettings();
synchronized (VolumeStreamState.class) {
// unmute stream that was muted but is not affect by mute anymore
- if (streamState.isMuted_syncVSS() && ((!isStreamAffectedByMute(streamType) &&
+ if (streamState.mIsMuted && ((!isStreamAffectedByMute(streamType) &&
!isStreamMutedByRingerMode(streamType)) || mUseFixedVolume)) {
- int size = streamState.mDeathHandlers.size();
- for (int i = 0; i < size; i++) {
- streamState.mDeathHandlers.get(i).mMuteCount = 1;
- streamState.mDeathHandlers.get(i).mute_syncVSS(false);
- }
+ streamState.mIsMuted = false;
}
}
}
@@ -3221,8 +3211,16 @@ public class AudioService extends IAudioService.Stub {
}
private void ensureValidDirection(int direction) {
- if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
- throw new IllegalArgumentException("Bad direction " + direction);
+ switch (direction) {
+ case AudioManager.ADJUST_LOWER:
+ case AudioManager.ADJUST_RAISE:
+ case AudioManager.ADJUST_SAME:
+ case AudioManager.ADJUST_MUTE:
+ case AudioManager.ADJUST_UNMUTE:
+ case AudioManager.ADJUST_TOGGLE_MUTE:
+ break;
+ default:
+ throw new IllegalArgumentException("Bad direction " + direction);
}
}
@@ -3238,6 +3236,11 @@ public class AudioService extends IAudioService.Stub {
}
}
+ private boolean isMuteAdjust(int adjust) {
+ return adjust == AudioManager.ADJUST_MUTE || adjust == AudioManager.ADJUST_UNMUTE
+ || adjust == AudioManager.ADJUST_TOGGLE_MUTE;
+ }
+
private boolean isInCommunication() {
boolean IsInCall = false;
@@ -3467,11 +3470,11 @@ public class AudioService extends IAudioService.Stub {
public class VolumeStreamState {
private final int mStreamType;
+ private boolean mIsMuted;
private String mVolumeIndexSettingName;
private int mIndexMax;
private final ConcurrentHashMap<Integer, Integer> mIndex =
new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
- private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death
private VolumeStreamState(String settingName, int streamType) {
@@ -3482,9 +3485,6 @@ public class AudioService extends IAudioService.Stub {
AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
mIndexMax *= 10;
- // mDeathHandlers must be created before calling readSettings()
- mDeathHandlers = new ArrayList<VolumeDeathHandler>();
-
readSettings();
}
@@ -3549,7 +3549,7 @@ public class AudioService extends IAudioService.Stub {
// must be called while synchronized VolumeStreamState.class
public void applyDeviceVolume_syncVSS(int device) {
int index;
- if (isMuted_syncVSS()) {
+ if (mIsMuted) {
index = 0;
} else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && mAvrcpAbsVolSupported)
|| ((device & mFullVolumeDevices) != 0)) {
@@ -3565,7 +3565,7 @@ public class AudioService extends IAudioService.Stub {
// apply default volume first: by convention this will reset all
// devices volumes in audio policy manager to the supplied value
int index;
- if (isMuted_syncVSS()) {
+ if (mIsMuted) {
index = 0;
} else {
index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
@@ -3578,7 +3578,7 @@ public class AudioService extends IAudioService.Stub {
Map.Entry entry = (Map.Entry)i.next();
int device = ((Integer)entry.getKey()).intValue();
if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
- if (isMuted_syncVSS()) {
+ if (mIsMuted) {
index = 0;
} else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
mAvrcpAbsVolSupported)
@@ -3688,14 +3688,20 @@ public class AudioService extends IAudioService.Stub {
}
}
- public void mute(IBinder cb, boolean state) {
+ public void mute(boolean state) {
synchronized (VolumeStreamState.class) {
- VolumeDeathHandler handler = getDeathHandler_syncVSS(cb, state);
- if (handler == null) {
- Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
- return;
+ if (state != mIsMuted) {
+ mIsMuted = state;
+ // Set the new mute volume. This propagates the values to
+ // the audio system, otherwise the volume won't be changed
+ // at the lower level.
+ sendMsg(mAudioHandler,
+ MSG_SET_ALL_VOLUMES,
+ SENDMSG_QUEUE,
+ 0,
+ 0,
+ this, 0);
}
- handler.mute_syncVSS(state);
}
}
@@ -3733,117 +3739,9 @@ public class AudioService extends IAudioService.Stub {
return index;
}
- private class VolumeDeathHandler implements IBinder.DeathRecipient {
- private IBinder mICallback; // To be notified of client's death
- private int mMuteCount; // Number of active mutes for this client
-
- VolumeDeathHandler(IBinder cb) {
- mICallback = cb;
- }
-
- // must be called while synchronized VolumeStreamState.class
- public void mute_syncVSS(boolean state) {
- boolean updateVolume = false;
- if (state) {
- if (mMuteCount == 0) {
- // Register for client death notification
- try {
- // mICallback can be 0 if muted by AudioService
- if (mICallback != null) {
- mICallback.linkToDeath(this, 0);
- }
- VolumeStreamState.this.mDeathHandlers.add(this);
- // If the stream is not yet muted by any client, set level to 0
- if (!VolumeStreamState.this.isMuted_syncVSS()) {
- updateVolume = true;
- }
- } catch (RemoteException e) {
- // Client has died!
- binderDied();
- return;
- }
- } else {
- Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
- }
- mMuteCount++;
- } else {
- if (mMuteCount == 0) {
- Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
- } else {
- mMuteCount--;
- if (mMuteCount == 0) {
- // Unregister from client death notification
- VolumeStreamState.this.mDeathHandlers.remove(this);
- // mICallback can be 0 if muted by AudioService
- if (mICallback != null) {
- mICallback.unlinkToDeath(this, 0);
- }
- if (!VolumeStreamState.this.isMuted_syncVSS()) {
- updateVolume = true;
- }
- }
- }
- }
- if (updateVolume) {
- sendMsg(mAudioHandler,
- MSG_SET_ALL_VOLUMES,
- SENDMSG_QUEUE,
- 0,
- 0,
- VolumeStreamState.this, 0);
- }
- }
-
- public void binderDied() {
- Log.w(TAG, "Volume service client died for stream: "+mStreamType);
- synchronized (VolumeStreamState.class) {
- if (mMuteCount != 0) {
- // Reset all active mute requests from this client.
- mMuteCount = 1;
- mute_syncVSS(false);
- }
- }
- }
- }
-
- private int muteCount() {
- int count = 0;
- int size = mDeathHandlers.size();
- for (int i = 0; i < size; i++) {
- count += mDeathHandlers.get(i).mMuteCount;
- }
- return count;
- }
-
- // must be called while synchronized VolumeStreamState.class
- private boolean isMuted_syncVSS() {
- return muteCount() != 0;
- }
-
- // must be called while synchronized VolumeStreamState.class
- private VolumeDeathHandler getDeathHandler_syncVSS(IBinder cb, boolean state) {
- VolumeDeathHandler handler;
- int size = mDeathHandlers.size();
- for (int i = 0; i < size; i++) {
- handler = mDeathHandlers.get(i);
- if (cb == handler.mICallback) {
- return handler;
- }
- }
- // If this is the first mute request for this client, create a new
- // client death handler. Otherwise, it is an out of sequence unmute request.
- if (state) {
- handler = new VolumeDeathHandler(cb);
- } else {
- Log.w(TAG, "stream was not muted by this client");
- handler = null;
- }
- return handler;
- }
-
private void dump(PrintWriter pw) {
- pw.print(" Mute count: ");
- pw.println(muteCount());
+ pw.print(" Muted: ");
+ pw.println(mIsMuted);
pw.print(" Max: ");
pw.println((mIndexMax + 5) / 10);
pw.print(" Current: ");
@@ -5648,7 +5546,10 @@ public class AudioService extends IAudioService.Stub {
Settings.Secure.LONG_PRESS_TIMEOUT, 500, UserHandle.USER_CURRENT);
}
- public boolean suppressAdjustment(int resolvedStream, int flags) {
+ public boolean suppressAdjustment(int resolvedStream, int flags, boolean isMute) {
+ if (isMute) {
+ return false;
+ }
boolean suppress = false;
if (resolvedStream == AudioSystem.STREAM_RING && mController != null) {
final long now = SystemClock.uptimeMillis();
@@ -5801,12 +5702,6 @@ public class AudioService extends IAudioService.Stub {
public void setRingerModeInternal(int ringerMode, String caller) {
AudioService.this.setRingerModeInternal(ringerMode, caller);
}
-
- @Override
- public void setMasterMuteForUid(boolean state, int flags, String callingPackage, IBinder cb,
- int uid) {
- setMasterMuteInternal(state, flags, callingPackage, cb, uid);
- }
}
//==========================================================================================
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index fad3cec..bfb78a1 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -52,16 +52,10 @@ interface IAudioService {
void setMasterVolume(int index, int flags, String callingPackage);
- void setStreamSolo(int streamType, boolean state, IBinder cb);
-
- void setStreamMute(int streamType, boolean state, IBinder cb);
-
boolean isStreamMute(int streamType);
void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb);
- void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb);
-
boolean isMasterMute();
int getStreamVolume(int streamType);
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 7ea269b..9954de5 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -222,13 +222,9 @@ public class MediaSessionLegacyHelper {
direction, flags);
} else if (isMute) {
if (down) {
- // We need to send two volume events on down, one to mute
- // and one to show the UI
mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
- MediaSessionManager.DIRECTION_MUTE, flags);
+ AudioManager.ADJUST_TOGGLE_MUTE, flags);
}
- mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
- 0 /* direction, causes UI to show on down */, flags);
}
}
}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index a4ef851..b4fff8f 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -59,14 +59,6 @@ public final class MediaSessionManager {
private Context mContext;
/**
- * Special flag for sending the mute key to dispatchAdjustVolume used by the
- * system.
- *
- * @hide
- */
- public static final int DIRECTION_MUTE = -99;
-
- /**
* @hide
*/
public MediaSessionManager(Context context) {
diff --git a/packages/SystemUI/res/layout/recents.xml b/packages/SystemUI/res/layout/recents.xml
index 8f367a6..26523f9 100644
--- a/packages/SystemUI/res/layout/recents.xml
+++ b/packages/SystemUI/res/layout/recents.xml
@@ -15,7 +15,7 @@
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+ android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Status Bar Scrim View -->
<ImageView
@@ -29,9 +29,16 @@
<!-- Recents View -->
<com.android.systemui.recents.views.RecentsView
android:id="@+id/recents_view"
- android:layout_width="match_parent"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:focusable="true" />
+ android:focusable="true">
+ <!-- MultiStack Debug View -->
+ <ViewStub android:id="@+id/multistack_debug_view_stub"
+ android:layout="@layout/recents_multistack_debug"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left|bottom" />
+ </com.android.systemui.recents.views.RecentsView>
<!-- Empty View -->
<ViewStub android:id="@+id/empty_view_stub"
diff --git a/packages/SystemUI/res/layout/recents_multistack_debug.xml b/packages/SystemUI/res/layout/recents_multistack_debug.xml
new file mode 100644
index 0000000..6524a54
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_multistack_debug.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left|bottom"
+ android:orientation="vertical">
+ <Button
+ android:id="@+id/add_stack"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:padding="8dp"
+ android:textSize="20sp"
+ android:textColor="#ffffffff"
+ android:text="@string/recents_multistack_add_stack"
+ android:fontFamily="sans-serif"
+ android:background="#000000"
+ android:alpha="0.5" />
+ <Button
+ android:id="@+id/resize_stack"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:padding="8dp"
+ android:textSize="20sp"
+ android:textColor="#ffffffff"
+ android:text="@string/recents_multistack_resize_stack"
+ android:fontFamily="sans-serif"
+ android:background="#000000"
+ android:alpha="0.5" />
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml b/packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml
new file mode 100644
index 0000000..36e54a0
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:orientation="vertical"
+ android:descendantFocusability="beforeDescendants"
+ android:focusableInTouchMode="true">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <EditText
+ android:id="@+id/inset_left"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:hint="Left"
+ android:singleLine="true"
+ android:imeOptions="actionNext"
+ android:inputType="number"
+ android:selectAllOnFocus="true" />
+ <EditText
+ android:id="@+id/inset_top"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:hint="Top"
+ android:singleLine="true"
+ android:imeOptions="actionNext"
+ android:inputType="number"
+ android:selectAllOnFocus="true" />
+ <EditText
+ android:id="@+id/inset_right"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:hint="Right"
+ android:singleLine="true"
+ android:imeOptions="actionNext"
+ android:inputType="number"
+ android:selectAllOnFocus="true" />
+ <EditText
+ android:id="@+id/inset_bottom"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:hint="Bottom"
+ android:singleLine="true"
+ android:imeOptions="actionDone"
+ android:inputType="number"
+ android:selectAllOnFocus="true" />
+ </LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index f1d8ad0..53047a3 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -43,6 +43,16 @@
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
<com.android.systemui.recents.views.FixedSizeImageView
+ android:id="@+id/move_task"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_marginEnd="52dp"
+ android:layout_gravity="center_vertical|end"
+ android:padding="12dp"
+ android:background="@drawable/recents_button_bg"
+ android:src="@drawable/star"
+ android:visibility="gone" />
+ <com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/dismiss_task"
android:layout_width="48dp"
android:layout_height="48dp"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c977db9..40bf13f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -672,6 +672,19 @@
<!-- Recents: Dismiss all button. [CHAR LIMIT=NONE] -->
<string name="recents_dismiss_all_message">Dismiss all applications</string>
+ <!-- Recents: MultiStack add stack button. [CHAR LIMIT=NONE] -->
+ <string name="recents_multistack_add_stack">+</string>
+ <!-- Recents: MultiStack remove stack button. [CHAR LIMIT=NONE] -->
+ <string name="recents_multistack_remove_stack">-</string>
+ <!-- Recents: MultiStack resize stack button. [CHAR LIMIT=NONE] -->
+ <string name="recents_multistack_resize_stack">[]</string>
+ <!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
+ <string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
+ <!-- Recents: MultiStack add stack split vertical radio button. [CHAR LIMIT=NONE] -->
+ <string name="recents_multistack_add_stack_dialog_split_vertical">Split Vertical</string>
+ <!-- Recents: MultiStack add stack split custom radio button. [CHAR LIMIT=NONE] -->
+ <string name="recents_multistack_add_stack_dialog_split_custom">Split Custom</string>
+
<!-- Expanded Status Bar Header: Battery Charged [CHAR LIMIT=40] -->
<string name="expanded_header_battery_charged">Charged</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f2b4a69..bf19b8d 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -20,7 +20,7 @@
<item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item>
</style>
- <style name="RecentsTheme" parent="@android:style/Theme">
+ <style name="RecentsTheme" parent="@android:style/Theme.Material.Light">
<!-- NoTitle -->
<item name="android:windowNoTitle">true</item>
<!-- Misc -->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 974235e..4dacacf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -423,7 +423,9 @@ public class QSPanel extends ViewGroup {
}
for (TileRecord record : mRecords) {
- record.tileView.setDual(record.tile.supportsDualTargets());
+ if (record.tileView.setDual(record.tile.supportsDualTargets())) {
+ record.tileView.handleStateChanged(record.tile.getState());
+ }
if (record.tileView.getVisibility() == GONE) continue;
final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
final int ch = record.row == 0 ? mLargeCellHeight : mCellHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index bb353d5..16ae6b4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -172,7 +172,7 @@ public class QSTileView extends ViewGroup {
}
}
- public void setDual(boolean dual) {
+ public boolean setDual(boolean dual) {
final boolean changed = dual != mDual;
mDual = dual;
if (changed) {
@@ -199,6 +199,7 @@ public class QSTileView extends ViewGroup {
setFocusable(!dual);
mDivider.setVisibility(dual ? VISIBLE : GONE);
postInvalidate();
+ return changed;
}
private void setRipple(RippleDrawable tileBackground) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index cfd6b40..192acc6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -39,6 +39,8 @@ public class Constants {
public static final boolean EnableSearchLayout = true;
// Enables the thumbnail alpha on the front-most task
public static final boolean EnableThumbnailAlphaOnFrontmost = false;
+ // Enables all system stacks to show up in the same recents stack
+ public static final boolean EnableMultiStackToSingleStack = true;
// This disables the bitmap and icon caches
public static final boolean DisableBackgroundCache = false;
// Enables the simulated task affiliations
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 3c75aac..9dd82fc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -24,7 +24,6 @@ import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -56,7 +55,6 @@ import com.android.systemui.recents.views.TaskViewHeader;
import com.android.systemui.recents.views.TaskViewTransform;
import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -112,6 +110,9 @@ public class Recents extends SystemUI
/** Preloads the next task */
public void run() {
+ // Temporarily skip this if multi stack is enabled
+ if (mConfig.multiStackEnabled) return;
+
RecentsConfiguration config = RecentsConfiguration.getInstance();
if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
@@ -362,13 +363,21 @@ public class Recents extends SystemUI
}
void showRelativeAffiliatedTask(boolean showNextTask) {
+ // Return early if there is no focused stack
+ int focusedStackId = mSystemServicesProxy.getFocusedStack();
+ TaskStack focusedStack = null;
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
loader.preloadTasks(plan, true /* isTopTaskHome */);
- TaskStack stack = plan.getTaskStack();
+ if (mConfig.multiStackEnabled) {
+ if (focusedStackId < 0) return;
+ focusedStack = plan.getTaskStack(focusedStackId);
+ } else {
+ focusedStack = plan.getAllTaskStacks().get(0);
+ }
- // Return early if there are no tasks
- if (stack.getTaskCount() == 0) return;
+ // Return early if there are no tasks in the focused stack
+ if (focusedStack.getTaskCount() == 0) return;
ActivityManager.RunningTaskInfo runningTask = mSystemServicesProxy.getTopMostTask();
// Return early if there is no running task (can't determine affiliated tasks in this case)
@@ -377,7 +386,7 @@ public class Recents extends SystemUI
if (mSystemServicesProxy.isInHomeStack(runningTask.id)) return;
// Find the task in the recents list
- ArrayList<Task> tasks = stack.getTasks();
+ ArrayList<Task> tasks = focusedStack.getTasks();
Task toTask = null;
ActivityOptions launchOpts = null;
int taskCount = tasks.size();
@@ -399,7 +408,7 @@ public class Recents extends SystemUI
R.anim.recents_launch_prev_affiliated_task_source);
}
if (toTaskKey != null) {
- toTask = stack.findTaskWithId(toTaskKey.id);
+ toTask = focusedStack.findTaskWithId(toTaskKey.id);
}
numAffiliatedTasks = group.getTaskCount();
break;
@@ -473,8 +482,9 @@ public class Recents extends SystemUI
// Reload the widget id before we get the task stack bounds
reloadSearchBarAppWidget(mContext, mSystemServicesProxy);
}
- mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mStatusBarHeight,
- (mConfig.hasTransposedNavBar ? mNavBarWidth : 0), mTaskStackBounds);
+ mConfig.getAvailableTaskStackBounds(mWindowRect.width(), mWindowRect.height(),
+ mStatusBarHeight, (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
+ mTaskStackBounds);
if (mConfig.isLandscape && mConfig.hasTransposedNavBar) {
mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
} else {
@@ -653,8 +663,25 @@ public class Recents extends SystemUI
// Create a new load plan if onPreloadRecents() was never triggered
sInstanceLoadPlan = loader.createLoadPlan(mContext);
}
+
+ // Temporarily skip the transition (use a dummy fade) if multi stack is enabled.
+ // For multi-stack we need to figure out where each of the tasks are going.
+ if (mConfig.multiStackEnabled) {
+ loader.preloadTasks(sInstanceLoadPlan, true);
+ ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
+ TaskStack stack = stacks.get(0);
+ mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, true);
+ TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
+ mDummyStackView.computeStackVisibilityReport();
+ ActivityOptions opts = getUnknownTransitionActivityOptions();
+ startAlternateRecentsActivity(topTask, opts, true /* fromHome */,
+ false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
+ return;
+ }
+
loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
- TaskStack stack = sInstanceLoadPlan.getTaskStack();
+ ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
+ TaskStack stack = stacks.get(0);
// Prepare the dummy stack for the transition
mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 1833e09..b1ac733 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -18,6 +18,7 @@ package com.android.systemui.recents;
import android.app.Activity;
import android.app.ActivityOptions;
+import android.app.Dialog;
import android.app.SearchManager;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
@@ -43,7 +44,6 @@ import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.SpaceNode;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.DebugOverlayView;
@@ -75,6 +75,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
View mEmptyView;
DebugOverlayView mDebugOverlay;
+ // MultiStack debug
+ RecentsMultiStackDialog mMultiStackDebugDialog;
+
// Search AppWidget
RecentsAppWidgetHost mAppWidgetHost;
AppWidgetProviderInfo mSearchAppWidgetInfo;
@@ -190,7 +193,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
}
// Start loading tasks according to the load plan
- if (plan.getTaskStack() == null) {
+ ArrayList<TaskStack> stacks = plan.getAllTaskStacks();
+ if (stacks.size() == 0) {
loader.preloadTasks(plan, mConfig.launchedFromHome);
}
RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
@@ -199,9 +203,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
loadOpts.numVisibleTaskThumbnails = mConfig.launchedNumVisibleThumbnails;
loader.loadTasks(this, plan, loadOpts);
- SpaceNode root = plan.getSpaceNode();
- ArrayList<TaskStack> stacks = root.getStacks();
- boolean hasTasks = root.hasTasks();
+ boolean hasTasks = plan.hasTasks();
if (hasTasks) {
mRecentsView.setTaskStacks(stacks);
}
@@ -591,6 +593,40 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
}
}
+
+ /**** RecentsMultiStackDialog ****/
+
+ private RecentsMultiStackDialog getMultiStackDebugDialog() {
+ if (mMultiStackDebugDialog == null) {
+ mMultiStackDebugDialog = new RecentsMultiStackDialog(getFragmentManager());
+ }
+ return mMultiStackDebugDialog;
+ }
+
+ @Override
+ public void onMultiStackAddStack() {
+ RecentsMultiStackDialog dialog = getMultiStackDebugDialog();
+ dialog.showAddStackDialog();
+ }
+
+ @Override
+ public void onMultiStackResizeStack() {
+ RecentsMultiStackDialog dialog = getMultiStackDebugDialog();
+ dialog.showResizeStackDialog();
+ }
+
+ @Override
+ public void onMultiStackRemoveStack() {
+ RecentsMultiStackDialog dialog = getMultiStackDebugDialog();
+ dialog.showRemoveStackDialog();
+ }
+
+ @Override
+ public void onMultiStackMoveTask(Task t) {
+ RecentsMultiStackDialog dialog = getMultiStackDebugDialog();
+ dialog.showMoveTaskDialog(t);
+ }
+
/**** RecentsView.RecentsViewCallbacks Implementation ****/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index bc10a48..1736c77 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -134,6 +134,7 @@ public class RecentsConfiguration {
public boolean fakeShadows;
/** Dev options and global settings */
+ public boolean multiStackEnabled;
public boolean lockToAppEnabled;
public boolean developerOptionsEnabled;
public boolean debugModeEnabled;
@@ -294,6 +295,7 @@ public class RecentsConfiguration {
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED) != 0;
lockToAppEnabled = ssp.getSystemSetting(context,
Settings.System.LOCK_TO_APP_ENABLED) != 0;
+ multiStackEnabled = "1".equals(ssp.getSystemProperty("overview.enableMultiStack"));
}
/** Called when the configuration has changed, and we want to reset any configuration specific
@@ -335,8 +337,8 @@ public class RecentsConfiguration {
* Returns the task stack bounds in the current orientation. These bounds do not account for
* the system insets.
*/
- public void getTaskStackBounds(int windowWidth, int windowHeight, int topInset, int rightInset,
- Rect taskStackBounds) {
+ public void getAvailableTaskStackBounds(int windowWidth, int windowHeight, int topInset,
+ int rightInset, Rect taskStackBounds) {
Rect searchBarBounds = new Rect();
getSearchBarBounds(windowWidth, windowHeight, topInset, searchBarBounds);
if (isLandscape && hasTransposedSearchBar) {
@@ -353,7 +355,7 @@ public class RecentsConfiguration {
* the system insets.
*/
public void getSearchBarBounds(int windowWidth, int windowHeight, int topInset,
- Rect searchBarSpaceBounds) {
+ Rect searchBarSpaceBounds) {
// Return empty rects if search is not enabled
int searchBarSize = searchBarSpaceHeightPx;
if (!Constants.DebugFlags.App.EnableSearchLayout || !hasSearchBarAppWidget()) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsMultiStackDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsMultiStackDialog.java
new file mode 100644
index 0000000..fdf9d39
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsMultiStackDialog.java
@@ -0,0 +1,339 @@
+/*
+ * 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 com.android.systemui.recents;
+
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ResolveInfo;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.MutableInt;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.Toast;
+import com.android.systemui.R;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.model.Task;
+
+import java.util.List;
+
+/**
+ * A helper for the dialogs that show when multistack debugging is on.
+ */
+public class RecentsMultiStackDialog extends DialogFragment {
+
+ static final String TAG = "RecentsMultiStackDialog";
+
+ public static final int ADD_STACK_DIALOG = 0;
+ public static final int ADD_STACK_PICK_APP_DIALOG = 1;
+ public static final int REMOVE_STACK_DIALOG = 2;
+ public static final int RESIZE_STACK_DIALOG = 3;
+ public static final int RESIZE_STACK_PICK_STACK_DIALOG = 4;
+ public static final int MOVE_TASK_DIALOG = 5;
+
+ FragmentManager mFragmentManager;
+ int mCurrentDialogType;
+ MutableInt mTargetStackIndex = new MutableInt(0);
+ Task mTaskToMove;
+ SparseArray<ActivityManager.StackInfo> mStacks;
+ List<ResolveInfo> mLauncherActivities;
+ Rect mAddStackRect;
+ Intent mAddStackIntent;
+
+ View mAddStackDialogContent;
+
+ public RecentsMultiStackDialog() {}
+
+ public RecentsMultiStackDialog(FragmentManager mgr) {
+ mFragmentManager = mgr;
+ }
+
+ /** Shows the add-stack dialog. */
+ void showAddStackDialog() {
+ mCurrentDialogType = ADD_STACK_DIALOG;
+ show(mFragmentManager, TAG);
+ }
+
+ /** Creates a new add-stack dialog. */
+ private void createAddStackDialog(final Context context, LayoutInflater inflater,
+ AlertDialog.Builder builder, final SystemServicesProxy ssp) {
+ builder.setTitle("Add Stack - Enter new dimensions");
+ mAddStackDialogContent =
+ inflater.inflate(R.layout.recents_multistack_stack_size_dialog, null, false);
+ Rect windowRect = ssp.getWindowRect();
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_left, windowRect.left);
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_top, windowRect.top);
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_right, windowRect.right);
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_bottom, windowRect.bottom);
+ builder.setView(mAddStackDialogContent);
+ builder.setPositiveButton("Add Stack", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int left = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_left);
+ int top = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_top);
+ int right = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_right);
+ int bottom = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_bottom);
+ if (bottom <= top || right <= left) {
+ Toast.makeText(context, "Invalid dimensions", Toast.LENGTH_SHORT).show();
+ dismiss();
+ return;
+ }
+
+ // Prompt the user for the app to start
+ dismiss();
+ mCurrentDialogType = ADD_STACK_PICK_APP_DIALOG;
+ mAddStackRect = new Rect(left, top, right, bottom);
+ show(mFragmentManager, TAG);
+ }
+ });
+ builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ }
+ });
+ }
+
+ /** Creates a new add-stack pick-app dialog. */
+ private void createAddStackPickAppDialog(final Context context, LayoutInflater inflater,
+ AlertDialog.Builder builder, final SystemServicesProxy ssp) {
+ mLauncherActivities = ssp.getLauncherApps();
+ mAddStackIntent = null;
+ int activityCount = mLauncherActivities.size();
+ CharSequence[] activityNames = new CharSequence[activityCount];
+ for (int i = 0; i < activityCount; i++) {
+ activityNames[i] = ssp.getActivityLabel(mLauncherActivities.get(i).activityInfo);
+ }
+ builder.setTitle("Add Stack - Pick starting app");
+ builder.setSingleChoiceItems(activityNames, -1,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ ActivityInfo ai = mLauncherActivities.get(which).activityInfo;
+ mAddStackIntent = new Intent(Intent.ACTION_MAIN);
+ mAddStackIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ mAddStackIntent.setComponent(new ComponentName(ai.packageName, ai.name));
+ }
+ });
+ builder.setPositiveButton("Add Stack", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // Display 0 = default display
+ ssp.createNewStack(0, mAddStackRect, mAddStackIntent);
+ }
+ });
+ builder.setNegativeButton("Skip", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // Display 0 = default display
+ ssp.createNewStack(0, mAddStackRect, null);
+ }
+ });
+ }
+
+ /** Shows the resize-stack dialog. */
+ void showResizeStackDialog() {
+ mCurrentDialogType = RESIZE_STACK_PICK_STACK_DIALOG;
+ show(mFragmentManager, TAG);
+ }
+
+ /** Creates a new resize-stack pick-stack dialog. */
+ private void createResizeStackPickStackDialog(final Context context, LayoutInflater inflater,
+ AlertDialog.Builder builder, final SystemServicesProxy ssp) {
+ mStacks = ssp.getAllStackInfos();
+ mTargetStackIndex.value = -1;
+ CharSequence[] stackNames = getAllStacksDescriptions(mStacks, -1, null);
+ builder.setTitle("Resize Stack - Pick stack");
+ builder.setSingleChoiceItems(stackNames, mTargetStackIndex.value,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mTargetStackIndex.value = which;
+ }
+ });
+ builder.setPositiveButton("Resize Stack", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (mTargetStackIndex.value != -1) {
+ // Prompt the user for the new dimensions
+ dismiss();
+ mCurrentDialogType = RESIZE_STACK_DIALOG;
+ show(mFragmentManager, TAG);
+ }
+ }
+ });
+ builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ }
+ });
+ }
+
+ /** Creates a new resize-stack dialog. */
+ private void createResizeStackDialog(final Context context, LayoutInflater inflater,
+ AlertDialog.Builder builder, final SystemServicesProxy ssp) {
+ builder.setTitle("Resize Stack - Enter new dimensions");
+ final ActivityManager.StackInfo stack = mStacks.valueAt(mTargetStackIndex.value);
+ mAddStackDialogContent =
+ inflater.inflate(R.layout.recents_multistack_stack_size_dialog, null, false);
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_left, stack.bounds.left);
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_top, stack.bounds.top);
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_right, stack.bounds.right);
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_bottom, stack.bounds.bottom);
+ builder.setView(mAddStackDialogContent);
+ builder.setPositiveButton("Resize Stack", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int left = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_left);
+ int top = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_top);
+ int right = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_right);
+ int bottom = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_bottom);
+ if (bottom <= top || right <= left) {
+ Toast.makeText(context, "Invalid dimensions", Toast.LENGTH_SHORT).show();
+ dismiss();
+ return;
+ }
+ ssp.resizeStack(stack.stackId, new Rect(left, top, right, bottom));
+ }
+ });
+ builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ }
+ });
+ }
+
+ /** Shows the remove-stack dialog. */
+ void showRemoveStackDialog() {
+ mCurrentDialogType = REMOVE_STACK_DIALOG;
+ show(mFragmentManager, TAG);
+ }
+
+ /** Shows the move-task dialog. */
+ void showMoveTaskDialog(Task task) {
+ mCurrentDialogType = MOVE_TASK_DIALOG;
+ mTaskToMove = task;
+ show(mFragmentManager, TAG);
+ }
+
+ /** Creates a new move-stack dialog. */
+ private void createMoveTaskDialog(final Context context, LayoutInflater inflater,
+ AlertDialog.Builder builder, final SystemServicesProxy ssp) {
+ mStacks = ssp.getAllStackInfos();
+ mTargetStackIndex.value = -1;
+ CharSequence[] stackNames = getAllStacksDescriptions(mStacks, mTaskToMove.key.stackId,
+ mTargetStackIndex);
+ builder.setTitle("Move Task to Stack");
+ builder.setSingleChoiceItems(stackNames, mTargetStackIndex.value,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mTargetStackIndex.value = which;
+ }
+ });
+ builder.setPositiveButton("Move Task", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (mTargetStackIndex.value != -1) {
+ ActivityManager.StackInfo toStack = mStacks.valueAt(mTargetStackIndex.value);
+ if (toStack.stackId != mTaskToMove.key.stackId) {
+ ssp.moveTaskToStack(mTaskToMove.key.id, toStack.stackId, true);
+ mTaskToMove.setStackId(toStack.stackId);
+ }
+ }
+ }
+ });
+ builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ }
+ });
+ }
+
+ /** Helper to get an integer value from an edit text. */
+ private int getDimensionFromEditText(View container, int id) {
+ String text = ((EditText) container.findViewById(id)).getText().toString();
+ if (text.trim().length() != 0) {
+ return Integer.parseInt(text.trim());
+ }
+ return 0;
+ }
+
+ /** Helper to set an integer value to an edit text. */
+ private void setDimensionInEditText(View container, int id, int value) {
+ ((EditText) container.findViewById(id)).setText("" + value);
+ }
+
+ /** Gets a list of all the stacks. */
+ private CharSequence[] getAllStacksDescriptions(SparseArray<ActivityManager.StackInfo> stacks,
+ int targetStackId, MutableInt indexOfTargetStackId) {
+ int stackCount = stacks.size();
+ CharSequence[] stackNames = new CharSequence[stackCount];
+ for (int i = 0; i < stackCount; i++) {
+ ActivityManager.StackInfo stack = stacks.valueAt(i);
+ Rect b = stack.bounds;
+ String desc = "Stack " + stack.stackId + " / " +
+ "" + (stack.taskIds.length > 0 ? stack.taskIds.length : "No") + " tasks\n" +
+ "(" + b.left + ", " + b.top + ")-(" + b.right + ", " + b.bottom + ")\n";
+ stackNames[i] = desc;
+ if (targetStackId != -1 && stack.stackId == targetStackId) {
+ indexOfTargetStackId.value = i;
+ }
+ }
+ return stackNames;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle args) {
+ final Context context = this.getActivity();
+ final SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ switch(mCurrentDialogType) {
+ case ADD_STACK_DIALOG:
+ createAddStackDialog(context, inflater, builder, ssp);
+ break;
+ case ADD_STACK_PICK_APP_DIALOG:
+ createAddStackPickAppDialog(context, inflater, builder, ssp);
+ break;
+ case MOVE_TASK_DIALOG:
+ createMoveTaskDialog(context, inflater, builder, ssp);
+ break;
+ case RESIZE_STACK_PICK_STACK_DIALOG:
+ createResizeStackPickStackDialog(context, inflater, builder, ssp);
+ break;
+ case RESIZE_STACK_DIALOG:
+ createResizeStackDialog(context, inflater, builder, ssp);
+ break;
+ }
+ return builder.create();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 237d4f0..72040fe 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -20,6 +20,7 @@ import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
import android.app.AppGlobals;
+import android.app.IActivityContainer;
import android.app.IActivityManager;
import android.app.ITaskStackListener;
import android.app.SearchManager;
@@ -49,10 +50,12 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import android.util.Pair;
+import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
@@ -64,6 +67,8 @@ import com.android.systemui.recents.Recents;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
@@ -228,6 +233,23 @@ public class SystemServicesProxy {
return null;
}
+ /** Returns a list of all the launcher apps sorted by name. */
+ public List<ResolveInfo> getLauncherApps() {
+ if (mPm == null) return new ArrayList<ResolveInfo>();
+
+ final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+ mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ List<ResolveInfo> activities = mPm.queryIntentActivities(mainIntent, 0 /* flags */);
+ Collections.sort(activities, new Comparator<ResolveInfo>() {
+ @Override
+ public int compare(ResolveInfo o1, ResolveInfo o2) {
+ return getActivityLabel(o1.activityInfo).compareTo(
+ getActivityLabel(o2.activityInfo));
+ }
+ });
+ return activities;
+ }
+
/** Returns whether the recents is currently running */
public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
AtomicBoolean isHomeTopMost) {
@@ -250,6 +272,64 @@ public class SystemServicesProxy {
return false;
}
+ /** Create a new stack. */
+ public void createNewStack(int displayId, Rect bounds, Intent activity) {
+ try {
+ IActivityContainer container = mIam.createStackOnDisplay(displayId);
+ if (container != null) {
+ // Resize the stack
+ resizeStack(container.getStackId(), bounds);
+ // Start the new activity on that stack
+ container.startActivity(activity);
+ }
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /** Resizes a stack. */
+ public void resizeStack(int stackId, Rect bounds) {
+ if (mIam == null) return;
+
+ try {
+ mIam.resizeStack(stackId, bounds);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /** Returns the stack info for all stacks. */
+ public SparseArray<ActivityManager.StackInfo> getAllStackInfos() {
+ if (mIam == null) return new SparseArray<ActivityManager.StackInfo>();
+
+ try {
+ SparseArray<ActivityManager.StackInfo> stacks =
+ new SparseArray<ActivityManager.StackInfo>();
+ List<ActivityManager.StackInfo> infos = mIam.getAllStackInfos();
+ int stackCount = infos.size();
+ for (int i = 0; i < stackCount; i++) {
+ ActivityManager.StackInfo info = infos.get(i);
+ stacks.put(info.stackId, info);
+ }
+ return stacks;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return new SparseArray<ActivityManager.StackInfo>();
+ }
+ }
+
+ /** Returns the focused stack id. */
+ public int getFocusedStack() {
+ if (mIam == null) return -1;
+
+ try {
+ return mIam.getFocusedStackId();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return -1;
+ }
+ }
+
/** Returns whether the specified task is in the home stack */
public boolean isInHomeStack(int taskId) {
if (mAm == null) return false;
@@ -313,7 +393,7 @@ public class SystemServicesProxy {
return thumbnail;
}
- /** Moves a task to the front with the specified activity options */
+ /** Moves a task to the front with the specified activity options. */
public void moveTaskToFront(int taskId, ActivityOptions opts) {
if (mAm == null) return;
if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
@@ -326,6 +406,18 @@ public class SystemServicesProxy {
}
}
+ /** Moves a task to another stack. */
+ public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
+ if (mIam == null) return;
+ if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
+
+ try {
+ mIam.moveTaskToStack(taskId, stackId, toTop);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
/** Removes the task */
public void removeTask(int taskId) {
if (mAm == null) return;
@@ -524,6 +616,13 @@ public class SystemServicesProxy {
}
/**
+ * Returns a system property.
+ */
+ public String getSystemProperty(String key) {
+ return SystemProperties.get(key);
+ }
+
+ /**
* Returns the window rect.
*/
public Rect getWindowRect() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 3d25c80..788e473 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -20,9 +20,12 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.util.Log;
+import android.util.SparseArray;
+import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -60,7 +63,7 @@ public class RecentsTaskLoadPlan {
SystemServicesProxy mSystemServicesProxy;
List<ActivityManager.RecentTaskInfo> mRawTasks;
- TaskStack mStack;
+ SparseArray<TaskStack> mStacks = new SparseArray<>();
HashMap<Task.ComponentNameKey, ActivityInfoHandle> mActivityInfoCache =
new HashMap<Task.ComponentNameKey, ActivityInfoHandle>();
@@ -90,21 +93,28 @@ public class RecentsTaskLoadPlan {
synchronized void preloadPlan(RecentsTaskLoader loader, boolean isTopTaskHome) {
if (DEBUG) Log.d(TAG, "preloadPlan");
+ // This activity info cache will be used for both preloadPlan() and executePlan()
mActivityInfoCache.clear();
- mStack = new TaskStack();
+
+ // TODO (multi-display): Currently assume the primary display
+ Rect displayBounds = mSystemServicesProxy.getWindowRect();
Resources res = mContext.getResources();
- ArrayList<Task> loadedTasks = new ArrayList<Task>();
+ SparseArray<ArrayList<Task>> stacksTasks = new SparseArray<>();
if (mRawTasks == null) {
preloadRawTasks(isTopTaskHome);
}
+ int firstStackId = -1;
int taskCount = mRawTasks.size();
for (int i = 0; i < taskCount; i++) {
ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
+ if (firstStackId < 0) {
+ firstStackId = t.stackId;
+ }
// Compose the task key
- Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.baseIntent, t.userId,
- t.firstActiveTime, t.lastActiveTime);
+ Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
+ t.userId, t.firstActiveTime, t.lastActiveTime);
// Get an existing activity info handle if possible
Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
@@ -143,14 +153,42 @@ public class RecentsTaskLoadPlan {
iconFilename);
task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy, false);
if (DEBUG) Log.d(TAG, "\tthumbnail: " + taskKey + ", " + task.thumbnail);
- loadedTasks.add(task);
+
+ if (!mConfig.multiStackEnabled ||
+ Constants.DebugFlags.App.EnableMultiStackToSingleStack) {
+ ArrayList<Task> stackTasks = stacksTasks.get(firstStackId);
+ if (stackTasks == null) {
+ stackTasks = new ArrayList<Task>();
+ stacksTasks.put(firstStackId, stackTasks);
+ }
+ stackTasks.add(task);
+ } else {
+ ArrayList<Task> stackTasks = stacksTasks.get(t.stackId);
+ if (stackTasks == null) {
+ stackTasks = new ArrayList<Task>();
+ stacksTasks.put(t.stackId, stackTasks);
+ }
+ stackTasks.add(task);
+ }
}
- mStack.setTasks(loadedTasks);
- mStack.createAffiliatedGroupings(mConfig);
- // Assertion
- if (mStack.getTaskCount() != mRawTasks.size()) {
- throw new RuntimeException("Loading failed");
+ // Initialize the stacks
+ SparseArray<ActivityManager.StackInfo> stackInfos = mSystemServicesProxy.getAllStackInfos();
+ mStacks.clear();
+ int stackCount = stacksTasks.size();
+ for (int i = 0; i < stackCount; i++) {
+ int stackId = stacksTasks.keyAt(i);
+ ActivityManager.StackInfo info = stackInfos.get(stackId);
+ ArrayList<Task> stackTasks = stacksTasks.valueAt(i);
+ TaskStack stack = new TaskStack(stackId);
+ if (Constants.DebugFlags.App.EnableMultiStackToSingleStack) {
+ stack.setBounds(displayBounds, displayBounds);
+ } else {
+ stack.setBounds(info.bounds, displayBounds);
+ }
+ stack.setTasks(stackTasks);
+ stack.createAffiliatedGroupings(mConfig);
+ mStacks.put(stackId, stack);
}
}
@@ -166,72 +204,93 @@ public class RecentsTaskLoadPlan {
Resources res = mContext.getResources();
// Iterate through each of the tasks and load them according to the load conditions.
- ArrayList<Task> tasks = mStack.getTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
- Task task = tasks.get(i);
- Task.TaskKey taskKey = task.key;
+ int stackCount = mStacks.size();
+ for (int j = 0; j < stackCount; j++) {
+ ArrayList<Task> tasks = mStacks.valueAt(j).getTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
+ Task task = tasks.get(i);
+ Task.TaskKey taskKey = task.key;
- // Get an existing activity info handle if possible
- Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
- ActivityInfoHandle infoHandle;
- boolean hadCachedActivityInfo = false;
- if (mActivityInfoCache.containsKey(cnKey)) {
- infoHandle = mActivityInfoCache.get(cnKey);
- hadCachedActivityInfo = true;
- } else {
- infoHandle = new ActivityInfoHandle();
- }
+ // Get an existing activity info handle if possible
+ Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
+ ActivityInfoHandle infoHandle;
+ boolean hadCachedActivityInfo = false;
+ if (mActivityInfoCache.containsKey(cnKey)) {
+ infoHandle = mActivityInfoCache.get(cnKey);
+ hadCachedActivityInfo = true;
+ } else {
+ infoHandle = new ActivityInfoHandle();
+ }
- boolean isRunningTask = (task.key.id == opts.runningTaskId);
- boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
- boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
+ boolean isRunningTask = (task.key.id == opts.runningTaskId);
+ boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
+ boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
- // If requested, skip the running task
- if (opts.onlyLoadPausedActivities && isRunningTask) {
- continue;
- }
+ // If requested, skip the running task
+ if (opts.onlyLoadPausedActivities && isRunningTask) {
+ continue;
+ }
- if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
- if (task.activityIcon == null) {
- if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey);
- task.activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription,
- mSystemServicesProxy, res, infoHandle, true);
+ if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
+ if (task.activityIcon == null) {
+ if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey);
+ task.activityIcon = loader.getAndUpdateActivityIcon(taskKey,
+ t.taskDescription, mSystemServicesProxy, res, infoHandle, true);
+ }
}
- }
- if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
- if (task.thumbnail == null || isRunningTask) {
- if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
- if (mConfig.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
- task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy,
- true);
- } else if (mConfig.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
- loadQueue.addTask(task);
+ if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
+ if (task.thumbnail == null || isRunningTask) {
+ if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
+ if (mConfig.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
+ task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
+ mSystemServicesProxy, true);
+ } else if (mConfig.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
+ loadQueue.addTask(task);
+ }
}
}
- }
- // Update the activity info cache
- if (!hadCachedActivityInfo && infoHandle.info != null) {
- mActivityInfoCache.put(cnKey, infoHandle);
+ // Update the activity info cache
+ if (!hadCachedActivityInfo && infoHandle.info != null) {
+ mActivityInfoCache.put(cnKey, infoHandle);
+ }
}
}
}
/**
- * Composes and returns a TaskStack from the preloaded list of recent tasks.
+ * Returns all TaskStacks from the preloaded list of recent tasks.
*/
- public TaskStack getTaskStack() {
- return mStack;
+ public ArrayList<TaskStack> getAllTaskStacks() {
+ ArrayList<TaskStack> stacks = new ArrayList<TaskStack>();
+ int stackCount = mStacks.size();
+ for (int i = 0; i < stackCount; i++) {
+ stacks.add(mStacks.valueAt(i));
+ }
+ // Ensure that we have at least one stack
+ if (stacks.isEmpty()) {
+ stacks.add(new TaskStack());
+ }
+ return stacks;
}
/**
- * Composes and returns a SpaceNode from the preloaded list of recent tasks.
+ * Returns a specific TaskStack from the preloaded list of recent tasks.
*/
- public SpaceNode getSpaceNode() {
- SpaceNode node = new SpaceNode();
- node.setStack(mStack);
- return node;
+ public TaskStack getTaskStack(int stackId) {
+ return mStacks.get(stackId);
+ }
+
+ /** Returns whether there are any tasks in any stacks. */
+ public boolean hasTasks() {
+ int stackCount = mStacks.size();
+ for (int i = 0; i < stackCount; i++) {
+ if (mStacks.valueAt(i).getTaskCount() > 0) {
+ return true;
+ }
+ }
+ return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java b/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
deleted file mode 100644
index 831698a..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.model;
-
-import android.graphics.Rect;
-
-import java.util.ArrayList;
-
-
-/**
- * The full recents space is partitioned using a BSP into various nodes that define where task
- * stacks should be placed.
- */
-public class SpaceNode {
- /* BSP node callbacks */
- public interface SpaceNodeCallbacks {
- /** Notifies when a node is added */
- public void onSpaceNodeAdded(SpaceNode node);
- /** Notifies when a node is measured */
- public void onSpaceNodeMeasured(SpaceNode node, Rect rect);
- }
-
- SpaceNode mStartNode;
- SpaceNode mEndNode;
-
- TaskStack mStack;
-
- public SpaceNode() {
- // Do nothing
- }
-
- /** Sets the current stack for this space node */
- public void setStack(TaskStack stack) {
- mStack = stack;
- }
-
- /** Returns the task stack (not null if this is a leaf) */
- TaskStack getStack() {
- return mStack;
- }
-
- /** Returns whether there are any tasks in any stacks below this node. */
- public boolean hasTasks() {
- return (mStack.getTaskCount() > 0) ||
- (mStartNode != null && mStartNode.hasTasks()) ||
- (mEndNode != null && mEndNode.hasTasks());
- }
-
- /** Returns whether this is a leaf node */
- boolean isLeafNode() {
- return (mStartNode == null) && (mEndNode == null);
- }
-
- /** Returns all the descendent task stacks */
- private void getStacksRec(ArrayList<TaskStack> stacks) {
- if (isLeafNode()) {
- stacks.add(mStack);
- } else {
- mStartNode.getStacksRec(stacks);
- mEndNode.getStacksRec(stacks);
- }
- }
- public ArrayList<TaskStack> getStacks() {
- ArrayList<TaskStack> stacks = new ArrayList<TaskStack>();
- getStacksRec(stacks);
- return stacks;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 55dfe45..0cd55d7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -36,6 +36,9 @@ public class Task {
public void onTaskDataLoaded();
/* Notifies when a task has been unbound */
public void onTaskDataUnloaded();
+
+ /* Notifies when a task's stack id has changed. */
+ public void onMultiStackDebugTaskStackIdChanged();
}
/** The ComponentNameKey represents the unique primary key for a component
@@ -68,14 +71,17 @@ public class Task {
public static class TaskKey {
final ComponentNameKey mComponentNameKey;
public final int id;
+ public int stackId;
public final Intent baseIntent;
public final int userId;
public long firstActiveTime;
public long lastActiveTime;
- public TaskKey(int id, Intent intent, int userId, long firstActiveTime, long lastActiveTime) {
+ public TaskKey(int id, int stackId, Intent intent, int userId, long firstActiveTime,
+ long lastActiveTime) {
mComponentNameKey = new ComponentNameKey(intent.getComponent(), userId);
this.id = id;
+ this.stackId = stackId;
this.baseIntent = intent;
this.userId = userId;
this.firstActiveTime = firstActiveTime;
@@ -92,18 +98,19 @@ public class Task {
if (!(o instanceof TaskKey)) {
return false;
}
- return id == ((TaskKey) o).id
- && userId == ((TaskKey) o).userId;
+ TaskKey otherKey = (TaskKey) o;
+ return id == otherKey.id && stackId == otherKey.stackId && userId == otherKey.userId;
}
@Override
public int hashCode() {
- return (id << 5) + userId;
+ return Objects.hash(id, stackId, userId);
}
@Override
public String toString() {
return "Task.Key: " + id + ", "
+ + "s: " + stackId + ", "
+ "u: " + userId + ", "
+ "lat: " + lastActiveTime + ", "
+ baseIntent.getComponent().getPackageName();
@@ -180,6 +187,14 @@ public class Task {
this.group = group;
}
+ /** Updates the stack id of this task. */
+ public void setStackId(int stackId) {
+ key.stackId = stackId;
+ if (mCb != null) {
+ mCb.onMultiStackDebugTaskStackIdChanged();
+ }
+ }
+
/** Notifies the callback listeners that this task has been loaded */
public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon) {
this.applicationIcon = applicationIcon;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 7f7eee4..5aaea15 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents.model;
import android.graphics.Color;
+import android.graphics.Rect;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.NamedCounter;
@@ -173,33 +174,38 @@ public class TaskStack {
public void onStackUnfiltered(TaskStack newStack, ArrayList<Task> curTasks);
}
- /** A pair of indices representing the group and task positions in the stack and group. */
- public static class GroupTaskIndex {
- public int groupIndex; // Index in the stack
- public int taskIndex; // Index in the group
-
- public GroupTaskIndex() {}
-
- public GroupTaskIndex(int gi, int ti) {
- groupIndex = gi;
- taskIndex = ti;
- }
- }
-
// The task offset to apply to a task id as a group affiliation
static final int IndividualTaskIdOffset = 1 << 16;
+ public final int id;
+ public final Rect stackBounds = new Rect();
+ public final Rect displayBounds = new Rect();
+
FilteredTaskList mTaskList = new FilteredTaskList();
TaskStackCallbacks mCb;
ArrayList<TaskGrouping> mGroups = new ArrayList<TaskGrouping>();
HashMap<Integer, TaskGrouping> mAffinitiesGroups = new HashMap<Integer, TaskGrouping>();
- /** Sets the callbacks for this task stack */
+ public TaskStack() {
+ this(0);
+ }
+
+ public TaskStack(int stackId) {
+ id = stackId;
+ }
+
+ /** Sets the callbacks for this task stack. */
public void setCallbacks(TaskStackCallbacks cb) {
mCb = cb;
}
+ /** Sets the bounds of this stack. */
+ public void setBounds(Rect stackBounds, Rect displayBounds) {
+ this.stackBounds.set(stackBounds);
+ this.displayBounds.set(displayBounds);
+ }
+
/** Resets this TaskStack. */
public void reset() {
mCb = null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 5152150..1bed553 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -29,8 +29,10 @@ import android.provider.Settings;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewStub;
import android.view.WindowInsets;
import android.widget.FrameLayout;
+import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -40,6 +42,7 @@ import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -56,13 +59,22 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
public void onAllTaskViewsDismissed();
public void onExitToHomeAnimationTriggered();
public void onScreenPinningRequest();
+
+ public void onMultiStackAddStack();
+ public void onMultiStackResizeStack();
+ public void onMultiStackRemoveStack();
+ public void onMultiStackMoveTask(Task t);
}
RecentsConfiguration mConfig;
LayoutInflater mInflater;
DebugOverlayView mDebugOverlay;
+ ViewStub mMultiStackDebugStub;
+ View mMultiStackDebugView;
+ RecentsViewLayoutAlgorithm mLayoutAlgorithm;
ArrayList<TaskStack> mStacks;
+ List<TaskStackView> mImmutableTaskStackViews = new ArrayList<TaskStackView>();
View mSearchBar;
RecentsViewCallbacks mCb;
@@ -82,6 +94,29 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = RecentsConfiguration.getInstance();
mInflater = LayoutInflater.from(context);
+ mLayoutAlgorithm = new RecentsViewLayoutAlgorithm(mConfig);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ if (!mConfig.multiStackEnabled) return;
+
+ mMultiStackDebugStub = (ViewStub) findViewById(R.id.multistack_debug_view_stub);
+ if (mMultiStackDebugView == null) {
+ mMultiStackDebugView = mMultiStackDebugStub.inflate();
+ mMultiStackDebugView.findViewById(R.id.add_stack).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCb.onMultiStackAddStack();
+ }
+ });
+ mMultiStackDebugView.findViewById(R.id.resize_stack).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCb.onMultiStackResizeStack();
+ }
+ });
+ }
}
/** Sets the callbacks */
@@ -99,24 +134,19 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
int numStacks = stacks.size();
// Make a list of the stack view children only
- ArrayList<TaskStackView> stackViews = new ArrayList<TaskStackView>();
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- stackViews.add((TaskStackView) child);
- }
- }
+ ArrayList<TaskStackView> stackViewsList = new ArrayList<TaskStackView>();
+ List<TaskStackView> stackViews = getTaskStackViews();
// Remove all/extra stack views
int numTaskStacksToKeep = 0; // Keep no tasks if we are recreating the layout
if (mConfig.launchedReuseTaskStackViews) {
- numTaskStacksToKeep = Math.min(childCount, numStacks);
+ numTaskStacksToKeep = Math.min(stackViews.size(), numStacks);
}
for (int i = stackViews.size() - 1; i >= numTaskStacksToKeep; i--) {
removeView(stackViews.get(i));
stackViews.remove(i);
}
+ stackViewsList.addAll(stackViews);
// Update the stack views that we are keeping
for (int i = 0; i < numTaskStacksToKeep; i++) {
@@ -133,42 +163,51 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
TaskStackView stackView = new TaskStackView(getContext(), stack);
stackView.setCallbacks(this);
addView(stackView);
+ stackViewsList.add(stackView);
}
+ // Set the immutable stack views list
+ mImmutableTaskStackViews = Collections.unmodifiableList(stackViewsList);
+
// Enable debug mode drawing on all the stacks if necessary
if (mConfig.debugModeEnabled) {
- for (int i = childCount - 1; i >= 0; i--) {
- View v = getChildAt(i);
- if (v != mSearchBar) {
- TaskStackView stackView = (TaskStackView) v;
- stackView.setDebugOverlay(mDebugOverlay);
- }
+ for (int i = mImmutableTaskStackViews.size() - 1; i >= 0; i--) {
+ TaskStackView stackView = mImmutableTaskStackViews.get(i);
+ stackView.setDebugOverlay(mDebugOverlay);
}
}
+ // Bring the debug view to the front
+ if (mMultiStackDebugView != null) {
+ mMultiStackDebugView.bringToFront();
+ }
+
// Trigger a new layout
requestLayout();
}
+ /** Gets the list of task views */
+ List<TaskStackView> getTaskStackViews() {
+ return mImmutableTaskStackViews;
+ }
+
/** Launches the focused task from the first stack if possible */
public boolean launchFocusedTask() {
// Get the first stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- TaskStack stack = stackView.mStack;
- // Iterate the stack views and try and find the focused task
- List<TaskView> taskViews = stackView.getTaskViews();
- int taskViewCount = taskViews.size();
- for (int j = 0; j < taskViewCount; j++) {
- TaskView tv = taskViews.get(j);
- Task task = tv.getTask();
- if (tv.isFocusedTask()) {
- onTaskViewClicked(stackView, tv, stack, task, false);
- return true;
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ TaskStack stack = stackView.getStack();
+ // Iterate the stack views and try and find the focused task
+ List<TaskView> taskViews = stackView.getTaskViews();
+ int taskViewCount = taskViews.size();
+ for (int j = 0; j < taskViewCount; j++) {
+ TaskView tv = taskViews.get(j);
+ Task task = tv.getTask();
+ if (tv.isFocusedTask()) {
+ onTaskViewClicked(stackView, tv, stack, task, false);
+ return true;
}
}
}
@@ -178,24 +217,22 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
/** Launches the task that Recents was launched from, if possible */
public boolean launchPreviousTask() {
// Get the first stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- TaskStack stack = stackView.mStack;
- ArrayList<Task> tasks = stack.getTasks();
-
- // Find the launch task in the stack
- if (!tasks.isEmpty()) {
- int taskCount = tasks.size();
- for (int j = 0; j < taskCount; j++) {
- if (tasks.get(j).isLaunchTarget) {
- Task task = tasks.get(j);
- TaskView tv = stackView.getChildViewForTask(task);
- onTaskViewClicked(stackView, tv, stack, task, false);
- return true;
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ TaskStack stack = stackView.getStack();
+ ArrayList<Task> tasks = stack.getTasks();
+
+ // Find the launch task in the stack
+ if (!tasks.isEmpty()) {
+ int taskCount = tasks.size();
+ for (int j = 0; j < taskCount; j++) {
+ if (tasks.get(j).isLaunchTarget) {
+ Task task = tasks.get(j);
+ TaskView tv = stackView.getChildViewForTask(task);
+ onTaskViewClicked(stackView, tv, stack, task, false);
+ return true;
}
}
}
@@ -209,13 +246,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
// to ensure that it runs
ctx.postAnimationTrigger.increment();
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- stackView.startEnterRecentsAnimation(ctx);
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ stackView.startEnterRecentsAnimation(ctx);
}
ctx.postAnimationTrigger.decrement();
}
@@ -225,13 +260,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
// We have to increment/decrement the post animation trigger in case there are no children
// to ensure that it runs
ctx.postAnimationTrigger.increment();
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- stackView.startExitToHomeAnimation(ctx);
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ stackView.startExitToHomeAnimation(ctx);
}
ctx.postAnimationTrigger.decrement();
@@ -287,22 +320,31 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
Rect taskStackBounds = new Rect();
- mConfig.getTaskStackBounds(width, height, mConfig.systemInsets.top,
+ mConfig.getAvailableTaskStackBounds(width, height, mConfig.systemInsets.top,
mConfig.systemInsets.right, taskStackBounds);
- // Measure each TaskStackView with the full width and height of the window since the
+ // Measure each TaskStackView with the full width and height of the window since the
// transition view is a child of that stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar && child.getVisibility() != GONE) {
- TaskStackView tsv = (TaskStackView) child;
- // Set the insets to be the top/left inset + search bounds
- tsv.setStackInsetRect(taskStackBounds);
- tsv.measure(widthMeasureSpec, heightMeasureSpec);
+ List<TaskStackView> stackViews = getTaskStackViews();
+ List<Rect> stackViewsBounds = mLayoutAlgorithm.computeStackRects(stackViews,
+ taskStackBounds);
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ if (stackView.getVisibility() != GONE) {
+ // We are going to measure the TaskStackView with the whole RecentsView dimensions,
+ // but the actual stack is going to be inset to the bounds calculated by the layout
+ // algorithm
+ stackView.setStackInsetRect(stackViewsBounds.get(i));
+ stackView.measure(widthMeasureSpec, heightMeasureSpec);
}
}
+ // Measure the multistack debug view
+ if (mMultiStackDebugView != null) {
+ mMultiStackDebugView.measure(width, height);
+ }
+
setMeasuredDimension(width, height);
}
@@ -322,14 +364,27 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
// Layout each TaskStackView with the full width and height of the window since the
// transition view is a child of that stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar && child.getVisibility() != GONE) {
- child.layout(left, top, left + child.getMeasuredWidth(),
- top + child.getMeasuredHeight());
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ if (stackView.getVisibility() != GONE) {
+ stackView.layout(left, top, left + stackView.getMeasuredWidth(),
+ top + stackView.getMeasuredHeight());
}
}
+
+ // Layout the multistack debug view
+ if (mMultiStackDebugView != null) {
+ Rect taskStackBounds = new Rect();
+ mConfig.getAvailableTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(),
+ mConfig.systemInsets.top, mConfig.systemInsets.right, taskStackBounds);
+ mMultiStackDebugView.layout(left,
+ taskStackBounds.bottom - mConfig.systemInsets.bottom -
+ mMultiStackDebugView.getMeasuredHeight(),
+ left + mMultiStackDebugView.getMeasuredWidth(),
+ taskStackBounds.bottom - mConfig.systemInsets.bottom);
+ }
}
@Override
@@ -343,41 +398,29 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
/** Notifies each task view of the user interaction. */
public void onUserInteraction() {
// Get the first stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- stackView.onUserInteraction();
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ stackView.onUserInteraction();
}
}
/** Focuses the next task in the first stack view */
public void focusNextTask(boolean forward) {
// Get the first stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- stackView.focusNextTask(forward, true);
- break;
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ if (!stackViews.isEmpty()) {
+ stackViews.get(0).focusNextTask(forward, true);
}
}
/** Dismisses the focused task. */
public void dismissFocusedTask() {
// Get the first stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- stackView.dismissFocusedTask();
- break;
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ if (!stackViews.isEmpty()) {
+ stackViews.get(0).dismissFocusedTask();
}
}
@@ -476,9 +519,16 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
};
}
- opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
- b, offsetX, offsetY, transform.rect.width(), transform.rect.height(),
- sourceView.getHandler(), animStartedListener);
+ if (mConfig.multiStackEnabled) {
+ opts = ActivityOptions.makeCustomAnimation(sourceView.getContext(),
+ R.anim.recents_from_unknown_enter,
+ R.anim.recents_from_unknown_exit,
+ sourceView.getHandler(), animStartedListener);
+ } else {
+ opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
+ b, offsetX, offsetY, transform.rect.width(), transform.rect.height(),
+ sourceView.getHandler(), animStartedListener);
+ }
}
final ActivityOptions launchOpts = opts;
@@ -561,13 +611,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
/** Final callback after Recents is finally hidden. */
public void onRecentsHidden() {
// Notify each task stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- stackView.onRecentsHidden();
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ stackView.onRecentsHidden();
}
}
@@ -599,18 +647,23 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
}
+ @Override
+ public void onMultiStackMoveTask(Task t) {
+ if (mCb != null) {
+ mCb.onMultiStackMoveTask(t);
+ }
+ }
+
/**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
@Override
public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) {
// Propagate this event down to each task stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- stackView.onPackagesChanged(monitor, packageName, userId);
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ stackView.onPackagesChanged(monitor, packageName, userId);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java
new file mode 100644
index 0000000..eea273c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.graphics.Rect;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.model.TaskStack;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/* The layout logic for the RecentsView. */
+public class RecentsViewLayoutAlgorithm {
+
+ RecentsConfiguration mConfig;
+
+ public RecentsViewLayoutAlgorithm(RecentsConfiguration config) {
+ mConfig = config;
+ }
+
+ /** Return the relative coordinate given coordinates in another size. */
+ private int getRelativeCoordinate(int availableOffset, int availableSize, int otherCoord, int otherSize) {
+ float relPos = (float) otherCoord / otherSize;
+ return availableOffset + (int) (relPos * availableSize);
+ }
+
+ /**
+ * Computes and returns the bounds that each of the stack views should take up.
+ */
+ List<Rect> computeStackRects(List<TaskStackView> stackViews, Rect availableBounds) {
+ ArrayList<Rect> bounds = new ArrayList<Rect>(stackViews.size());
+ int stackViewsCount = stackViews.size();
+ for (int i = 0; i < stackViewsCount; i++) {
+ TaskStack stack = stackViews.get(i).getStack();
+ Rect sb = stack.stackBounds;
+ Rect db = stack.displayBounds;
+ Rect ab = availableBounds;
+ bounds.add(new Rect(getRelativeCoordinate(ab.left, ab.width(), sb.left, db.width()),
+ getRelativeCoordinate(ab.top, ab.height(), sb.top, db.height()),
+ getRelativeCoordinate(ab.left, ab.width(), sb.right, db.width()),
+ getRelativeCoordinate(ab.top, ab.height(), sb.bottom, db.height())));
+ }
+ return bounds;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 290792a..2318319 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -60,6 +60,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
public void onAllTaskViewsDismissed(ArrayList<Task> removedTasks);
public void onTaskStackFilterTriggered();
public void onTaskStackUnfilterTriggered();
+
+ public void onMultiStackMoveTask(Task t);
}
RecentsConfiguration mConfig;
@@ -149,6 +151,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
requestLayout();
}
+ /** Returns the task stack. */
+ TaskStack getStack() {
+ return mStack;
+ }
+
/** Sets the debug overlay */
public void setDebugOverlay(DebugOverlayView overlay) {
mDebugOverlay = overlay;
@@ -625,6 +632,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
return mTouchHandler.onGenericMotionEvent(ev);
}
+ /** Returns the region that touch gestures can be started in. */
+ Rect getTouchableRegion() {
+ return mTaskStackBounds;
+ }
+
@Override
public void computeScroll() {
mStackScroller.computeScroll();
@@ -1326,6 +1338,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
+ @Override
+ public void onMultiStackMoveTask(TaskView tv) {
+ if (mCb != null) {
+ mCb.onMultiStackMoveTask(tv.getTask());
+ }
+ }
+
/**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index ccad2f1..fabc86d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -96,16 +96,6 @@ public class TaskStackViewScroller {
}
return false;
}
- /** Bounds the current scroll if necessary, but does not synchronize the stack view with the model. */
- public boolean boundScrollRaw() {
- float curScroll = getStackScroll();
- float newScroll = getBoundedStackScroll(curScroll);
- if (Float.compare(newScroll, curScroll) != 0) {
- setStackScrollRaw(newScroll);
- return true;
- }
- return false;
- }
/** Returns the bounded stack scroll */
float getBoundedStackScroll(float scroll) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 4a6112c..6cdddc5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -123,6 +123,16 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
return false;
}
+ int action = ev.getAction();
+ if (mConfig.multiStackEnabled) {
+ // Check if we are within the bounds of the stack view contents
+ if ((action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
+ if (!mSv.getTouchableRegion().contains((int) ev.getX(), (int) ev.getY())) {
+ return false;
+ }
+ }
+ }
+
// Pass through to swipe helper if we are swiping
mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
if (mInterceptedBySwipeHelper) {
@@ -131,7 +141,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
boolean wasScrolling = mScroller.isScrolling() ||
(mScroller.mScrollAnimator != null && mScroller.mScrollAnimator.isRunning());
- int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
// Save the touch down info
@@ -198,6 +207,16 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
return false;
}
+ int action = ev.getAction();
+ if (mConfig.multiStackEnabled) {
+ // Check if we are within the bounds of the stack view contents
+ if ((action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
+ if (!mSv.getTouchableRegion().contains((int) ev.getX(), (int) ev.getY())) {
+ return false;
+ }
+ }
+ }
+
// Pass through to swipe helper if we are swiping
if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
return true;
@@ -206,7 +225,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
// Update the velocity tracker
initVelocityTrackerIfNotExists();
- int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
// Save the touch down info
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 82120bf..098f2f9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -45,6 +45,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
public void onTaskViewDismissed(TaskView tv);
public void onTaskViewClipStateChanged(TaskView tv);
public void onTaskViewFocusChanged(TaskView tv, boolean focused);
+
+ public void onMultiStackMoveTask(TaskView tv);
}
RecentsConfiguration mConfig;
@@ -457,6 +459,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
.start();
}
+ /** Enables/disables handling touch on this task view. */
+ void setTouchEnabled(boolean enabled) {
+ setOnClickListener(enabled ? this : null);
+ }
+
/** Animates this task view if the user does not interact with the stack after a certain time. */
void startNoUserInteractionAnimation() {
mHeaderView.startNoUserInteractionAnimation();
@@ -667,6 +674,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
// Rebind any listeners
mHeaderView.mApplicationIcon.setOnClickListener(this);
mHeaderView.mDismissButton.setOnClickListener(this);
+ if (mConfig.multiStackEnabled) {
+ mHeaderView.mMoveTaskButton.setOnClickListener(this);
+ }
mActionButtonView.setOnClickListener(this);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
if (mConfig.developerOptionsEnabled) {
@@ -687,6 +697,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
// Unbind any listeners
mHeaderView.mApplicationIcon.setOnClickListener(null);
mHeaderView.mDismissButton.setOnClickListener(null);
+ if (mConfig.multiStackEnabled) {
+ mHeaderView.mMoveTaskButton.setOnClickListener(null);
+ }
mActionButtonView.setOnClickListener(null);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
mHeaderView.mApplicationIcon.setOnLongClickListener(null);
@@ -695,9 +708,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
mTaskDataLoaded = false;
}
- /** Enables/disables handling touch on this task view. */
- void setTouchEnabled(boolean enabled) {
- setOnClickListener(enabled ? this : null);
+ @Override
+ public void onMultiStackDebugTaskStackIdChanged() {
+ mHeaderView.rebindToTask(mTask);
}
/**** View.OnClickListener Implementation ****/
@@ -717,6 +730,10 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
}
} else if (v == mHeaderView.mDismissButton) {
dismissTask();
+ } else if (v == mHeaderView.mMoveTaskButton) {
+ if (mCb != null) {
+ mCb.onMultiStackMoveTask(tv);
+ }
}
}
}, 125);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index e13eed8..b827acc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -56,6 +56,7 @@ public class TaskViewHeader extends FrameLayout {
RecentsConfiguration mConfig;
// Header views
+ ImageView mMoveTaskButton;
ImageView mDismissButton;
ImageView mApplicationIcon;
TextView mActivityDescription;
@@ -126,6 +127,10 @@ public class TaskViewHeader extends FrameLayout {
mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
mActivityDescription = (TextView) findViewById(R.id.activity_description);
mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
+ mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
+ if (mConfig.multiStackEnabled) {
+ mMoveTaskButton.setVisibility(View.VISIBLE);
+ }
// Hide the backgrounds if they are ripple drawables
if (!Constants.DebugFlags.App.EnableTaskFiltering) {
@@ -188,7 +193,10 @@ public class TaskViewHeader extends FrameLayout {
mApplicationIcon.setImageDrawable(t.applicationIcon);
}
mApplicationIcon.setContentDescription(t.activityLabel);
- if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
+ // Always update when multi stack debugging is enabled as the stack id can change
+ if (mConfig.multiStackEnabled) {
+ mActivityDescription.setText("[" + t.key.stackId + "] " + t.activityLabel);
+ } else if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
mActivityDescription.setText(t.activityLabel);
}
// Try and apply the system ui tint
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index a2ff64a..747e702 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -148,7 +148,8 @@ public class NotificationPanelView extends PanelView implements
private boolean mBlockTouches;
private int mNotificationScrimWaitDistance;
- private boolean mTwoFingerQsExpand;
+ // Used for two finger gesture as well as accessibility shortcut to QS.
+ private boolean mQsExpandImmediate;
private boolean mTwoFingerQsExpandPossible;
/**
@@ -475,6 +476,13 @@ public class NotificationPanelView extends PanelView implements
}
}
+ public void expandWithQs() {
+ if (mQsExpansionEnabled) {
+ mQsExpandImmediate = true;
+ }
+ expand();
+ }
+
@Override
public void fling(float vel, boolean expand) {
GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
@@ -658,7 +666,7 @@ public class NotificationPanelView extends PanelView implements
if (mExpandedHeight != 0) {
handleQsDown(event);
}
- if (!mTwoFingerQsExpand && mQsTracking) {
+ if (!mQsExpandImmediate && mQsTracking) {
onQsTouch(event);
if (!mConflictingQsExpansionGesture) {
return true;
@@ -675,7 +683,7 @@ public class NotificationPanelView extends PanelView implements
if (mTwoFingerQsExpandPossible && event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN
&& event.getPointerCount() == 2
&& event.getY(event.getActionIndex()) < mStatusBarMinHeight) {
- mTwoFingerQsExpand = true;
+ mQsExpandImmediate = true;
requestPanelHeightUpdate();
// Normally, we start listening when the panel is expanded, but here we need to start
@@ -1166,7 +1174,7 @@ public class NotificationPanelView extends PanelView implements
private float calculateQsTopPadding() {
if (mKeyguardShowing
- && (mTwoFingerQsExpand || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
+ && (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
// Either QS pushes the notifications down when fully expanded, or QS is fully above the
// notifications (mostly on tablets). maxNotifications denotes the normal top padding
@@ -1200,7 +1208,7 @@ public class NotificationPanelView extends PanelView implements
mScrollView.getScrollY(),
mAnimateNextTopPaddingChange || animate,
mKeyguardShowing
- && (mTwoFingerQsExpand || mIsExpanding && mQsExpandedWhenExpandingStarted));
+ && (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted));
mAnimateNextTopPaddingChange = false;
}
@@ -1313,7 +1321,7 @@ public class NotificationPanelView extends PanelView implements
min = Math.max(min, minHeight);
}
int maxHeight;
- if (mTwoFingerQsExpand || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted) {
+ if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted) {
maxHeight = calculatePanelHeightQsExpanded();
} else {
maxHeight = calculatePanelHeightShade();
@@ -1328,10 +1336,10 @@ public class NotificationPanelView extends PanelView implements
@Override
protected void onHeightUpdated(float expandedHeight) {
- if (!mQsExpanded || mTwoFingerQsExpand || mIsExpanding && mQsExpandedWhenExpandingStarted) {
+ if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
positionClockAndNotifications();
}
- if (mTwoFingerQsExpand || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
+ if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
&& !mQsExpansionFromOverscroll) {
float t;
if (mKeyguardShowing) {
@@ -1555,7 +1563,7 @@ public class NotificationPanelView extends PanelView implements
} else {
setListening(true);
}
- mTwoFingerQsExpand = false;
+ mQsExpandImmediate = false;
mTwoFingerQsExpandPossible = false;
}
@@ -1573,7 +1581,7 @@ public class NotificationPanelView extends PanelView implements
@Override
protected void setOverExpansion(float overExpansion, boolean isPixels) {
- if (mConflictingQsExpansionGesture || mTwoFingerQsExpand) {
+ if (mConflictingQsExpansionGesture || mQsExpandImmediate) {
return;
}
if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
@@ -1593,7 +1601,7 @@ public class NotificationPanelView extends PanelView implements
protected void onTrackingStarted() {
super.onTrackingStarted();
if (mQsFullyExpanded) {
- mTwoFingerQsExpand = true;
+ mQsExpandImmediate = true;
}
if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
@@ -1817,7 +1825,7 @@ public class NotificationPanelView extends PanelView implements
@Override
protected boolean fullyExpandedClearAllVisible() {
return mNotificationStackScroller.isDismissViewNotGone()
- && mNotificationStackScroller.isScrolledToBottom() && !mTwoFingerQsExpand;
+ && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 25a2f93..d286441 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -2206,8 +2206,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
// Settings are not available in setup
if (!mUserSetup) return;
- mNotificationPanel.expand();
- mNotificationPanel.openQs();
+ mNotificationPanel.expandWithQs();
if (false) postStartTracing();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bef561e..287cd6f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -87,6 +87,7 @@ import com.android.server.pm.UserManagerService;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.AppTransition;
import com.android.server.wm.WindowManagerService;
+
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -1854,9 +1855,14 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (ActivityManagerService.this) {
if (DEBUG_PSS) Slog.d(TAG, "Collected native and kernel memory in "
+ (SystemClock.uptimeMillis()-start) + "ms");
- mProcessStats.addSysMemUsageLocked(memInfo.getCachedSizeKb(),
- memInfo.getFreeSizeKb(), memInfo.getZramTotalSizeKb(),
- memInfo.getKernelUsedSizeKb(), nativeTotalPss);
+ final long cachedKb = memInfo.getCachedSizeKb();
+ final long freeKb = memInfo.getFreeSizeKb();
+ final long zramKb = memInfo.getZramTotalSizeKb();
+ final long kernelKb = memInfo.getKernelUsedSizeKb();
+ EventLogTags.writeAmMeminfo(cachedKb*1024, freeKb*1024, zramKb*1024,
+ kernelKb*1024, nativeTotalPss*1024);
+ mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
+ nativeTotalPss);
}
}
@@ -7056,7 +7062,6 @@ public final class ActivityManagerService extends ActivityManagerNative
return;
}
- final IPackageManager pm = AppGlobals.getPackageManager();
final String authority = uri.getAuthority();
final ProviderInfo pi = getProviderInfoLocked(authority, userId);
if (pi == null) {
@@ -8167,7 +8172,7 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public void resizeStack(int stackId, Rect bounds) {
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
- "resizeStackBox()");
+ "resizeStack()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
@@ -10039,7 +10044,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
final boolean translucentChanged = r.changeWindowTranslucency(true);
if (translucentChanged) {
- r.task.stack.releaseBackgroundResources();
+ r.task.stack.releaseBackgroundResources(r);
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
}
mWindowManager.setAppFullscreen(token, true);
@@ -10066,7 +10071,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
final boolean translucentChanged = r.changeWindowTranslucency(false);
if (translucentChanged) {
- r.task.stack.convertToTranslucent(r);
+ r.task.stack.convertActivityToTranslucent(r);
}
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
mWindowManager.setAppFullscreen(token, false);
@@ -13902,9 +13907,14 @@ public final class ActivityManagerService extends ActivityManagerNative
memInfo.readMemInfo();
if (nativeProcTotalPss > 0) {
synchronized (this) {
- mProcessStats.addSysMemUsageLocked(memInfo.getCachedSizeKb(),
- memInfo.getFreeSizeKb(), memInfo.getZramTotalSizeKb(),
- memInfo.getKernelUsedSizeKb(), nativeProcTotalPss);
+ final long cachedKb = memInfo.getCachedSizeKb();
+ final long freeKb = memInfo.getFreeSizeKb();
+ final long zramKb = memInfo.getZramTotalSizeKb();
+ final long kernelKb = memInfo.getKernelUsedSizeKb();
+ EventLogTags.writeAmMeminfo(cachedKb*1024, freeKb*1024, zramKb*1024,
+ kernelKb*1024, nativeProcTotalPss*1024);
+ mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
+ nativeProcTotalPss);
}
}
if (!brief) {
@@ -15783,14 +15793,14 @@ public final class ActivityManagerService extends ActivityManagerNative
callerPackage, callingPid, callingUid, resolvedType,
requiredPermission, appOp, receivers, resultTo, resultCode,
resultData, map, ordered, sticky, false, userId);
+
if (DEBUG_BROADCAST) Slog.v(
TAG, "Enqueueing ordered broadcast " + r
+ ": prev had " + queue.mOrderedBroadcasts.size());
- if (DEBUG_BROADCAST) {
- int seq = r.intent.getIntExtra("seq", -1);
- Slog.i(TAG, "Enqueueing broadcast " + r.intent.getAction() + " seq=" + seq);
- }
- boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
+ if (DEBUG_BROADCAST) Slog.i(
+ TAG, "Enqueueing broadcast " + r.intent.getAction());
+
+ boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
@@ -16121,6 +16131,15 @@ public final class ActivityManagerService extends ActivityManagerNative
return mStackSupervisor.getFocusedStack();
}
+ @Override
+ public int getFocusedStackId() throws RemoteException {
+ ActivityStack focusedStack = getFocusedStack();
+ if (focusedStack != null) {
+ return focusedStack.getStackId();
+ }
+ return -1;
+ }
+
public Configuration getConfiguration() {
Configuration ci;
synchronized(this) {
@@ -17098,6 +17117,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* Record new PSS sample for a process.
*/
void recordPssSample(ProcessRecord proc, int procState, long pss, long uss, long now) {
+ EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss*1024, uss*1024);
proc.lastPssTime = now;
proc.baseProcessTracker.addPss(pss, uss, true, proc.pkgList);
if (DEBUG_PSS) Slog.d(TAG, "PSS of " + proc.toShortString()
@@ -18351,8 +18371,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- private Set getProfileIdsLocked(int userId) {
- Set userIds = new HashSet<Integer>();
+ private Set<Integer> getProfileIdsLocked(int userId) {
+ Set<Integer> userIds = new HashSet<Integer>();
final List<UserInfo> profiles = getUserManagerLocked().getProfiles(
userId, false /* enabledOnly */);
for (UserInfo user : profiles) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index dbd787b..91013ef 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -220,6 +220,9 @@ final class ActivityStack {
*/
boolean mConfigWillChange;
+ // Whether or not this stack covers the entire screen; by default stacks are full screen
+ boolean mFullscreen = true;
+
long mLaunchStartTime = 0;
long mFullyDrawnStartTime = 0;
@@ -1125,7 +1128,8 @@ final class ActivityStack {
final int numStacks = mStacks.size();
while (stackNdx < numStacks) {
- tasks = mStacks.get(stackNdx).mTaskHistory;
+ ActivityStack historyStack = mStacks.get(stackNdx);
+ tasks = historyStack.mTaskHistory;
final int numTasks = tasks.size();
while (taskNdx < numTasks) {
activities = tasks.get(taskNdx).mActivities;
@@ -1133,7 +1137,7 @@ final class ActivityStack {
while (activityNdx < numActivities) {
final ActivityRecord activity = activities.get(activityNdx);
if (!activity.finishing) {
- return activity.fullscreen ? null : activity;
+ return historyStack.mFullscreen && activity.fullscreen ? null : activity;
}
++activityNdx;
}
@@ -1149,7 +1153,7 @@ final class ActivityStack {
// Checks if any of the stacks above this one has a fullscreen activity behind it.
// If so, this stack is hidden, otherwise it is visible.
- private boolean isStackVisible() {
+ private boolean isStackVisibleLocked() {
if (!isAttached()) {
return false;
}
@@ -1164,11 +1168,18 @@ final class ActivityStack {
* wallpaper to be shown behind it.
*/
for (int i = mStacks.indexOf(this) + 1; i < mStacks.size(); i++) {
- final ArrayList<TaskRecord> tasks = mStacks.get(i).getAllTasks();
- for (int taskNdx = 0; taskNdx < tasks.size(); taskNdx++) {
+ ActivityStack stack = mStacks.get(i);
+ // stack above isn't full screen, so, we assume we're still visible. at some point
+ // we should look at the stack bounds to see if we're occluded even if the stack
+ // isn't fullscreen
+ if (!stack.mFullscreen) {
+ continue;
+ }
+ final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = tasks.get(taskNdx);
final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = 0; activityNdx < activities.size(); activityNdx++) {
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
// Conditions for an activity to obscure the stack we're
@@ -1214,7 +1225,7 @@ final class ActivityStack {
// If the top activity is not fullscreen, then we need to
// make sure any activities under it are now visible.
boolean aboveTop = true;
- boolean behindFullscreen = !isStackVisible();
+ boolean behindFullscreen = !isStackVisibleLocked();
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -1337,7 +1348,7 @@ final class ActivityStack {
// This case created for transitioning activities from
// translucent to opaque {@link Activity#convertToOpaque}.
if (getVisibleBehindActivity() == r) {
- releaseBackgroundResources();
+ releaseBackgroundResources(r);
} else {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
mStackSupervisor.mStoppingActivities.add(r);
@@ -1369,7 +1380,7 @@ final class ActivityStack {
}
}
- void convertToTranslucent(ActivityRecord r) {
+ void convertActivityToTranslucent(ActivityRecord r) {
mTranslucentActivityWaiting = r;
mUndrawnActivitiesBelowTopTranslucent.clear();
mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
@@ -3282,10 +3293,9 @@ final class ActivityStack {
}
}
- void releaseBackgroundResources() {
+ void releaseBackgroundResources(ActivityRecord r) {
if (hasVisibleBehindActivity() &&
!mHandler.hasMessages(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG)) {
- final ActivityRecord r = getVisibleBehindActivity();
if (r == topRunningActivityLocked(null)) {
// Don't release the top activity if it has requested to run behind the next
// activity.
@@ -4143,6 +4153,10 @@ final class ActivityStack {
boolean updateOverrideConfiguration(Configuration newConfig) {
Configuration oldConfig = mOverrideConfig;
mOverrideConfig = (newConfig == null) ? Configuration.EMPTY : newConfig;
+ // we override the configuration only when the stack's dimensions are different from
+ // the display. in this manner, we know that if the override configuration is empty,
+ // the stack is necessarily full screen
+ mFullscreen = Configuration.EMPTY.equals(mOverrideConfig);
return !mOverrideConfig.equals(oldConfig);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 4674cc2..b4455b6 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3752,6 +3752,13 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
@Override
+ public int getStackId() {
+ synchronized (mService) {
+ return mStackId;
+ }
+ }
+
+ @Override
public boolean injectEvent(InputEvent event) {
final long origId = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 9b7d0b2..7ab3794 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -513,11 +513,7 @@ public final class BroadcastQueue {
}
}
try {
- if (DEBUG_BROADCAST_LIGHT) {
- int seq = r.intent.getIntExtra("seq", -1);
- Slog.i(TAG, "Delivering to " + filter
- + " (seq=" + seq + "): " + r);
- }
+ if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG, "Delivering to " + filter + " : " + r);
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
@@ -662,12 +658,9 @@ public final class BroadcastQueue {
// result if requested...
if (r.resultTo != null) {
try {
- if (DEBUG_BROADCAST) {
- int seq = r.intent.getIntExtra("seq", -1);
- Slog.i(TAG, "Finishing broadcast ["
- + mQueueName + "] " + r.intent.getAction()
- + " seq=" + seq + " app=" + r.callerApp);
- }
+ if (DEBUG_BROADCAST) Slog.i(TAG,
+ "Finishing broadcast [" + mQueueName + "] "
+ + r.intent.getAction() + " app=" + r.callerApp);
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId);
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index e43e717..9a645df 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -98,3 +98,8 @@ option java_package com.android.server.am
# Running pre boot receiver
30045 am_pre_boot (User|1|5),(Package|3)
+
+# Report collection of global memory state
+30046 am_meminfo (CachedKb|2|2),(FreeKb|2|2),(ZramKb|2|2),(KernelKb|2|2),(NativeKb|2|2)
+# Report collection of memory used by a process
+30047 am_pss (Pid|1|5),(UID|1|5),(Process Name|3),(PssKb|2|2),(UssKb|2|2)
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index ce52920..a8f6954 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -55,10 +55,6 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
mAddress, mService.getVendorId()));
startQueuedActions();
-
- // Switch TV input after bootup.
- setActiveSource(true);
- maySendActiveSource(Constants.ADDR_TV);
}
@Override
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index da63caa..df31158 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -250,77 +250,37 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
if (isPlaybackActive(false) || hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
flags &= ~AudioManager.FLAG_PLAY_SOUND;
}
- boolean isMute = direction == MediaSessionManager.DIRECTION_MUTE;
- if (direction > 1) {
- direction = 1;
- } else if (direction < -1) {
- direction = -1;
- }
if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
if (mUseMasterVolume) {
// If this device only uses master volume and playback is local
// just adjust the master volume and return.
- boolean isMasterMute = mAudioManager.isMasterMute();
- if (isMute) {
- mAudioManagerInternal.setMasterMuteForUid(!isMasterMute,
- flags, packageName, mService.mICallback, uid);
- } else {
- mAudioManagerInternal.adjustMasterVolumeForUid(direction, flags, packageName,
- uid);
- if (isMasterMute) {
- mAudioManagerInternal.setMasterMuteForUid(false,
- flags, packageName, mService.mICallback, uid);
- }
- }
+ mAudioManagerInternal.adjustMasterVolumeForUid(direction, flags, packageName,
+ uid);
return;
}
int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
- boolean isStreamMute = mAudioManager.isStreamMute(stream);
if (useSuggested) {
if (AudioSystem.isStreamActive(stream, 0)) {
- if (isMute) {
- mAudioManager.setStreamMute(stream, !isStreamMute);
- } else {
- mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction,
- flags, packageName, uid);
- if (isStreamMute && direction != 0) {
- mAudioManager.setStreamMute(stream, false);
- }
- }
+ mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction,
+ flags, packageName, uid);
} else {
flags |= previousFlagPlaySound;
- isStreamMute =
- mAudioManager.isStreamMute(AudioManager.USE_DEFAULT_STREAM_TYPE);
- if (isMute) {
- mAudioManager.setStreamMute(AudioManager.USE_DEFAULT_STREAM_TYPE,
- !isStreamMute);
- } else {
- mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
- AudioManager.USE_DEFAULT_STREAM_TYPE, direction, flags, packageName,
- uid);
- if (isStreamMute && direction != 0) {
- mAudioManager.setStreamMute(AudioManager.USE_DEFAULT_STREAM_TYPE,
- false);
- }
- }
+ mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
+ AudioManager.USE_DEFAULT_STREAM_TYPE, direction, flags, packageName,
+ uid);
}
} else {
- if (isMute) {
- mAudioManager.setStreamMute(stream, !isStreamMute);
- } else {
- mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
- packageName, uid);
- if (isStreamMute && direction != 0) {
- mAudioManager.setStreamMute(stream, false);
- }
- }
+ mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
+ packageName, uid);
}
} else {
if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
// Nothing to do, the volume cannot be changed
return;
}
- if (isMute) {
+ if (direction == AudioManager.ADJUST_TOGGLE_MUTE
+ || direction == AudioManager.ADJUST_MUTE
+ || direction == AudioManager.ADJUST_UNMUTE) {
Log.w(TAG, "Muting remote playback is not supported");
return;
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 667d02a..0500f94 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -872,30 +872,10 @@ public class MediaSessionService extends SystemService implements Monitor {
try {
String packageName = getContext().getOpPackageName();
if (mUseMasterVolume) {
- boolean isMasterMute = mAudioService.isMasterMute();
- if (direction == MediaSessionManager.DIRECTION_MUTE) {
- mAudioService.setMasterMute(!isMasterMute, flags, packageName, mICallback);
- } else {
mAudioService.adjustMasterVolume(direction, flags, packageName);
- // Do not call setMasterMute when direction = 0 which is used just to
- // show the UI.
- if (isMasterMute && direction != 0) {
- mAudioService.setMasterMute(false, flags, packageName, mICallback);
- }
- }
} else {
- boolean isStreamMute = mAudioService.isStreamMute(suggestedStream);
- if (direction == MediaSessionManager.DIRECTION_MUTE) {
- mAudioManager.setStreamMute(suggestedStream, !isStreamMute);
- } else {
- mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
- flags, packageName);
- // Do not call setStreamMute when direction = 0 which is used just to
- // show the UI.
- if (isStreamMute && direction != 0) {
- mAudioManager.setStreamMute(suggestedStream, false);
- }
- }
+ mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
+ flags, packageName);
}
} catch (RemoteException e) {
Log.e(TAG, "Error adjusting default volume.", e);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 5d73149..6660843 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -26,6 +26,7 @@ import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Slog;
import android.util.TypedValue;
+
import com.android.server.EventLogTags;
import java.io.PrintWriter;
@@ -126,6 +127,7 @@ public class TaskStack {
boolean oldFullscreen = mFullscreen;
if (mDisplayContent != null) {
mDisplayContent.getLogicalDisplayRect(mTmpRect);
+ bounds.intersect(mTmpRect); // ensure bounds are entirely within the display rect
mFullscreen = mTmpRect.equals(bounds);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e4d0b77..bec0f66 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1914,12 +1914,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- public void setPasswordQuality(ComponentName who, int quality, int userHandle) {
+ public void setPasswordQuality(ComponentName who, int quality) {
if (!mHasFeature) {
return;
}
+ final int userHandle = UserHandle.getCallingUserId();
validateQualityConstant(quality);
- enforceCrossUserPermission(userHandle);
synchronized (this) {
if (who == null) {
@@ -1963,11 +1963,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- public void setPasswordMinimumLength(ComponentName who, int length, int userHandle) {
+ public void setPasswordMinimumLength(ComponentName who, int length) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2010,11 +2010,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- public void setPasswordHistoryLength(ComponentName who, int length, int userHandle) {
+ public void setPasswordHistoryLength(ComponentName who, int length) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2057,11 +2057,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- public void setPasswordExpirationTimeout(ComponentName who, long timeout, int userHandle) {
+ public void setPasswordExpirationTimeout(ComponentName who, long timeout) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2226,11 +2226,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- public void setPasswordMinimumUpperCase(ComponentName who, int length, int userHandle) {
+ public void setPasswordMinimumUpperCase(ComponentName who, int length) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2273,8 +2273,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- public void setPasswordMinimumLowerCase(ComponentName who, int length, int userHandle) {
- enforceCrossUserPermission(userHandle);
+ public void setPasswordMinimumLowerCase(ComponentName who, int length) {
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2317,11 +2317,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- public void setPasswordMinimumLetters(ComponentName who, int length, int userHandle) {
+ public void setPasswordMinimumLetters(ComponentName who, int length) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2364,11 +2364,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- public void setPasswordMinimumNumeric(ComponentName who, int length, int userHandle) {
+ public void setPasswordMinimumNumeric(ComponentName who, int length) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2411,11 +2411,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- public void setPasswordMinimumSymbols(ComponentName who, int length, int userHandle) {
+ public void setPasswordMinimumSymbols(ComponentName who, int length) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2458,11 +2458,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- public void setPasswordMinimumNonLetter(ComponentName who, int length, int userHandle) {
+ public void setPasswordMinimumNonLetter(ComponentName who, int length) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2522,8 +2522,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// This API can only be called by an active device admin,
// so try to retrieve it to check that the caller is one.
- getActiveAdminForCallerLocked(null,
- DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
if (policy.mActivePasswordQuality < getPasswordQuality(null, userHandle)
|| policy.mActivePasswordLength < getPasswordMinimumLength(null, userHandle)) {
return false;
@@ -2556,11 +2555,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- public void setMaximumFailedPasswordsForWipe(ComponentName who, int num, int userHandle) {
+ public void setMaximumFailedPasswordsForWipe(ComponentName who, int num) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2632,11 +2631,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return strictestAdmin;
}
- public boolean resetPassword(String passwordOrNull, int flags, int userHandle) {
+ public boolean resetPassword(String passwordOrNull, int flags) {
if (!mHasFeature) {
return false;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
enforceNotManagedProfile(userHandle, "reset the password");
String password = passwordOrNull != null ? passwordOrNull : "";
@@ -2767,11 +2766,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return true;
}
- public void setMaximumTimeToLock(ComponentName who, long timeMs, int userHandle) {
+ public void setMaximumTimeToLock(ComponentName who, long timeMs) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -3231,11 +3230,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
public ComponentName setGlobalProxy(ComponentName who, String proxySpec,
- String exclusionList, int userHandle) {
+ String exclusionList) {
if (!mHasFeature) {
return null;
}
- enforceCrossUserPermission(userHandle);
synchronized(this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -3261,7 +3259,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// If the user is not the owner, don't set the global proxy. Fail silently.
if (UserHandle.getCallingUserId() != UserHandle.USER_OWNER) {
Slog.w(LOG_TAG, "Only the owner is allowed to set the global proxy. User "
- + userHandle + " is not permitted.");
+ + UserHandle.getCallingUserId() + " is not permitted.");
return null;
}
if (proxySpec == null) {
@@ -3371,11 +3369,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
* Set the storage encryption request for a single admin. Returns the new total request
* status (for all admins).
*/
- public int setStorageEncryption(ComponentName who, boolean encrypt, int userHandle) {
+ public int setStorageEncryption(ComponentName who, boolean encrypt) {
if (!mHasFeature) {
return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
// Check for permissions
if (who == null) {
@@ -3507,11 +3505,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
/**
* Set whether the screen capture is disabled for the user managed by the specified admin.
*/
- public void setScreenCaptureDisabled(ComponentName who, int userHandle, boolean disabled) {
+ public void setScreenCaptureDisabled(ComponentName who, boolean disabled) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -3566,11 +3564,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
/**
* Set whether auto time is required by the specified admin (must be device owner).
*/
- public void setAutoTimeRequired(ComponentName who, int userHandle, boolean required) {
+ public void setAutoTimeRequired(ComponentName who, boolean required) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -3617,11 +3615,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
/**
* Disables all device cameras according to the specified admin.
*/
- public void setCameraDisabled(ComponentName who, boolean disabled, int userHandle) {
+ public void setCameraDisabled(ComponentName who, boolean disabled) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -3666,11 +3664,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
/**
* Selectively disable keyguard features.
*/
- public void setKeyguardDisabledFeatures(ComponentName who, int which, int userHandle) {
+ public void setKeyguardDisabledFeatures(ComponentName who, int which) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
enforceNotManagedProfile(userHandle, "disable keyguard features");
synchronized (this) {
if (who == null) {
@@ -3920,7 +3918,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Bundle userRestrictions = mUserManager.getUserRestrictions();
mUserManager.setUserRestrictions(new Bundle(), userHandle);
if (userRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME)) {
- audioManager.setMasterMute(false);
+ audioManager.adjustMasterVolume(AudioManager.ADJUST_UNMUTE, 0);
}
if (userRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE)) {
audioManager.setMicrophoneMute(false);
@@ -4216,11 +4214,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
public void setTrustAgentConfiguration(ComponentName admin, ComponentName agent,
- PersistableBundle args, int userHandle) {
+ PersistableBundle args) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
enforceNotManagedProfile(userHandle, "set trust agent configuration");
synchronized (this) {
if (admin == null) {
@@ -4841,7 +4839,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
iAudioService.setMicrophoneMute(true, who.getPackageName());
} else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- iAudioService.setMasterMute(true, 0, who.getPackageName(), null);
+ iAudioService.adjustMasterVolume(AudioManager.ADJUST_MUTE, 0,
+ who.getPackageName());
}
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
@@ -4906,7 +4905,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
iAudioService.setMicrophoneMute(false, who.getPackageName());
} else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- iAudioService.setMasterMute(false, 0, who.getPackageName(), null);
+ iAudioService.adjustMasterVolume(AudioManager.ADJUST_UNMUTE, 0,
+ who.getPackageName());
}
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
@@ -5361,8 +5361,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setMasterVolumeMuted(ComponentName who, boolean on) {
- final ContentResolver contentResolver = mContext.getContentResolver();
-
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -5372,7 +5370,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
IAudioService iAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
try{
- iAudioService.setMasterMute(on, 0, who.getPackageName(), null);
+ iAudioService.adjustMasterVolume(
+ on ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE, 0,
+ who.getPackageName());
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Failed to setMasterMute", re);
}
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index a59505c..07476e3 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -39,8 +39,6 @@ import android.text.style.TtsSpan;
import android.util.SparseIntArray;
import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY;
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING;
import java.util.Locale;
@@ -2200,8 +2198,8 @@ public class PhoneNumberUtils
if (!TextUtils.isEmpty(dialStr)) {
if (isReallyDialable(dialStr.charAt(0)) &&
isNonSeparator(dialStr)) {
- String currIso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY, "");
- String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
+ String currIso = TelephonyManager.getDefault().getNetworkCountryIso();
+ String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
getFormatTypeFromCountryCode(currIso),
@@ -2223,7 +2221,7 @@ public class PhoneNumberUtils
public static String cdmaCheckAndProcessPlusCodeForSms(String dialStr) {
if (!TextUtils.isEmpty(dialStr)) {
if (isReallyDialable(dialStr.charAt(0)) && isNonSeparator(dialStr)) {
- String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
+ String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
if (!TextUtils.isEmpty(defaultIso)) {
int format = getFormatTypeFromCountryCode(defaultIso);
return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr, format, format);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index c67629d..aca94e9 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1101,9 +1101,7 @@ public class SubscriptionManager {
// What else can we do?
return false;
}
- // FIXME: use better way to get roaming status instead of reading from system property
- return Boolean.parseBoolean(TelephonyManager.getTelephonyProperty(phoneId,
- TelephonyProperties.PROPERTY_OPERATOR_ISROAMING, null));
+ return TelephonyManager.getDefault().isNetworkRoaming(subId);
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 339fc6d..ba5a679 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1077,7 +1077,7 @@ public class TelephonyManager {
* on a CDMA network).
*/
public String getNetworkOperator() {
- return getNetworkOperator(getDefaultSubscription());
+ return getNetworkOperatorForPhone(getDefaultPhone());
}
/**
@@ -1091,8 +1091,23 @@ public class TelephonyManager {
* @param subId
*/
/** {@hide} */
- public String getNetworkOperator(int subId) {
+ public String getNetworkOperatorForSubscription(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
+ return getNetworkOperatorForPhone(phoneId);
+ }
+
+ /**
+ * Returns the numeric name (MCC+MNC) of current registered operator
+ * for a particular subscription.
+ * <p>
+ * Availability: Only when user is registered to a network. Result may be
+ * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+ * on a CDMA network).
+ *
+ * @param phoneId
+ * @hide
+ **/
+ public String getNetworkOperatorForPhone(int phoneId) {
return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, "");
}
@@ -1130,7 +1145,7 @@ public class TelephonyManager {
* on a CDMA network).
*/
public String getNetworkCountryIso() {
- return getNetworkCountryIso(getDefaultSubscription());
+ return getNetworkCountryIsoForPhone(getDefaultPhone());
}
/**
@@ -1144,8 +1159,23 @@ public class TelephonyManager {
* @param subId for which Network CountryIso is returned
*/
/** {@hide} */
- public String getNetworkCountryIso(int subId) {
+ public String getNetworkCountryIsoForSubscription(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
+ return getNetworkCountryIsoForPhone(phoneId);
+ }
+
+ /**
+ * Returns the ISO country code equivalent of the current registered
+ * operator's MCC (Mobile Country Code) of a subscription.
+ * <p>
+ * Availability: Only when user is registered to a network. Result may be
+ * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+ * on a CDMA network).
+ *
+ * @param phoneId for which Network CountryIso is returned
+ */
+ /** {@hide} */
+ public String getNetworkCountryIsoForPhone(int phoneId) {
return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
}
@@ -1537,6 +1567,34 @@ public class TelephonyManager {
* @see #getSimState
*/
public String getSimOperator() {
+ return getSimOperatorNumeric();
+ }
+
+ /**
+ * Returns the MCC+MNC (mobile country code + mobile network code) of the
+ * provider of the SIM. 5 or 6 decimal digits.
+ * <p>
+ * Availability: SIM state must be {@link #SIM_STATE_READY}
+ *
+ * @see #getSimState
+ *
+ * @param subId for which SimOperator is returned
+ * @hide
+ */
+ public String getSimOperator(int subId) {
+ return getSimOperatorNumericForSubscription(subId);
+ }
+
+ /**
+ * Returns the MCC+MNC (mobile country code + mobile network code) of the
+ * provider of the SIM. 5 or 6 decimal digits.
+ * <p>
+ * Availability: SIM state must be {@link #SIM_STATE_READY}
+ *
+ * @see #getSimState
+ * @hide
+ */
+ public String getSimOperatorNumeric() {
int subId = SubscriptionManager.getDefaultDataSubId();
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
subId = SubscriptionManager.getDefaultSmsSubId();
@@ -1547,8 +1605,8 @@ public class TelephonyManager {
}
}
}
- Rlog.d(TAG, "getSimOperator(): default subId=" + subId);
- return getSimOperator(subId);
+ Rlog.d(TAG, "getSimOperatorNumeric(): default subId=" + subId);
+ return getSimOperatorNumericForSubscription(subId);
}
/**
@@ -1560,14 +1618,24 @@ public class TelephonyManager {
* @see #getSimState
*
* @param subId for which SimOperator is returned
+ * @hide
*/
- /** {@hide} */
- public String getSimOperator(int subId) {
+ public String getSimOperatorNumericForSubscription(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
- String operator = getTelephonyProperty(phoneId,
+ return getSimOperatorNumericForPhone(phoneId);
+ }
+
+ /**
+ * Returns the MCC+MNC (mobile country code + mobile network code) of the
+ * provider of the SIM for a particular subscription. 5 or 6 decimal digits.
+ * <p>
+ *
+ * @param phoneId for which SimOperator is returned
+ * @hide
+ */
+ public String getSimOperatorNumericForPhone(int phoneId) {
+ return getTelephonyProperty(phoneId,
TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
- Rlog.d(TAG, "getSimOperator: subId=" + subId + " operator=" + operator);
- return operator;
}
/**
@@ -1578,7 +1646,7 @@ public class TelephonyManager {
* @see #getSimState
*/
public String getSimOperatorName() {
- return getSimOperatorName(getDefaultSubscription());
+ return getSimOperatorNameForPhone(getDefaultPhone());
}
/**
@@ -1589,30 +1657,61 @@ public class TelephonyManager {
* @see #getSimState
*
* @param subId for which SimOperatorName is returned
+ * @hide
*/
- /** {@hide} */
- public String getSimOperatorName(int subId) {
+ public String getSimOperatorNameForSubscription(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
- return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "");
+ return getSimOperatorNameForPhone(phoneId);
+ }
+
+ /**
+ * Returns the Service Provider Name (SPN).
+ *
+ * @hide
+ */
+ public String getSimOperatorNameForPhone(int phoneId) {
+ return getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "");
}
/**
* Returns the ISO country code equivalent for the SIM provider's country code.
*/
public String getSimCountryIso() {
- return getSimCountryIso(getDefaultSubscription());
+ return getSimCountryIsoForPhone(getDefaultPhone());
}
/**
* Returns the ISO country code equivalent for the SIM provider's country code.
*
* @param subId for which SimCountryIso is returned
+ *
+ * @hide
*/
- /** {@hide} */
public String getSimCountryIso(int subId) {
+ return getSimCountryIsoForSubscription(subId);
+ }
+
+ /**
+ * Returns the ISO country code equivalent for the SIM provider's country code.
+ *
+ * @param subId for which SimCountryIso is returned
+ *
+ * @hide
+ */
+ public String getSimCountryIsoForSubscription(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
- return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
- "");
+ return getSimCountryIsoForPhone(phoneId);
+ }
+
+ /**
+ * Returns the ISO country code equivalent for the SIM provider's country code.
+ *
+ * @hide
+ */
+ public String getSimCountryIsoForPhone(int phoneId) {
+ return getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
}
/**
@@ -3677,4 +3776,344 @@ public class TelephonyManager {
return false;
}
}
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone.
+ *
+ * @hide
+ */
+ public void setSimOperatorNumeric(String numeric) {
+ int phoneId = getDefaultPhone();
+ setSimOperatorNumericForPhone(phoneId, numeric);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the given phone.
+ *
+ * @hide
+ */
+ public void setSimOperatorNumericForPhone(int phoneId, String numeric) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, numeric);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone.
+ *
+ * @hide
+ */
+ public void setSimOperatorName(String name) {
+ int phoneId = getDefaultPhone();
+ setSimOperatorNameForPhone(phoneId, name);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the given phone.
+ *
+ * @hide
+ */
+ public void setSimOperatorNameForPhone(int phoneId, String name) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, name);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY for the default phone.
+ *
+ * @hide
+ */
+ public void setSimCountryIso(String iso) {
+ int phoneId = getDefaultPhone();
+ setSimCountryIsoForPhone(phoneId, iso);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY for the given phone.
+ *
+ * @hide
+ */
+ public void setSimCountryIsoForPhone(int phoneId, String iso) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, iso);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_SIM_STATE for the default phone.
+ *
+ * @hide
+ */
+ public void setSimState(String state) {
+ int phoneId = getDefaultPhone();
+ setSimStateForPhone(phoneId, state);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_SIM_STATE for the given phone.
+ *
+ * @hide
+ */
+ public void setSimStateForPhone(int phoneId, String state) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_SIM_STATE, state);
+ }
+
+ /**
+ * Set baseband version for the default phone.
+ *
+ * @param version baseband version
+ * @hide
+ */
+ public void setBasebandVersion(String version) {
+ int phoneId = getDefaultPhone();
+ setBasebandVersionForPhone(phoneId, version);
+ }
+
+ /**
+ * Set baseband version by phone id.
+ *
+ * @param phoneId for which baseband version is set
+ * @param version baseband version
+ * @hide
+ */
+ public void setBasebandVersionForPhone(int phoneId, String version) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ String prop = TelephonyProperties.PROPERTY_BASEBAND_VERSION +
+ ((phoneId == 0) ? "" : Integer.toString(phoneId));
+ SystemProperties.set(prop, version);
+ }
+ }
+
+ /**
+ * Set phone type for the default phone.
+ *
+ * @param type phone type
+ *
+ * @hide
+ */
+ public void setPhoneType(int type) {
+ int phoneId = getDefaultPhone();
+ setPhoneType(phoneId, type);
+ }
+
+ /**
+ * Set phone type by phone id.
+ *
+ * @param phoneId for which phone type is set
+ * @param type phone type
+ *
+ * @hide
+ */
+ public void setPhoneType(int phoneId, int type) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ TelephonyManager.setTelephonyProperty(phoneId,
+ TelephonyProperties.CURRENT_ACTIVE_PHONE, String.valueOf(type));
+ }
+ }
+
+ /**
+ * Get OTASP number schema for the default phone.
+ *
+ * @param defaultValue default value
+ * @return OTA SP number schema
+ *
+ * @hide
+ */
+ public String getOtaSpNumberSchema(String defaultValue) {
+ int phoneId = getDefaultPhone();
+ return getOtaSpNumberSchemaForPhone(phoneId, defaultValue);
+ }
+
+ /**
+ * Get OTASP number schema by phone id.
+ *
+ * @param phoneId for which OTA SP number schema is get
+ * @param defaultValue default value
+ * @return OTA SP number schema
+ *
+ * @hide
+ */
+ public String getOtaSpNumberSchemaForPhone(int phoneId, String defaultValue) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ return TelephonyManager.getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_OTASP_NUM_SCHEMA, defaultValue);
+ }
+
+ return defaultValue;
+ }
+
+ /**
+ * Get SMS receive capable from system property for the default phone.
+ *
+ * @param defaultValue default value
+ * @return SMS receive capable
+ *
+ * @hide
+ */
+ public boolean getSmsReceiveCapable(boolean defaultValue) {
+ int phoneId = getDefaultPhone();
+ return getSmsReceiveCapableForPhone(phoneId, defaultValue);
+ }
+
+ /**
+ * Get SMS receive capable from system property by phone id.
+ *
+ * @param phoneId for which SMS receive capable is get
+ * @param defaultValue default value
+ * @return SMS receive capable
+ *
+ * @hide
+ */
+ public boolean getSmsReceiveCapableForPhone(int phoneId, boolean defaultValue) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ return Boolean.valueOf(TelephonyManager.getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_SMS_RECEIVE, String.valueOf(defaultValue)));
+ }
+
+ return defaultValue;
+ }
+
+ /**
+ * Get SMS send capable from system property for the default phone.
+ *
+ * @param defaultValue default value
+ * @return SMS send capable
+ *
+ * @hide
+ */
+ public boolean getSmsSendCapable(boolean defaultValue) {
+ int phoneId = getDefaultPhone();
+ return getSmsSendCapableForPhone(phoneId, defaultValue);
+ }
+
+ /**
+ * Get SMS send capable from system property by phone id.
+ *
+ * @param phoneId for which SMS send capable is get
+ * @param defaultValue default value
+ * @return SMS send capable
+ *
+ * @hide
+ */
+ public boolean getSmsSendCapableForPhone(int phoneId, boolean defaultValue) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ return Boolean.valueOf(TelephonyManager.getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_SMS_SEND, String.valueOf(defaultValue)));
+ }
+
+ return defaultValue;
+ }
+
+ /**
+ * Set the alphabetic name of current registered operator.
+ * @param name the alphabetic name of current registered operator.
+ * @hide
+ */
+ public void setNetworkOperatorName(String name) {
+ int phoneId = getDefaultPhone();
+ setNetworkOperatorNameForPhone(phoneId, name);
+ }
+
+ /**
+ * Set the alphabetic name of current registered operator.
+ * @param phoneId which phone you want to set
+ * @param name the alphabetic name of current registered operator.
+ * @hide
+ */
+ public void setNetworkOperatorNameForPhone(int phoneId, String name) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ALPHA, name);
+ }
+ }
+
+ /**
+ * Set the numeric name (MCC+MNC) of current registered operator.
+ * @param operator the numeric name (MCC+MNC) of current registered operator
+ * @hide
+ */
+ public void setNetworkOperatorNumeric(String numeric) {
+ int phoneId = getDefaultPhone();
+ setNetworkOperatorNumericForPhone(phoneId, numeric);
+ }
+
+ /**
+ * Set the numeric name (MCC+MNC) of current registered operator.
+ * @param phoneId for which phone type is set
+ * @param operator the numeric name (MCC+MNC) of current registered operator
+ * @hide
+ */
+ public void setNetworkOperatorNumericForPhone(int phoneId, String numeric) {
+ setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, numeric);
+ }
+
+ /**
+ * Set roaming state of the current network, for GSM purposes.
+ * @param isRoaming is network in romaing state or not
+ * @hide
+ */
+ public void setNetworkRoaming(boolean isRoaming) {
+ int phoneId = getDefaultPhone();
+ setNetworkRoamingForPhone(phoneId, isRoaming);
+ }
+
+ /**
+ * Set roaming state of the current network, for GSM purposes.
+ * @param phoneId which phone you want to set
+ * @param isRoaming is network in romaing state or not
+ * @hide
+ */
+ public void setNetworkRoamingForPhone(int phoneId, boolean isRoaming) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ISROAMING,
+ isRoaming ? "true" : "false");
+ }
+ }
+
+ /**
+ * Set the ISO country code equivalent of the current registered
+ * operator's MCC (Mobile Country Code).
+ * @param iso the ISO country code equivalent of the current registered
+ * @hide
+ */
+ public void setNetworkCountryIso(String iso) {
+ int phoneId = getDefaultPhone();
+ setNetworkCountryIsoForPhone(phoneId, iso);
+ }
+
+ /**
+ * Set the ISO country code equivalent of the current registered
+ * operator's MCC (Mobile Country Code).
+ * @param phoneId which phone you want to set
+ * @param iso the ISO country code equivalent of the current registered
+ * @hide
+ */
+ public void setNetworkCountryIsoForPhone(int phoneId, String iso) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, iso);
+ }
+ }
+
+ /**
+ * Set the network type currently in use on the device for data transmission.
+ * @param type the network type currently in use on the device for data transmission
+ * @hide
+ */
+ public void setDataNetworkType(int type) {
+ int phoneId = getDefaultPhone();
+ setDataNetworkTypeForPhone(phoneId, type);
+ }
+
+ /**
+ * Set the network type currently in use on the device for data transmission.
+ * @param phoneId which phone you want to set
+ * @param type the network type currently in use on the device for data transmission
+ * @hide
+ */
+ public void setDataNetworkTypeForPhone(int phoneId, int type) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
+ ServiceState.rilRadioTechnologyToString(type));
+ }
+ }
}