Merge "Add TtsSpan class."
diff --git a/Android.mk b/Android.mk
index 0c5051c..6859678 100644
--- a/Android.mk
+++ b/Android.mk
@@ -153,6 +153,7 @@
core/java/android/hardware/hdmi/IHdmiHotplugEventListener.aidl \
core/java/android/hardware/hdmi/IHdmiInputChangeListener.aidl \
core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl \
+ core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl \
core/java/android/hardware/input/IInputManager.aidl \
core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
core/java/android/hardware/location/IFusedLocationHardware.aidl \
@@ -328,12 +329,12 @@
media/java/android/media/tv/ITvInputServiceCallback.aidl \
media/java/android/media/tv/ITvInputSession.aidl \
media/java/android/media/tv/ITvInputSessionCallback.aidl \
- telecomm/java/com/android/internal/telecomm/ICallService.aidl \
- telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl \
telecomm/java/com/android/internal/telecomm/ICallServiceLookupResponse.aidl \
telecomm/java/com/android/internal/telecomm/ICallServiceProvider.aidl \
telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl \
telecomm/java/com/android/internal/telecomm/ICallVideoClient.aidl \
+ telecomm/java/com/android/internal/telecomm/IConnectionService.aidl \
+ telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl \
telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl \
telecomm/java/com/android/internal/telecomm/IInCallService.aidl \
telecomm/java/com/android/internal/telecomm/ITelecommService.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 20e48f9..2c9ea54 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -209,6 +209,7 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/ims-common_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework2_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/telecomm/java/com/android/internal/telecomm)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
diff --git a/api/current.txt b/api/current.txt
index 2915e36..8890d6d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -347,6 +347,9 @@
field public static final int bufferType = 16843086; // 0x101014e
field public static final int button = 16843015; // 0x1010107
field public static final int buttonBarButtonStyle = 16843567; // 0x101032f
+ field public static final int buttonBarNegativeButtonStyle = 16843917; // 0x101048d
+ field public static final int buttonBarNeutralButtonStyle = 16843916; // 0x101048c
+ field public static final int buttonBarPositiveButtonStyle = 16843915; // 0x101048b
field public static final int buttonBarStyle = 16843566; // 0x101032e
field public static final int buttonStyle = 16842824; // 0x1010048
field public static final int buttonStyleInset = 16842826; // 0x101004a
@@ -2384,6 +2387,7 @@
field public static final int Widget_Material_ButtonBar = 16974439; // 0x1030267
field public static final int Widget_Material_ButtonBar_AlertDialog = 16974440; // 0x1030268
field public static final int Widget_Material_Button_Borderless = 16974434; // 0x1030262
+ field public static final int Widget_Material_Button_Borderless_Colored = 16974556; // 0x10302dc
field public static final int Widget_Material_Button_Borderless_Small = 16974435; // 0x1030263
field public static final int Widget_Material_Button_Inset = 16974436; // 0x1030264
field public static final int Widget_Material_Button_Small = 16974437; // 0x1030265
@@ -2417,6 +2421,7 @@
field public static final int Widget_Material_Light_ButtonBar = 16974500; // 0x10302a4
field public static final int Widget_Material_Light_ButtonBar_AlertDialog = 16974501; // 0x10302a5
field public static final int Widget_Material_Light_Button_Borderless = 16974495; // 0x103029f
+ field public static final int Widget_Material_Light_Button_Borderless_Colored = 16974557; // 0x10302dd
field public static final int Widget_Material_Light_Button_Borderless_Small = 16974496; // 0x10302a0
field public static final int Widget_Material_Light_Button_Inset = 16974497; // 0x10302a1
field public static final int Widget_Material_Light_Button_Small = 16974498; // 0x10302a2
@@ -8972,6 +8977,7 @@
public class TypedArray {
method public boolean getBoolean(int, boolean);
+ method public int getChangingConfigurations();
method public int getColor(int, int);
method public android.content.res.ColorStateList getColorStateList(int);
method public float getDimension(int, float);
@@ -16092,6 +16098,7 @@
public abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
ctor public TvInputService.Session();
+ method public void dispatchChannelRetuned(android.net.Uri);
method public android.view.View onCreateOverlayView();
method public boolean onGenericMotionEvent(android.view.MotionEvent);
method public boolean onKeyDown(int, android.view.KeyEvent);
@@ -16129,6 +16136,7 @@
public static abstract class TvView.TvInputListener {
ctor public TvView.TvInputListener();
+ method public void onChannelRetuned(java.lang.String, android.net.Uri);
method public void onError(java.lang.String, int);
}
@@ -27530,21 +27538,6 @@
field public static final int VoWIFI = 2; // 0x2
}
- public final class CallInfo implements android.os.Parcelable {
- ctor public CallInfo(java.lang.String, android.telecomm.CallState, android.net.Uri);
- method public int describeContents();
- method public android.telecomm.PhoneAccount getAccount();
- method public android.telecomm.CallServiceDescriptor getCurrentCallServiceDescriptor();
- method public android.os.Bundle getExtras();
- method public android.telecomm.GatewayInfo getGatewayInfo();
- method public android.net.Uri getHandle();
- method public java.lang.String getId();
- method public android.net.Uri getOriginalHandle();
- method public android.telecomm.CallState getState();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- }
-
public final class CallNumberPresentation extends java.lang.Enum {
method public static android.telecomm.CallNumberPresentation valueOf(java.lang.String);
method public static final android.telecomm.CallNumberPresentation[] values();
@@ -27554,15 +27547,9 @@
enum_constant public static final android.telecomm.CallNumberPresentation UNKNOWN;
}
- public abstract class CallService extends android.app.Service {
- ctor public CallService();
- method public final android.os.IBinder getBinder();
- method public final android.os.IBinder onBind(android.content.Intent);
- }
-
public final class CallServiceDescriptor implements android.os.Parcelable {
method public int describeContents();
- method public java.lang.String getCallServiceId();
+ method public java.lang.String getConnectionServiceId();
method public int getNetworkType();
method public android.content.ComponentName getServiceComponent();
method public static android.telecomm.CallServiceDescriptor.Builder newBuilder(android.content.Context);
@@ -27575,7 +27562,7 @@
public static class CallServiceDescriptor.Builder {
method public android.telecomm.CallServiceDescriptor build();
- method public android.telecomm.CallServiceDescriptor.Builder setCallService(java.lang.Class<? extends android.telecomm.CallService>);
+ method public android.telecomm.CallServiceDescriptor.Builder setConnectionService(java.lang.Class<? extends android.telecomm.ConnectionService>);
method public android.telecomm.CallServiceDescriptor.Builder setNetworkType(int);
}
@@ -27648,7 +27635,6 @@
method protected void onAbort();
method protected void onAnswer();
method protected void onChildrenChanged(java.util.List<android.telecomm.Connection>);
- method protected void onConference();
method protected void onDisconnect();
method protected void onHold();
method protected void onPhoneAccountClicked();
@@ -27699,12 +27685,13 @@
field public static final android.os.Parcelable.Creator CREATOR;
}
- public abstract class ConnectionService extends android.telecomm.CallService {
+ public abstract class ConnectionService extends android.app.Service {
ctor public ConnectionService();
method public final void createRemoteOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.OutgoingCallResponse<android.telecomm.RemoteConnection>);
method public final java.util.Collection<android.telecomm.Connection> getAllConnections();
method public final void lookupRemoteAccounts(android.net.Uri, android.telecomm.SimpleResponse<android.net.Uri, java.util.List<android.telecomm.PhoneAccount>>);
method public final void maybeRespondToAccountLookup();
+ method public final android.os.IBinder onBind(android.content.Intent);
method protected void onConnectionAdded(android.telecomm.Connection);
method protected void onConnectionRemoved(android.telecomm.Connection);
method protected void onCreateConferenceConnection(java.lang.String, android.telecomm.Connection, android.telecomm.Response<java.lang.String, android.telecomm.Connection>);
@@ -27822,6 +27809,7 @@
method public void disconnect();
method public int getDisconnectCause();
method public java.lang.String getDisconnectMessage();
+ method public int getFeatures();
method public int getState();
method public void hold();
method public void playDtmf(char);
@@ -27836,6 +27824,7 @@
public static abstract interface RemoteConnection.Listener {
method public abstract void onDestroyed(android.telecomm.RemoteConnection);
method public abstract void onDisconnected(android.telecomm.RemoteConnection, int, java.lang.String);
+ method public abstract void onFeaturesChanged(android.telecomm.RemoteConnection, int);
method public abstract void onPostDialWait(android.telecomm.RemoteConnection, java.lang.String);
method public abstract void onRequestingRingback(android.telecomm.RemoteConnection, boolean);
method public abstract void onStateChanged(android.telecomm.RemoteConnection, int);
@@ -27853,14 +27842,15 @@
public final class TelecommConstants {
ctor public TelecommConstants();
- field public static final java.lang.String ACTION_CALL_SERVICE;
field public static final java.lang.String ACTION_CALL_SERVICE_PROVIDER;
+ field public static final java.lang.String ACTION_CONNECTION_SERVICE;
field public static final java.lang.String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL";
field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecomm.extra.CALL_DISCONNECT_CAUSE";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecomm.extra.CALL_DISCONNECT_MESSAGE";
field public static final java.lang.String EXTRA_CALL_SERVICE_DESCRIPTOR = "android.intent.extra.CALL_SERVICE_DESCRIPTOR";
+ field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecomm.extra.CONNECTION_SERVICE";
field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS";
field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO = "android.intent.extra.START_CALL_WITH_VIDEO";
@@ -28326,6 +28316,7 @@
method public java.lang.String getSubscriberId();
method public java.lang.String getVoiceMailAlphaTag();
method public java.lang.String getVoiceMailNumber();
+ method public int hasCarrierPrivileges();
method public boolean hasIccCard();
method public boolean isNetworkRoaming();
method public void listen(android.telephony.PhoneStateListener, int);
@@ -28334,6 +28325,10 @@
field public static final int CALL_STATE_IDLE = 0; // 0x0
field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
field public static final int CALL_STATE_RINGING = 1; // 0x1
+ field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
+ field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
+ field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
+ field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
field public static final int DATA_ACTIVITY_DORMANT = 4; // 0x4
field public static final int DATA_ACTIVITY_IN = 1; // 0x1
field public static final int DATA_ACTIVITY_INOUT = 3; // 0x3
@@ -34275,6 +34270,7 @@
method public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo getCollectionInfo();
method public android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo getCollectionItemInfo();
method public java.lang.CharSequence getContentDescription();
+ method public java.lang.CharSequence getError();
method public android.os.Bundle getExtras();
method public int getInputType();
method public android.view.accessibility.AccessibilityNodeInfo getLabelFor();
@@ -34333,6 +34329,7 @@
method public void setDismissable(boolean);
method public void setEditable(boolean);
method public void setEnabled(boolean);
+ method public void setError(java.lang.CharSequence);
method public void setFocusable(boolean);
method public void setFocused(boolean);
method public void setInputType(int);
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 3dc024e..e659b2b 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -108,7 +108,7 @@
return NO_INIT;
SkBitmap bitmap;
SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(),
- &bitmap, SkBitmap::kNo_Config, SkImageDecoder::kDecodePixels_Mode);
+ &bitmap, kUnknown_SkColorType, SkImageDecoder::kDecodePixels_Mode);
asset->close();
delete asset;
@@ -127,20 +127,20 @@
glGenTextures(1, &texture->name);
glBindTexture(GL_TEXTURE_2D, texture->name);
- switch (bitmap.config()) {
- case SkBitmap::kA8_Config:
+ switch (bitmap.colorType()) {
+ case kAlpha_8_SkColorType:
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA,
GL_UNSIGNED_BYTE, p);
break;
- case SkBitmap::kARGB_4444_Config:
+ case kARGB_4444_SkColorType:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
GL_UNSIGNED_SHORT_4_4_4_4, p);
break;
- case SkBitmap::kARGB_8888_Config:
+ case kN32_SkColorType:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
GL_UNSIGNED_BYTE, p);
break;
- case SkBitmap::kRGB_565_Config:
+ case kRGB_565_SkColorType:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
GL_UNSIGNED_SHORT_5_6_5, p);
break;
@@ -166,7 +166,7 @@
if (codec) {
codec->setDitherImage(false);
codec->decode(&stream, &bitmap,
- SkBitmap::kARGB_8888_Config,
+ kN32_SkColorType,
SkImageDecoder::kDecodePixels_Mode);
delete codec;
}
@@ -190,8 +190,8 @@
if (tw < w) tw <<= 1;
if (th < h) th <<= 1;
- switch (bitmap.config()) {
- case SkBitmap::kARGB_8888_Config:
+ switch (bitmap.colorType()) {
+ case kN32_SkColorType:
if (tw != w || th != h) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA,
GL_UNSIGNED_BYTE, 0);
@@ -203,7 +203,7 @@
}
break;
- case SkBitmap::kRGB_565_Config:
+ case kRGB_565_SkColorType:
if (tw != w || th != h) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB,
GL_UNSIGNED_SHORT_5_6_5, 0);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 228d82e..2914a61 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5202,6 +5202,22 @@
}
/**
+ * Indication of whether this is the highest level activity in this task. Can be used to
+ * determine whether an activity launched by this activity was placed in the same task or
+ * another task.
+ *
+ * @return true if this is the topmost, non-finishing activity in its task.
+ * @hide
+ */
+ public boolean isTopOfTask() {
+ try {
+ return ActivityManagerNative.getDefault().isTopOfTask(mToken);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Convert a translucent themed Activity {@link android.R.attr#windowIsTranslucent} to a
* fullscreen opaque Activity.
* <p>
@@ -5284,6 +5300,14 @@
}
}
+ /** @hide */
+ public void onNewActivityOptions(ActivityOptions options) {
+ mActivityTransitionState.setEnterActivityOptions(this, options);
+ if (!mStopped) {
+ mActivityTransitionState.enterReady(this);
+ }
+ }
+
/**
* Retrieve the ActivityOptions passed in from the launching activity or passed back
* from an activity launched by this activity in its call to {@link
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 6e45868..3cf8f9a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -677,6 +677,12 @@
public int userId;
/**
+ * The first time this task was active.
+ * @hide
+ */
+ public long firstActiveTime;
+
+ /**
* The last time this task was active.
* @hide
*/
@@ -719,6 +725,7 @@
}
dest.writeInt(stackId);
dest.writeInt(userId);
+ dest.writeLong(firstActiveTime);
dest.writeLong(lastActiveTime);
}
@@ -732,6 +739,7 @@
TaskDescription.CREATOR.createFromParcel(source) : null;
stackId = source.readInt();
userId = source.readInt();
+ firstActiveTime = source.readLong();
lastActiveTime = source.readLong();
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1b2b08f..ce36fd8 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1546,6 +1546,15 @@
return true;
}
+ case IS_TOP_OF_TASK_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ final boolean isTopOfTask = isTopOfTask(token);
+ reply.writeNoException();
+ reply.writeInt(isTopOfTask ? 1 : 0);
+ return true;
+ }
+
case CONVERT_FROM_TRANSLUCENT_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
@@ -2129,7 +2138,7 @@
return true;
}
- case START_LOCK_TASK_BY_CURRENT: {
+ case START_LOCK_TASK_BY_CURRENT_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
startLockTaskModeOnCurrent();
reply.writeNoException();
@@ -2143,7 +2152,7 @@
return true;
}
- case STOP_LOCK_TASK_BY_CURRENT: {
+ case STOP_LOCK_TASK_BY_CURRENT_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
stopLockTaskModeOnCurrent();
reply.writeNoException();
@@ -4182,6 +4191,19 @@
return res;
}
+ public boolean isTopOfTask(IBinder token) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(IS_TOP_OF_TASK_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean res = reply.readInt() == 1;
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+
public boolean isTopActivityImmersive()
throws RemoteException {
Parcel data = Parcel.obtain();
@@ -4930,7 +4952,7 @@
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
- mRemote.transact(START_LOCK_TASK_BY_CURRENT, data, reply, 0);
+ mRemote.transact(START_LOCK_TASK_BY_CURRENT_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
@@ -4952,7 +4974,7 @@
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
- mRemote.transact(STOP_LOCK_TASK_BY_CURRENT, data, reply, 0);
+ mRemote.transact(STOP_LOCK_TASK_BY_CURRENT_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 422d88c..7b48e1d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -77,6 +77,7 @@
import android.util.EventLog;
import android.util.Log;
import android.util.LogPrinter;
+import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SuperNotCalledException;
@@ -1113,6 +1114,11 @@
sendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0);
}
+ public void scheduleOnNewActivityOptions(IBinder token, ActivityOptions options) {
+ sendMessage(H.ON_NEW_ACTIVITY_OPTIONS,
+ new Pair<IBinder, ActivityOptions>(token, options));
+ }
+
public void setProcessState(int state) {
updateProcessState(state, true);
}
@@ -1196,6 +1202,7 @@
public static final int REQUEST_ASSIST_CONTEXT_EXTRAS = 143;
public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
public static final int INSTALL_PROVIDER = 145;
+ public static final int ON_NEW_ACTIVITY_OPTIONS = 146;
String codeToString(int code) {
if (DEBUG_MESSAGES) {
@@ -1245,6 +1252,7 @@
case REQUEST_ASSIST_CONTEXT_EXTRAS: return "REQUEST_ASSIST_CONTEXT_EXTRAS";
case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE";
case INSTALL_PROVIDER: return "INSTALL_PROVIDER";
+ case ON_NEW_ACTIVITY_OPTIONS: return "ON_NEW_ACTIVITY_OPTIONS";
}
}
return Integer.toString(code);
@@ -1459,6 +1467,10 @@
case INSTALL_PROVIDER:
handleInstallProvider((ProviderInfo) msg.obj);
break;
+ case ON_NEW_ACTIVITY_OPTIONS:
+ Pair<IBinder, ActivityOptions> pair = (Pair<IBinder, ActivityOptions>) msg.obj;
+ onNewActivityOptions(pair.first, pair.second);
+ break;
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
@@ -2435,6 +2447,13 @@
}
}
+ public void onNewActivityOptions(IBinder token, ActivityOptions options) {
+ ActivityClientRecord r = mActivities.get(token);
+ if (r != null) {
+ r.activity.onNewActivityOptions(options);
+ }
+ }
+
public void handleInstallProvider(ProviderInfo info) {
installContentProviders(mInitialApplication, Lists.newArrayList(info));
}
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 5998d7a..6dead08 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -611,6 +611,16 @@
return true;
}
+ case SCHEDULE_ON_NEW_ACTIVITY_OPTIONS_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder token = data.readStrongBinder();
+ ActivityOptions options = new ActivityOptions(data.readBundle());
+ scheduleOnNewActivityOptions(token, options);
+ reply.writeNoException();
+ return true;
+ }
+
case SET_PROCESS_STATE_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -1251,7 +1261,20 @@
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
data.writeInt(timeout ? 1 : 0);
- mRemote.transact(SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+ mRemote.transact(SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ @Override
+ public void scheduleOnNewActivityOptions(IBinder token, ActivityOptions options)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ data.writeBundle(options == null ? null : options.toBundle());
+ mRemote.transact(SCHEDULE_ON_NEW_ACTIVITY_OPTIONS_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
data.recycle();
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 931903b..6e4192b 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -302,10 +302,10 @@
public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)
throws RemoteException;
-
+
public void overridePendingTransition(IBinder token, String packageName,
int enterAnim, int exitAnim) throws RemoteException;
-
+
public boolean isUserAMonkey() throws RemoteException;
public void setUserIsMonkey(boolean monkey) throws RemoteException;
@@ -320,12 +320,13 @@
public void setImmersive(IBinder token, boolean immersive) throws RemoteException;
public boolean isImmersive(IBinder token) throws RemoteException;
public boolean isTopActivityImmersive() throws RemoteException;
-
+ public boolean isTopOfTask(IBinder token) throws RemoteException;
+
public void crashApplication(int uid, int initialPid, String packageName,
String message) throws RemoteException;
public String getProviderMimeType(Uri uri, int userId) throws RemoteException;
-
+
public IBinder newUriPermissionOwner(String name) throws RemoteException;
public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
Uri uri, int mode, int userId) throws RemoteException;
@@ -748,7 +749,8 @@
int START_VOICE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218;
int GET_ACTIVITY_OPTIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+219;
int GET_APP_TASKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+220;
- int START_LOCK_TASK_BY_CURRENT = IBinder.FIRST_CALL_TRANSACTION+221;
- int STOP_LOCK_TASK_BY_CURRENT = IBinder.FIRST_CALL_TRANSACTION+222;
+ int START_LOCK_TASK_BY_CURRENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+221;
+ int STOP_LOCK_TASK_BY_CURRENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+222;
int FINISH_VOICE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+223;
+ int IS_TOP_OF_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+224;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index d0df7c3..d3c4854 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -140,6 +140,8 @@
throws RemoteException;
void scheduleTranslucentConversionComplete(IBinder token, boolean timeout)
throws RemoteException;
+ void scheduleOnNewActivityOptions(IBinder token, ActivityOptions options)
+ throws RemoteException;
void setProcessState(int state) throws RemoteException;
void scheduleInstallProvider(ProviderInfo provider) throws RemoteException;
void updateTimePrefs(boolean is24Hour) throws RemoteException;
@@ -176,7 +178,7 @@
int SET_SCHEDULING_GROUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
int SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
int SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
-
+ int SCHEDULE_ON_NEW_ACTIVITY_OPTIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31;
int SCHEDULE_SUICIDE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
int DISPATCH_PACKAGE_BROADCAST_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33;
int SCHEDULE_CRASH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+34;
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index 8cf8c25..abd042b 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -16,9 +16,11 @@
package android.app;
+import android.app.UiModeManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.LayoutInflater;
@@ -110,11 +112,14 @@
(LayoutInflater) themeContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.time_picker_dialog, null);
setView(view);
- setButtonPanelLayoutHint(LAYOUT_HINT_SIDE);
mTimePicker = (TimePicker) view.findViewById(R.id.timePicker);
// Initialize state
- mTimePicker.setLegacyMode(false /* will show new UI */);
+ UiModeManager uiModeManager =
+ (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
+ if (uiModeManager.getCurrentModeType() != Configuration.UI_MODE_TYPE_TELEVISION) {
+ mTimePicker.setLegacyMode(false /* will show new UI */);
+ }
mTimePicker.setShowDoneButton(true);
mTimePicker.setDismissCallback(new TimePicker.TimePickerDismissCallback() {
@Override
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index bb90fd7..514b26e 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -410,10 +410,14 @@
*/
public int largestWidthLimitDp = 0;
+ /** {@hide} */
+ public String scanSourceDir;
+ /** {@hide} */
+ public String scanPublicSourceDir;
+
/**
* Full path to the base APK for this application.
*/
- // TODO: verify that nobody is doing codePath comparisons against this
public String sourceDir;
/**
@@ -779,11 +783,25 @@
return true;
}
}
-
+
/**
* @hide
*/
@Override protected ApplicationInfo getApplicationInfo() {
return this;
}
+
+ /** {@hide} */ public void setCodePath(String codePath) { scanSourceDir = codePath; }
+ /** {@hide} */ public void setBaseCodePath(String baseCodePath) { sourceDir = baseCodePath; }
+ /** {@hide} */ public void setSplitCodePaths(String[] splitCodePaths) { splitSourceDirs = splitCodePaths; }
+ /** {@hide} */ public void setResourcePath(String resourcePath) { scanPublicSourceDir = resourcePath; }
+ /** {@hide} */ public void setBaseResourcePath(String baseResourcePath) { publicSourceDir = baseResourcePath; }
+ /** {@hide} */ public void setSplitResourcePaths(String[] splitResourcePaths) { splitPublicSourceDirs = splitResourcePaths; }
+
+ /** {@hide} */ public String getCodePath() { return scanSourceDir; }
+ /** {@hide} */ public String getBaseCodePath() { return sourceDir; }
+ /** {@hide} */ public String[] getSplitCodePaths() { return splitSourceDirs; }
+ /** {@hide} */ public String getResourcePath() { return scanPublicSourceDir; }
+ /** {@hide} */ public String getBaseResourcePath() { return publicSourceDir; }
+ /** {@hide} */ public String[] getSplitResourcePaths() { return splitSourceDirs; }
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index bb47124..7651aef 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -719,7 +719,12 @@
* <p>
* Note that this <em>does not</em> perform signature verification; that
* must be done separately in {@link #collectCertificates(Package, int)}.
+ *
+ * @deprecated external callers should move to
+ * {@link #parsePackage(File, int)}. Eventually this method will
+ * be marked private.
*/
+ @Deprecated
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
final Package pkg = parseBaseApk(apkFile, flags);
if (pkg == null) {
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index d2146ac..645f7df 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -927,6 +927,30 @@
return attrs;
}
+ /**
+ * Return a mask of the configuration parameters for which the values in
+ * this typed array may change.
+ *
+ * @return Returns a mask of the changing configuration parameters, as
+ * defined by {@link android.content.pm.ActivityInfo}.
+ * @see android.content.pm.ActivityInfo
+ */
+ public int getChangingConfigurations() {
+ int changingConfig = 0;
+
+ final int[] data = mData;
+ final int N = length();
+ for (int i = 0; i < N; i++) {
+ final int index = i * AssetManager.STYLE_NUM_ENTRIES;
+ final int type = data[index + AssetManager.STYLE_TYPE];
+ if (type == TypedValue.TYPE_NULL) {
+ continue;
+ }
+ changingConfig |= data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS];
+ }
+ return changingConfig;
+ }
+
private boolean getValueAt(int index, TypedValue outValue) {
final int[] data = mData;
final int type = data[index+AssetManager.STYLE_TYPE];
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 2e59eee..e55ee67 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -533,6 +533,20 @@
handler = checkHandler(handler);
}
+ // Make sure that there all requests have at least 1 surface; all surfaces are non-null
+ for (CaptureRequest request : requestList) {
+ if (request.getTargets().isEmpty()) {
+ throw new IllegalArgumentException(
+ "Each request must have at least one Surface target");
+ }
+
+ for (Surface surface : request.getTargets()) {
+ if (surface == null) {
+ throw new IllegalArgumentException("Null Surface targets are not allowed");
+ }
+ }
+ }
+
try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
checkIfCameraClosedOrInError();
int requestId;
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index d7b1a36..3ef538c 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -154,16 +154,6 @@
private static void mapCharacteristicsFromParameters(CameraMetadataNative m,
Camera.Parameters p) {
/*
- * info.supportedHardwareLevel
- */
- m.set(INFO_SUPPORTED_HARDWARE_LEVEL, INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
-
- /*
- * scaler.availableStream*, scaler.available*Durations, sensor.info.maxFrameDuration
- */
- mapScalerStreamConfigs(m, p);
-
- /*
* control.ae*
*/
mapControlAe(m, p);
@@ -205,6 +195,17 @@
* sync.*
*/
mapSync(m, p);
+
+ /*
+ * info.supportedHardwareLevel
+ */
+ m.set(INFO_SUPPORTED_HARDWARE_LEVEL, INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
+
+ /*
+ * scaler.availableStream*, scaler.available*Durations, sensor.info.maxFrameDuration
+ */
+ mapScalerStreamConfigs(m, p);
+
}
private static void mapScalerStreamConfigs(CameraMetadataNative m, Camera.Parameters p) {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
index 3345967..17dda08 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
@@ -176,8 +176,23 @@
Range<Integer> aeFpsRange = request.get(CONTROL_AE_TARGET_FPS_RANGE);
if (aeFpsRange != null) {
int[] legacyFps = convertAeFpsRangeToLegacy(aeFpsRange);
- params.setPreviewFpsRange(legacyFps[Parameters.PREVIEW_FPS_MIN_INDEX],
- legacyFps[Parameters.PREVIEW_FPS_MAX_INDEX]);
+
+ // TODO - Should we enforce that all HAL1 devices must include (30, 30) FPS range?
+ boolean supported = false;
+ for(int[] range : params.getSupportedPreviewFpsRange()) {
+ if (legacyFps[0] == range[0] && legacyFps[1] == range[1]) {
+ supported = true;
+ break;
+ }
+ }
+ if (supported) {
+ params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+ legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+ params.setRecordingHint(false);
+ } else {
+ Log.w(TAG, "Unsupported FPS range set [" + legacyFps[0] + "," + legacyFps[1] + "]");
+ params.setRecordingHint(true);
+ }
}
/*
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index a9f9355..e6d84c5 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -78,8 +78,8 @@
private volatile RequestHolder mInFlightPreview;
private volatile RequestHolder mInFlightJpeg;
- private final List<Surface> mPreviewOutputs = new ArrayList<Surface>();
- private final List<Surface> mCallbackOutputs = new ArrayList<Surface>();
+ private final List<Surface> mPreviewOutputs = new ArrayList<>();
+ private final List<Surface> mCallbackOutputs = new ArrayList<>();
private GLThreadManager mGLThreadManager;
private SurfaceTexture mPreviewTexture;
private Camera.Parameters mParams;
@@ -274,18 +274,6 @@
mPreviewTexture.setDefaultBufferSize(mIntermediateBufferSize.getWidth(),
mIntermediateBufferSize.getHeight());
mCamera.setPreviewTexture(mPreviewTexture);
- Camera.Parameters params = mCamera.getParameters();
- List<int[]> supportedFpsRanges = params.getSupportedPreviewFpsRange();
- int[] bestRange = getPhotoPreviewFpsRange(supportedFpsRanges);
- if (DEBUG) {
- Log.d(TAG, "doPreviewCapture - Selected range [" +
- bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + "," +
- bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] + "]");
- }
- params.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
- bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
- params.setRecordingHint(true);
- mCamera.setParameters(params);
startPreview();
}
@@ -322,6 +310,18 @@
}
}
mParams = mCamera.getParameters();
+
+ List<int[]> supportedFpsRanges = mParams.getSupportedPreviewFpsRange();
+ int[] bestRange = getPhotoPreviewFpsRange(supportedFpsRanges);
+ if (DEBUG) {
+ Log.d(TAG, "doPreviewCapture - Selected range [" +
+ bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + "," +
+ bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] + "]");
+ }
+ mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+ bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+ mParams.setRecordingHint(true);
+
if (mPreviewOutputs.size() > 0) {
List<Size> outputSizes = new ArrayList<>(outputs.size());
for (Surface s : mPreviewOutputs) {
diff --git a/core/java/android/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java
new file mode 100644
index 0000000..7bdcca2
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiClient.java
@@ -0,0 +1,51 @@
+package android.hardware.hdmi;
+
+import android.annotation.SystemApi;
+import android.hardware.hdmi.HdmiControlManager.VendorCommandListener;
+import android.hardware.hdmi.IHdmiVendorCommandListener;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Parent for classes of various HDMI-CEC device type used to access
+ * {@link HdmiControlService}. Contains methods and data used in common.
+ *
+ * @hide
+ */
+public abstract class HdmiClient {
+ private static final String TAG = "HdmiClient";
+
+ protected final IHdmiControlService mService;
+
+ protected abstract int getDeviceType();
+
+ public HdmiClient(IHdmiControlService service) {
+ mService = service;
+ }
+
+ public void sendVendorCommand(int targetAddress, byte[] params, boolean hasVendorId) {
+ try {
+ mService.sendVendorCommand(getDeviceType(), targetAddress, params, hasVendorId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to send vendor command: ", e);
+ }
+ }
+
+ public void addVendorCommandListener(VendorCommandListener listener) {
+ try {
+ mService.addVendorCommandListener(getListenerWrapper(listener), getDeviceType());
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to add vendor command listener: ", e);
+ }
+ }
+
+ private static IHdmiVendorCommandListener getListenerWrapper(
+ final VendorCommandListener listener) {
+ return new IHdmiVendorCommandListener.Stub() {
+ @Override
+ public void onReceived(int srcAddress, byte[] params, boolean hasVendorId) {
+ listener.onReceived(srcAddress, params, hasVendorId);
+ }
+ };
+ }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 80ec6f8..56fc1d6 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -17,6 +17,8 @@
package android.hardware.hdmi;
import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.os.RemoteException;
@@ -36,6 +38,22 @@
public final class HdmiControlManager {
@Nullable private final IHdmiControlService mService;
+ /**
+ * Broadcast Action: Display OSD message.
+ * <p>Send when the service has a message to display on screen for events
+ * that need user's attention such as ARC status change.
+ * <p>Always contains the extra fields {@link #EXTRA_MESSAGE}.
+ * <p>Requires {@link android.Manifest.permission#HDMI_CEC} to receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE";
+
+ /**
+ * Used as an extra field in the intent {@link #ACTION_OSD_MESSAGE}. Contains the ID of
+ * the message to display on screen.
+ */
+ public static final String EXTRA_MESSAGE_ID = "android.hardware.hdmi.extra.MESSAGE_ID";
+
public static final int POWER_STATUS_UNKNOWN = -1;
public static final int POWER_STATUS_ON = 0;
public static final int POWER_STATUS_STANDBY = 1;
@@ -130,6 +148,21 @@
}
/**
+ * Listener used to get vendor-specific commands.
+ */
+ public interface VendorCommandListener {
+ /**
+ * Called when a vendor command is received.
+ *
+ * @param srcAddress source logical address
+ * @param params vendor-specific parameters
+ * @param hasVendorId {@code true} if the command is <Vendor Command
+ * With ID>. The first 3 bytes of params is vendor id.
+ */
+ void onReceived(int srcAddress, byte[] params, boolean hasVendorId);
+ }
+
+ /**
* Adds a listener to get informed of {@link HdmiHotplugEvent}.
*
* <p>To stop getting the notification,
diff --git a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
index d7e9a9a..74cdc4e 100644
--- a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
+++ b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
@@ -18,7 +18,6 @@
import android.annotation.SystemApi;
import android.os.RemoteException;
-
import android.util.Log;
/**
@@ -30,11 +29,9 @@
* @hide
*/
@SystemApi
-public final class HdmiPlaybackClient {
+public final class HdmiPlaybackClient extends HdmiClient {
private static final String TAG = "HdmiPlaybackClient";
- private final IHdmiControlService mService;
-
/**
* Listener used by the client to get the result of one touch play operation.
*/
@@ -66,7 +63,7 @@
}
HdmiPlaybackClient(IHdmiControlService service) {
- mService = service;
+ super(service);
}
/**
@@ -85,6 +82,10 @@
}
}
+ public int getDeviceType() {
+ return HdmiCecDeviceInfo.DEVICE_PLAYBACK;
+ }
+
/**
* Get the status of display device connected through HDMI bus.
*
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index 6f65efc..1285ccb 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -16,6 +16,8 @@
package android.hardware.hdmi;
import android.annotation.SystemApi;
+import android.hardware.hdmi.HdmiControlManager.VendorCommandListener;
+import android.hardware.hdmi.IHdmiVendorCommandListener;
import android.os.RemoteException;
import android.util.Log;
@@ -27,7 +29,7 @@
* @hide
*/
@SystemApi
-public final class HdmiTvClient {
+public final class HdmiTvClient extends HdmiClient {
private static final String TAG = "HdmiTvClient";
// Definitions used for setOption(). These should be in sync with the definition
@@ -79,10 +81,8 @@
public static final int DISABLED = 0;
public static final int ENABLED = 1;
- private final IHdmiControlService mService;
-
HdmiTvClient(IHdmiControlService service) {
- mService = service;
+ super(service);
}
// Factory method for HdmiTvClient.
@@ -91,6 +91,10 @@
return new HdmiTvClient(service);
}
+ public int getDeviceType() {
+ return HdmiCecDeviceInfo.DEVICE_TV;
+ }
+
/**
* Callback interface used to get the result of {@link #deviceSelect}.
*/
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 3f0f2ae..3a477fb 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -23,6 +23,7 @@
import android.hardware.hdmi.IHdmiHotplugEventListener;
import android.hardware.hdmi.IHdmiInputChangeListener;
import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
+import android.hardware.hdmi.IHdmiVendorCommandListener;
import java.util.List;
@@ -57,4 +58,7 @@
oneway void setSystemAudioMute(boolean mute);
void setInputChangeListener(IHdmiInputChangeListener listener);
List<HdmiCecDeviceInfo> getInputDevices();
+ void sendVendorCommand(int deviceType, int targetAddress, in byte[] params,
+ boolean hasVendorId);
+ void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType);
}
diff --git a/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
new file mode 100644
index 0000000..55cc925
--- /dev/null
+++ b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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 android.hardware.hdmi;
+
+/**
+ * Callback interface definition for HDMI client to get the vendor-specific
+ * commands.
+ *
+ * @hide
+ */
+oneway interface IHdmiVendorCommandListener {
+ void onReceived(int logicalAddress, in byte[] operands, boolean hasVendorId);
+}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 4c34d46..fe47f5b 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -371,6 +371,8 @@
* attacks.
*/
public static boolean contains(File dir, File file) {
+ if (file == null) return false;
+
String dirPath = dir.getAbsolutePath();
String filePath = file.getAbsolutePath();
@@ -418,16 +420,27 @@
}
public static String rewriteAfterRename(File beforeDir, File afterDir, String path) {
+ if (path == null) return null;
final File result = rewriteAfterRename(beforeDir, afterDir, new File(path));
return (result != null) ? result.getAbsolutePath() : null;
}
+ public static String[] rewriteAfterRename(File beforeDir, File afterDir, String[] paths) {
+ if (paths == null) return null;
+ final String[] result = new String[paths.length];
+ for (int i = 0; i < paths.length; i++) {
+ result[i] = rewriteAfterRename(beforeDir, afterDir, paths[i]);
+ }
+ return result;
+ }
+
/**
* Given a path under the "before" directory, rewrite it to live under the
* "after" directory. For example, {@code /before/foo/bar.txt} would become
* {@code /after/foo/bar.txt}.
*/
public static File rewriteAfterRename(File beforeDir, File afterDir, File file) {
+ if (file == null) return null;
if (contains(beforeDir, file)) {
final String splice = file.getAbsolutePath().substring(
beforeDir.getAbsolutePath().length());
diff --git a/core/java/android/os/SELinux.java b/core/java/android/os/SELinux.java
index 71d12c6..84aa427 100644
--- a/core/java/android/os/SELinux.java
+++ b/core/java/android/os/SELinux.java
@@ -28,9 +28,15 @@
* {@hide}
*/
public class SELinux {
-
private static final String TAG = "SELinux";
+ /** Keep in sync with ./external/libselinux/include/selinux/android.h */
+ private static final int SELINUX_ANDROID_RESTORECON_NOCHANGE = 1;
+ private static final int SELINUX_ANDROID_RESTORECON_VERBOSE = 2;
+ private static final int SELINUX_ANDROID_RESTORECON_RECURSE = 4;
+ private static final int SELINUX_ANDROID_RESTORECON_FORCE = 8;
+ private static final int SELINUX_ANDROID_RESTORECON_DATADATA = 16;
+
/**
* Determine whether SELinux is disabled or enabled.
* @return a boolean indicating whether SELinux is enabled.
@@ -136,7 +142,7 @@
*/
public static boolean restorecon(String pathname) throws NullPointerException {
if (pathname == null) { throw new NullPointerException(); }
- return native_restorecon(pathname);
+ return native_restorecon(pathname, 0);
}
/**
@@ -149,7 +155,7 @@
* @param pathname The pathname of the file to be relabeled.
* @return a boolean indicating whether the relabeling succeeded.
*/
- private static native boolean native_restorecon(String pathname);
+ private static native boolean native_restorecon(String pathname, int flags);
/**
* Restores a file to its default SELinux security context.
@@ -164,10 +170,10 @@
*/
public static boolean restorecon(File file) throws NullPointerException {
try {
- return native_restorecon(file.getCanonicalPath());
+ return native_restorecon(file.getCanonicalPath(), 0);
} catch (IOException e) {
Slog.e(TAG, "Error getting canonical path. Restorecon failed for " +
- file.getPath(), e);
+ file.getPath(), e);
return false;
}
}
@@ -180,14 +186,13 @@
*
* @return a boolean indicating whether the relabeling succeeded.
*/
- public static boolean restoreconTree(File dir) {
- final File[] files = dir.listFiles();
- boolean success = true;
- if (files != null) {
- for (File file : files) {
- success &= restorecon(file);
- }
+ public static boolean restoreconRecursive(File file) {
+ try {
+ return native_restorecon(file.getCanonicalPath(), SELINUX_ANDROID_RESTORECON_RECURSE);
+ } catch (IOException e) {
+ Slog.e(TAG, "Error getting canonical path. Restorecon failed for " +
+ file.getPath(), e);
+ return false;
}
- return success;
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 01e1f8a..206b75d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6190,18 +6190,21 @@
"lock_screen_show_notifications";
/**
- * Defines global zen mode. ZEN_MODE_OFF or ZEN_MODE_ON.
+ * Defines global zen mode. ZEN_MODE_OFF, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ * or ZEN_MODE_NO_INTERRUPTIONS.
*
* @hide
*/
public static final String ZEN_MODE = "zen_mode";
/** @hide */ public static final int ZEN_MODE_OFF = 0;
- /** @hide */ public static final int ZEN_MODE_ON = 1;
+ /** @hide */ public static final int ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1;
+ /** @hide */ public static final int ZEN_MODE_NO_INTERRUPTIONS = 2;
/** @hide */ public static String zenModeToString(int mode) {
- if (mode == ZEN_MODE_OFF) return "ZEN_MODE_OFF";
- return "ZEN_MODE_ON";
+ if (mode == ZEN_MODE_IMPORTANT_INTERRUPTIONS) return "ZEN_MODE_IMPORTANT_INTERRUPTIONS";
+ if (mode == ZEN_MODE_NO_INTERRUPTIONS) return "ZEN_MODE_NO_INTERRUPTIONS";
+ return "ZEN_MODE_OFF";
}
/**
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 68a3d30..8fc3c5a 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -30,6 +30,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Calendar;
import java.util.Objects;
/**
@@ -42,12 +43,18 @@
public static final String SLEEP_MODE_NIGHTS = "nights";
public static final String SLEEP_MODE_WEEKNIGHTS = "weeknights";
+ public static final String SLEEP_MODE_DAYS_PREFIX = "days:";
public static final int SOURCE_ANYONE = 0;
public static final int SOURCE_CONTACT = 1;
public static final int SOURCE_STAR = 2;
public static final int MAX_SOURCE = SOURCE_STAR;
+ public static final int[] ALL_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
+ Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY };
+ public static final int[] WEEKNIGHT_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
+ Calendar.WEDNESDAY, Calendar.THURSDAY };
+
private static final int XML_VERSION = 1;
private static final String ZEN_TAG = "zen";
private static final String ZEN_ATT_VERSION = "version";
@@ -198,8 +205,39 @@
public boolean isValid() {
return isValidHour(sleepStartHour) && isValidMinute(sleepStartMinute)
&& isValidHour(sleepEndHour) && isValidMinute(sleepEndMinute)
- && (sleepMode == null || sleepMode.equals(SLEEP_MODE_NIGHTS)
- || sleepMode.equals(SLEEP_MODE_WEEKNIGHTS));
+ && isValidSleepMode(sleepMode);
+ }
+
+ public static boolean isValidSleepMode(String sleepMode) {
+ return sleepMode == null || sleepMode.equals(SLEEP_MODE_NIGHTS)
+ || sleepMode.equals(SLEEP_MODE_WEEKNIGHTS) || tryParseDays(sleepMode) != null;
+ }
+
+ public static int[] tryParseDays(String sleepMode) {
+ if (sleepMode == null) return null;
+ sleepMode = sleepMode.trim();
+ if (SLEEP_MODE_NIGHTS.equals(sleepMode)) return ALL_DAYS;
+ if (SLEEP_MODE_WEEKNIGHTS.equals(sleepMode)) return WEEKNIGHT_DAYS;
+ if (!sleepMode.startsWith(SLEEP_MODE_DAYS_PREFIX)) return null;
+ if (sleepMode.equals(SLEEP_MODE_DAYS_PREFIX)) return null;
+ final String[] tokens = sleepMode.substring(SLEEP_MODE_DAYS_PREFIX.length()).split(",");
+ if (tokens.length == 0) return null;
+ final int[] rt = new int[tokens.length];
+ for (int i = 0; i < tokens.length; i++) {
+ final int day = tryParseInt(tokens[i], -1);
+ if (day == -1) return null;
+ rt[i] = day;
+ }
+ return rt;
+ }
+
+ private static int tryParseInt(String value, int defValue) {
+ if (TextUtils.isEmpty(value)) return defValue;
+ try {
+ return Integer.valueOf(value);
+ } catch (NumberFormatException e) {
+ return defValue;
+ }
}
public static ZenModeConfig readXml(XmlPullParser parser)
@@ -209,7 +247,7 @@
String tag = parser.getName();
if (!ZEN_TAG.equals(tag)) return null;
final ZenModeConfig rt = new ZenModeConfig();
- final int version = Integer.parseInt(parser.getAttributeValue(null, ZEN_ATT_VERSION));
+ final int version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION);
final ArrayList<ComponentName> conditionComponents = new ArrayList<ComponentName>();
final ArrayList<Uri> conditionIds = new ArrayList<Uri>();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -232,8 +270,7 @@
}
} else if (SLEEP_TAG.equals(tag)) {
final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE);
- rt.sleepMode = (SLEEP_MODE_NIGHTS.equals(mode)
- || SLEEP_MODE_WEEKNIGHTS.equals(mode)) ? mode : null;
+ rt.sleepMode = isValidSleepMode(mode)? mode : null;
final int startHour = safeInt(parser, SLEEP_ATT_START_HR, 0);
final int startMinute = safeInt(parser, SLEEP_ATT_START_MIN, 0);
final int endHour = safeInt(parser, SLEEP_ATT_END_HR, 0);
@@ -312,8 +349,7 @@
private static int safeInt(XmlPullParser parser, String att, int defValue) {
final String val = parser.getAttributeValue(null, att);
- if (TextUtils.isEmpty(val)) return defValue;
- return Integer.valueOf(val);
+ return tryParseInt(val, defValue);
}
private static ComponentName safeComponentName(XmlPullParser parser, String att) {
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 57d1beb..fa4564e 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -127,6 +127,8 @@
@Override
void destroyHardwareResources(View view) {
destroyResources(view);
+ // mRootNode belongs to us and not a view, so we need to destroy it
+ mRootNode.destroyDisplayListData();
nDestroyHardwareResources(mNativeProxy);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3b2e1d1..706fb1c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -13625,9 +13625,7 @@
* @hide
*/
protected void destroyHardwareResources() {
- // Intentionally empty. RenderNode's lifecycle is now fully managed
- // by the hardware renderer.
- // However some subclasses (eg, WebView, TextureView) still need this signal
+ resetDisplayList();
}
/**
@@ -14996,6 +14994,7 @@
if (mBackgroundSizeChanged) {
background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false;
+ invalidateOutline();
}
// Attempt to use a display list if requested.
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 66cc3f6..1bf4639 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -555,6 +555,7 @@
private CharSequence mPackageName;
private CharSequence mClassName;
private CharSequence mText;
+ private CharSequence mError;
private CharSequence mContentDescription;
private String mViewIdResourceName;
@@ -1923,6 +1924,32 @@
}
/**
+ * Sets the error text of this node.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param error The error text.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setError(CharSequence error) {
+ enforceNotSealed();
+ mError = error;
+ }
+
+ /**
+ * Gets the error text of this node.
+ *
+ * @return The error text.
+ */
+ public CharSequence getError() {
+ return mError;
+ }
+
+ /**
* Gets the content description of this node.
*
* @return The content description.
@@ -2449,6 +2476,7 @@
parcel.writeCharSequence(mPackageName);
parcel.writeCharSequence(mClassName);
parcel.writeCharSequence(mText);
+ parcel.writeCharSequence(mError);
parcel.writeCharSequence(mContentDescription);
parcel.writeString(mViewIdResourceName);
@@ -2519,6 +2547,7 @@
mPackageName = other.mPackageName;
mClassName = other.mClassName;
mText = other.mText;
+ mError = other.mError;
mContentDescription = other.mContentDescription;
mViewIdResourceName = other.mViewIdResourceName;
@@ -2614,6 +2643,7 @@
mPackageName = parcel.readCharSequence();
mClassName = parcel.readCharSequence();
mText = parcel.readCharSequence();
+ mError = parcel.readCharSequence();
mContentDescription = parcel.readCharSequence();
mViewIdResourceName = parcel.readString();
@@ -2675,6 +2705,7 @@
mPackageName = null;
mClassName = null;
mText = null;
+ mError = null;
mContentDescription = null;
mViewIdResourceName = null;
if (mActions != null) {
@@ -2879,6 +2910,7 @@
builder.append("; packageName: ").append(mPackageName);
builder.append("; className: ").append(mClassName);
builder.append("; text: ").append(mText);
+ builder.append("; error: ").append(mError);
builder.append("; contentDescription: ").append(mContentDescription);
builder.append("; viewIdResName: ").append(mViewIdResourceName);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 8f8876b..3a1f6ed 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3326,8 +3326,8 @@
if (mLastY == Integer.MIN_VALUE) {
rawDeltaY -= mMotionCorrection;
}
- if (dispatchNestedPreScroll(0, rawDeltaY, mScrollConsumed, mScrollOffset)) {
- rawDeltaY -= mScrollConsumed[1];
+ if (dispatchNestedPreScroll(0, -rawDeltaY, mScrollConsumed, mScrollOffset)) {
+ rawDeltaY += mScrollConsumed[1];
scrollOffsetCorrection -= mScrollOffset[1];
scrollConsumedCorrection -= mScrollConsumed[1];
if (vtev != null) {
@@ -3853,7 +3853,8 @@
// Since we can potentially overfling more than we can overscroll, don't
// allow the weird behavior where you can scroll to a boundary then
// fling further.
- if (Math.abs(initialVelocity) > mMinimumVelocity &&
+ boolean flingVelocity = Math.abs(initialVelocity) > mMinimumVelocity;
+ if (flingVelocity &&
!((mFirstPosition == 0 &&
firstChildTop == contentTop - mOverscrollDistance) ||
(mFirstPosition + childCount == mItemCount &&
@@ -3864,6 +3865,7 @@
reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
mFlingRunnable.start(-initialVelocity);
+ dispatchNestedFling(0, -initialVelocity, true);
} else {
mTouchMode = TOUCH_MODE_REST;
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
@@ -3873,6 +3875,9 @@
if (mPositionScroller != null) {
mPositionScroller.stop();
}
+ if (flingVelocity) {
+ dispatchNestedFling(0, -initialVelocity, false);
+ }
}
}
} else {
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 90fec23..3758d86 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -330,7 +330,7 @@
canvas.clipRect(mBounds);
canvas.translate(translateX, 0);
- canvas.drawArc(mArcRect, 45, 90, true, mPaint);
+ canvas.drawArc(mArcRect, 45, 90, false, mPaint);
canvas.restoreToCount(count);
boolean oneLastFrame = false;
diff --git a/core/java/android/widget/LegacyTimePickerDelegate.java b/core/java/android/widget/LegacyTimePickerDelegate.java
index 1634d5f..97f46ea 100644
--- a/core/java/android/widget/LegacyTimePickerDelegate.java
+++ b/core/java/android/widget/LegacyTimePickerDelegate.java
@@ -75,6 +75,11 @@
// accommodates these two cases to be backwards compatible.
private final Button mAmPmButton;
+ // May be null if layout has no done button
+ private final View mDoneButton;
+ private boolean mShowDoneButton;
+ private TimePicker.TimePickerDismissCallback mDismissCallback;
+
private final String[] mAmPmStrings;
private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
@@ -218,6 +223,19 @@
}
}
+ mDoneButton = delegator.findViewById(R.id.done_button);
+ if (mDoneButton != null) {
+ mDoneButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mDismissCallback != null) {
+ mDismissCallback.dismiss(mDelegator, false, getCurrentHour(),
+ getCurrentMinute());
+ }
+ }
+ });
+ }
+
getHourFormatData();
// update controls to initial state
@@ -242,6 +260,9 @@
if (mDelegator.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
mDelegator.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
+
+ mShowDoneButton = false;
+ updateDoneButton();
}
private void getHourFormatData() {
@@ -408,12 +429,23 @@
@Override
public void setShowDoneButton(boolean showDoneButton) {
- // Nothing to do
+ mShowDoneButton = showDoneButton;
+ updateDoneButton();
+ }
+
+ private boolean isShowDoneButton() {
+ return mShowDoneButton;
+ }
+
+ private void updateDoneButton() {
+ if (mDoneButton != null) {
+ mDoneButton.setVisibility(mShowDoneButton ? View.VISIBLE : View.GONE);
+ }
}
@Override
public void setDismissCallback(TimePicker.TimePickerDismissCallback callback) {
- // Nothing to do
+ mDismissCallback = callback;
}
@Override
@@ -428,7 +460,8 @@
@Override
public Parcelable onSaveInstanceState(Parcelable superState) {
- return new SavedState(superState, getCurrentHour(), getCurrentMinute());
+ return new SavedState(superState, getCurrentHour(), getCurrentMinute(),
+ isShowDoneButton());
}
@Override
@@ -436,6 +469,7 @@
SavedState ss = (SavedState) state;
setCurrentHour(ss.getHour());
setCurrentMinute(ss.getMinute());
+ setShowDoneButton(ss.isShowDoneButton());
}
@Override
@@ -596,16 +630,20 @@
private final int mMinute;
- private SavedState(Parcelable superState, int hour, int minute) {
+ private final boolean mShowDoneButton;
+
+ private SavedState(Parcelable superState, int hour, int minute, boolean showDoneButton) {
super(superState);
mHour = hour;
mMinute = minute;
+ mShowDoneButton = showDoneButton;
}
private SavedState(Parcel in) {
super(in);
mHour = in.readInt();
mMinute = in.readInt();
+ mShowDoneButton = (in.readInt() == 1);
}
public int getHour() {
@@ -616,11 +654,16 @@
return mMinute;
}
+ public boolean isShowDoneButton() {
+ return mShowDoneButton;
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(mHour);
dest.writeInt(mMinute);
+ dest.writeInt(mShowDoneButton ? 1 : 0);
}
@SuppressWarnings({"unused", "hiding"})
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 755bb48..ee17b78 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -478,6 +478,11 @@
private int mLastHandledDownDpadKeyCode = -1;
/**
+ * If true then the selector wheel is hidden until the picker has focus.
+ */
+ private boolean mHideWheelUntilFocused;
+
+ /**
* Interface to listen for changes of the current value.
*/
public interface OnValueChangeListener {
@@ -598,6 +603,9 @@
mHasSelectorWheel = (layoutResId != DEFAULT_LAYOUT_RESOURCE_ID);
+ mHideWheelUntilFocused = attributesArray.getBoolean(
+ R.styleable.NumberPicker_hideWheelUntilFocused, false);
+
mSolidColor = attributesArray.getColor(R.styleable.NumberPicker_solidColor, 0);
mSelectionDivider = attributesArray.getDrawable(R.styleable.NumberPicker_selectionDivider);
@@ -974,8 +982,8 @@
}
switch (event.getAction()) {
case KeyEvent.ACTION_DOWN:
- if (mWrapSelectorWheel || (keyCode == KeyEvent.KEYCODE_DPAD_DOWN)
- ? getValue() < getMaxValue() : getValue() > getMinValue()) {
+ if (mWrapSelectorWheel || ((keyCode == KeyEvent.KEYCODE_DPAD_DOWN)
+ ? getValue() < getMaxValue() : getValue() > getMinValue())) {
requestFocus();
mLastHandledDownDpadKeyCode = keyCode;
removeAllCallbacks();
@@ -1497,11 +1505,12 @@
super.onDraw(canvas);
return;
}
+ final boolean showSelectorWheel = mHideWheelUntilFocused ? hasFocus() : true;
float x = (mRight - mLeft) / 2;
float y = mCurrentScrollOffset;
// draw the virtual buttons pressed state if needed
- if (mVirtualButtonPressedDrawable != null
+ if (showSelectorWheel && mVirtualButtonPressedDrawable != null
&& mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
if (mDecrementVirtualButtonPressed) {
mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET);
@@ -1526,14 +1535,15 @@
// item. Otherwise, if the user starts editing the text via the
// IME he may see a dimmed version of the old value intermixed
// with the new one.
- if (i != SELECTOR_MIDDLE_ITEM_INDEX || mInputText.getVisibility() != VISIBLE) {
+ if ((showSelectorWheel && i != SELECTOR_MIDDLE_ITEM_INDEX) ||
+ (i == SELECTOR_MIDDLE_ITEM_INDEX && mInputText.getVisibility() != VISIBLE)) {
canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint);
}
y += mSelectorElementHeight;
}
// draw the selection dividers
- if (mSelectionDivider != null) {
+ if (showSelectorWheel && mSelectionDivider != null) {
// draw the top divider
int topOfTopDivider = mTopSelectionDividerTop;
int bottomOfTopDivider = topOfTopDivider + mSelectionDividerHeight;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2381a78..dd58ba6 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8240,6 +8240,7 @@
if (mEditor.mError != null) {
info.setContentInvalid(true);
+ info.setError(mEditor.mError);
}
}
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 52f611a..b8fcff6 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -201,7 +201,7 @@
int sampleSize = 1;
SkImageDecoder::Mode decodeMode = SkImageDecoder::kDecodePixels_Mode;
- SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
+ SkColorType prefColorType = kN32_SkColorType;
bool doDither = true;
bool isMutable = false;
@@ -223,7 +223,7 @@
env->SetObjectField(options, gOptions_mimeFieldID, 0);
jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
- prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
+ prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
preferQualityOverSpeed = env->GetBooleanField(options,
@@ -305,7 +305,7 @@
}
SkBitmap decodingBitmap;
- if (!decoder->decode(stream, &decodingBitmap, prefConfig, decodeMode)) {
+ if (!decoder->decode(stream, &decodingBitmap, prefColorType, decodeMode)) {
return nullObjectReturn("decoder->decode returned false");
}
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index ead2de3..91efc8c 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -60,7 +60,7 @@
}
bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect,
- SkBitmap::Config pref, int sampleSize) {
+ SkColorType pref, int sampleSize) {
fDecoder->setSampleSize(sampleSize);
return fDecoder->decodeSubset(bitmap, rect, pref);
}
@@ -175,7 +175,7 @@
jobject tileBitmap = NULL;
SkImageDecoder *decoder = brd->getDecoder();
int sampleSize = 1;
- SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
+ SkColorType prefColorType = kUnknown_SkColorType;
bool doDither = true;
bool preferQualityOverSpeed = false;
bool requireUnpremultiplied = false;
@@ -188,7 +188,7 @@
env->SetObjectField(options, gOptions_mimeFieldID, 0);
jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
- prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
+ prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
preferQualityOverSpeed = env->GetBooleanField(options,
gOptions_preferQualityOverSpeedFieldID);
@@ -226,7 +226,7 @@
adb.reset(bitmap);
}
- if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
+ if (!brd->decodeRegion(bitmap, region, prefColorType, sampleSize)) {
return nullObjectReturn("decoder->decodeRegion returned false");
}
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 2bd7a28..7a186a2 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -301,18 +301,17 @@
return b;
}
-SkBitmap::Config GraphicsJNI::getNativeBitmapConfig(JNIEnv* env,
- jobject jconfig) {
+SkColorType GraphicsJNI::getNativeBitmapColorType(JNIEnv* env, jobject jconfig) {
SkASSERT(env);
if (NULL == jconfig) {
- return SkBitmap::kNo_Config;
+ return kUnknown_SkColorType;
}
SkASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class));
int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID);
if (c < 0 || c >= SkBitmap::kConfigCount) {
- c = SkBitmap::kNo_Config;
+ c = kUnknown_SkColorType;
}
- return static_cast<SkBitmap::Config>(c);
+ return SkBitmapConfigToColorType(static_cast<SkBitmap::Config>(c));
}
SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 0ad7fd8..6d82ceb 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -58,10 +58,10 @@
// ref to its SkRasterizer* (or NULL).
static SkRasterizer* refNativeRasterizer(jlong rasterizerHandle);
- /** Return the corresponding native config from the java Config enum,
- or kNo_Config if the java object is null.
+ /** Return the corresponding native colorType from the java Config enum,
+ or kUnknown_SkColorType if the java object is null.
*/
- static SkBitmap::Config getNativeBitmapConfig(JNIEnv*, jobject jconfig);
+ static SkColorType getNativeBitmapColorType(JNIEnv*, jobject jconfig);
/** Create a java Bitmap object given the native bitmap (required) and optional
storage array (may be null).
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 26405b5..ffa569e 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -404,7 +404,7 @@
* Returns: boolean: (true) file label successfully restored, (false) otherwise
* Exceptions: none
*/
-static jboolean native_restorecon(JNIEnv *env, jobject, jstring pathnameStr) {
+static jboolean native_restorecon(JNIEnv *env, jobject, jstring pathnameStr, jint flags) {
if (isSELinuxDisabled) {
return true;
}
@@ -415,7 +415,7 @@
return false;
}
- int ret = selinux_android_restorecon(pathname.c_str(), 0);
+ int ret = selinux_android_restorecon(pathname.c_str(), flags);
ALOGV("restorecon(%s) => %d", pathname.c_str(), ret);
return (ret == 0);
}
@@ -434,7 +434,7 @@
{ "getPidContext" , "(I)Ljava/lang/String;" , (void*)getPidCon },
{ "isSELinuxEnforced" , "()Z" , (void*)isSELinuxEnforced},
{ "isSELinuxEnabled" , "()Z" , (void*)isSELinuxEnabled },
- { "native_restorecon" , "(Ljava/lang/String;)Z" , (void*)native_restorecon},
+ { "native_restorecon" , "(Ljava/lang/String;I)Z" , (void*)native_restorecon},
{ "setBooleanValue" , "(Ljava/lang/String;Z)Z" , (void*)setBooleanValue },
{ "setFileContext" , "(Ljava/lang/String;Ljava/lang/String;)Z" , (void*)setFileCon },
{ "setFSCreateContext" , "(Ljava/lang/String;)Z" , (void*)setFSCreateCon },
diff --git a/core/res/res/anim/disabled_anim_material.xml b/core/res/res/anim/disabled_anim_material.xml
new file mode 100644
index 0000000..6a7731e
--- /dev/null
+++ b/core/res/res/anim/disabled_anim_material.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false">
+ <set>
+ <objectAnimator android:propertyName="alpha"
+ android:duration="@integer/disabled_alpha_animation_duration"
+ android:valueTo="?attr/disabledAlpha"
+ android:valueType="floatType"/>
+ </set>
+ </item>
+ <item>
+ <set>
+ <objectAnimator android:propertyName="alpha"
+ android:duration="@integer/disabled_alpha_animation_duration"
+ android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </item>
+</selector>
\ No newline at end of file
diff --git a/core/res/res/layout/alert_dialog_material.xml b/core/res/res/layout/alert_dialog_material.xml
index 927b7b2..93acc3f 100644
--- a/core/res/res/layout/alert_dialog_material.xml
+++ b/core/res/res/layout/alert_dialog_material.xml
@@ -83,7 +83,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:minHeight="64dp">
- <FrameLayout android:id="@+android:id/custom"
+ <FrameLayout android:id="@+id/custom"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
@@ -101,7 +101,7 @@
android:layout_height="wrap_content"
android:layoutDirection="locale">
<Button android:id="@+id/button3"
- style="?attr/buttonBarButtonStyle"
+ style="?attr/buttonBarNeutralButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
@@ -112,13 +112,13 @@
android:layout_weight="1"
android:visibility="invisible" />
<Button android:id="@+id/button2"
- style="?attr/buttonBarButtonStyle"
+ style="?attr/buttonBarNegativeButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:minHeight="@dimen/alert_dialog_button_bar_height" />
<Button android:id="@+id/button1"
- style="?attr/buttonBarButtonStyle"
+ style="?attr/buttonBarPositiveButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
diff --git a/core/res/res/layout/time_picker_legacy_leanback.xml b/core/res/res/layout/time_picker_legacy_leanback.xml
new file mode 100644
index 0000000..397f733
--- /dev/null
+++ b/core/res/res/layout/time_picker_legacy_leanback.xml
@@ -0,0 +1,92 @@
+<?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:id="@+id/timePickerLayout"
+ android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingStart="8dip"
+ android:paddingEnd="8dip">
+
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingStart="8dip"
+ android:paddingEnd="8dip"
+ android:layoutDirection="ltr">
+
+ <!-- hour -->
+ <NumberPicker
+ android:id="@+id/hour"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dip"
+ android:layout_marginBottom="16dip"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ />
+
+ <!-- divider -->
+ <TextView
+ android:id="@+id/divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="6dip"
+ android:layout_marginEnd="6dip"
+ android:layout_gravity="center_vertical"
+ android:importantForAccessibility="no"
+ />
+
+ <!-- minute -->
+ <NumberPicker
+ android:id="@+id/minute"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dip"
+ android:layout_marginBottom="16dip"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ />
+
+ </LinearLayout>
+
+ <!-- AM / PM -->
+ <NumberPicker
+ android:id="@+id/amPm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dip"
+ android:layout_marginBottom="16dip"
+ android:layout_marginStart="8dip"
+ android:layout_marginEnd="8dip"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ />
+
+ <!-- Width fixed here because TextView doesn't set MEASURED_STATE_TOO_SMALL -->
+ <Button
+ android:id="@+id/done_button"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/done_label"
+ android:textSize="@dimen/timepicker_done_label_size"
+ style="?android:attr/buttonBarButtonStyle" />
+
+</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 44125b0..4b708a7 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -894,6 +894,15 @@
<!-- Style for buttons within button bars -->
<attr name="buttonBarButtonStyle" format="reference" />
+ <!-- Style for the "positive" buttons within button bars -->
+ <attr name="buttonBarPositiveButtonStyle" format="reference" />
+
+ <!-- Style for the "negative" buttons within button bars -->
+ <attr name="buttonBarNegativeButtonStyle" format="reference" />
+
+ <!-- Style for the "neutral" buttons within button bars -->
+ <attr name="buttonBarNeutralButtonStyle" format="reference" />
+
<!-- Style for the search query widget. -->
<attr name="searchViewStyle" format="reference" />
@@ -4411,6 +4420,8 @@
<attr name="internalLayout" />
<!-- @hide The drawable for pressed virtual (increment/decrement) buttons. -->
<attr name="virtualButtonPressedDrawable" format="reference"/>
+ <!-- @hide If true then the selector wheel is hidden until the picker has focus. -->
+ <attr name="hideWheelUntilFocused" format="boolean"/>
</declare-styleable>
<declare-styleable name="TimePicker">
diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml
index fd61f73..0343cfa 100644
--- a/core/res/res/values/integers.xml
+++ b/core/res/res/values/integers.xml
@@ -21,4 +21,5 @@
<integer name="kg_glowpad_rotation_offset">0</integer>
<integer name="button_pressed_animation_duration">100</integer>
<integer name="button_pressed_animation_delay">100</integer>
+ <integer name="disabled_alpha_animation_duration">100</integer>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 8e93353..99c3450 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2228,6 +2228,9 @@
<public type="attr" name="suggestionRowLayout" />
<public type="attr" name="queryBackground" />
<public type="attr" name="submitBackground" />
+ <public type="attr" name="buttonBarPositiveButtonStyle" />
+ <public type="attr" name="buttonBarNeutralButtonStyle" />
+ <public type="attr" name="buttonBarNegativeButtonStyle" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
@@ -2470,6 +2473,9 @@
<public type="style" name="TextAppearance.Material.Menu" />
<public type="style" name="TextAppearance.Material.Button" />
+ <public type="style" name="Widget.Material.Button.Borderless.Colored" />
+ <public type="style" name="Widget.Material.Light.Button.Borderless.Colored" />
+
<public-padding type="interpolator" name="l_resource_pad" end="0x010c0010" />
<!-- An interpolator which accelerates fast but decelerates slowly. -->
diff --git a/core/res/res/values/styles_leanback.xml b/core/res/res/values/styles_leanback.xml
index 72d0379..6f9a88e 100644
--- a/core/res/res/values/styles_leanback.xml
+++ b/core/res/res/values/styles_leanback.xml
@@ -22,4 +22,12 @@
<style name="AlertDialog.Leanback.Light">
</style>
+ <style name="Widget.Leanback.TimePicker" parent="Widget.Material.TimePicker">
+ <item name="legacyLayout">@layout/time_picker_legacy_leanback</item>
+ </style>
+
+ <style name="Widget.Leanback.NumberPicker" parent="Widget.Material.NumberPicker">
+ <item name="hideWheelUntilFocused">true</item>
+ </style>
+
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index ea67f46..c1eb999 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -415,6 +415,12 @@
<item name="stateListAnimator">@null</item>
</style>
+ <!-- Colored borderless ink button -->
+ <style name="Widget.Material.Button.Borderless.Colored">
+ <item name="textColor">?attr/colorAccent</item>
+ <item name="stateListAnimator">@anim/disabled_anim_material</item>
+ </style>
+
<!-- Small borderless ink button -->
<style name="Widget.Material.Button.Borderless.Small">
<item name="minHeight">48dip</item>
@@ -837,6 +843,7 @@
<style name="Widget.Material.Light.Button" parent="Widget.Material.Button"/>
<style name="Widget.Material.Light.Button.Small" parent="Widget.Material.Button.Small"/>
<style name="Widget.Material.Light.Button.Borderless" parent="Widget.Material.Button.Borderless"/>
+ <style name="Widget.Material.Light.Button.Borderless.Colored" parent="Widget.Material.Button.Borderless.Colored"/>
<style name="Widget.Material.Light.Button.Borderless.Small" parent="Widget.Material.Button.Borderless.Small"/>
<style name="Widget.Material.Light.Button.Inset" parent="Widget.Material.Button.Inset"/>
<style name="Widget.Material.Light.Button.Toggle" parent="Widget.Material.Button.Toggle" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 8edfc8f..b1bf123 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -371,6 +371,9 @@
<item name="dividerHorizontal">@drawable/divider_vertical_dark</item>
<item name="buttonBarStyle">@style/ButtonBar</item>
<item name="buttonBarButtonStyle">?attr/buttonStyle</item>
+ <item name="buttonBarPositiveButtonStyle">?attr/buttonBarButtonStyle</item>
+ <item name="buttonBarNegativeButtonStyle">?attr/buttonBarButtonStyle</item>
+ <item name="buttonBarNeutralButtonStyle">?attr/buttonBarButtonStyle</item>
<item name="segmentedButtonStyle">@style/SegmentedButton</item>
<!-- SearchView attributes -->
diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml
index a571b98..6f6385b 100644
--- a/core/res/res/values/themes_leanback.xml
+++ b/core/res/res/values/themes_leanback.xml
@@ -19,6 +19,7 @@
<item name="textColorPrimary">@color/primary_text_leanback_dark</item>
<item name="textColorSecondary">@color/secondary_text_leanback_dark</item>
<item name="alertDialogStyle">@style/AlertDialog.Leanback</item>
+ <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
</style>
<style name="Theme.Leanback.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.BaseAlert">
@@ -26,6 +27,7 @@
<item name="textColorPrimary">@color/primary_text_leanback_light</item>
<item name="textColorSecondary">@color/secondary_text_leanback_light</item>
<item name="alertDialogStyle">@style/AlertDialog.Leanback.Light</item>
+ <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
</style>
<style name="Theme.Leanback.Dialog.TimePicker" parent="Theme.Material.Dialog.BaseTimePicker">
@@ -33,6 +35,8 @@
<item name="textColorPrimary">@color/primary_text_leanback_dark</item>
<item name="textColorSecondary">@color/secondary_text_leanback_dark</item>
<item name="alertDialogStyle">@style/AlertDialog.Leanback</item>
+ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
+ <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
</style>
<style name="Theme.Leanback.Light.Dialog.TimePicker" parent="Theme.Material.Light.Dialog.BaseTimePicker">
@@ -40,6 +44,8 @@
<item name="textColorPrimary">@color/primary_text_leanback_light</item>
<item name="textColorSecondary">@color/secondary_text_leanback_light</item>
<item name="alertDialogStyle">@style/AlertDialog.Leanback.Light</item>
+ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
+ <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
</style>
<style name="Theme.Leanback.Light.Dialog" parent="Theme.Material.Light.Dialog">
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 8646524..472177f 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -332,6 +332,7 @@
<item name="dividerHorizontal">?attr/listDivider</item>
<item name="buttonBarStyle">@style/Widget.Material.ButtonBar</item>
<item name="buttonBarButtonStyle">@style/Widget.Material.Button.Borderless</item>
+ <item name="buttonBarPositiveButtonStyle">@style/Widget.Material.Button.Borderless.Colored</item>
<item name="segmentedButtonStyle">@style/Widget.Material.SegmentedButton</item>
<!-- SearchView attributes -->
@@ -681,6 +682,7 @@
<item name="dividerHorizontal">?attr/listDivider</item>
<item name="buttonBarStyle">@style/Widget.Material.Light.ButtonBar</item>
<item name="buttonBarButtonStyle">@style/Widget.Material.Light.Button.Borderless</item>
+ <item name="buttonBarPositiveButtonStyle">@style/Widget.Material.Light.Button.Borderless.Colored</item>
<item name="segmentedButtonStyle">@style/Widget.Material.Light.SegmentedButton</item>
<!-- SearchView attributes -->
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 6437e07..86a45c5 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -56,7 +56,6 @@
droidsans_fallback_src := DroidSansFallbackFull.ttf
-ifneq ($(EXTENDED_FONT_FOOTPRINT),true)
include $(CLEAR_VARS)
LOCAL_MODULE := MTLmr3m.ttf
LOCAL_SRC_FILES := $(LOCAL_MODULE)
@@ -65,7 +64,6 @@
LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts
include $(BUILD_PREBUILT)
extra_font_files += MTLmr3m.ttf
-endif # !EXTENDED_FONT_FOOTPRINT
endif # SMALLER_FONT_FOOTPRINT
diff --git a/docs/html/google/google_toc.cs b/docs/html/google/google_toc.cs
index b4028bd..b770135 100644
--- a/docs/html/google/google_toc.cs
+++ b/docs/html/google/google_toc.cs
@@ -192,6 +192,12 @@
</li>
<li class="nav-section">
+ <div class="nav-section-header empty"><a href="<?cs var:toroot ?>google/gcs/index.html">
+ <span class="en">Google Cloud Save</span></a>
+ </div>
+ </li>
+
+ <li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>google/play/dist.html">
<span class="en">Google Play Distribution</span></a>
</div>
diff --git a/docs/html/sdk/installing/studio-build.jd b/docs/html/sdk/installing/studio-build.jd
index 2a616a0..29ba12d 100644
--- a/docs/html/sdk/installing/studio-build.jd
+++ b/docs/html/sdk/installing/studio-build.jd
@@ -882,11 +882,11 @@
buildTypes { ... }
productFlavors {
demo {
- packageName "com.buildsystemexample.app.demo"
+ applicationId "com.buildsystemexample.app.demo"
versionName "1.0-demo"
}
full {
- packageName "com.buildsystemexample.app.full"
+ applicationId "com.buildsystemexample.app.full"
versionName "1.0-full"
}
}
@@ -896,8 +896,9 @@
<p>The product flavor definitions support the same properties as the <code>defaultConfig</code>
element. The base configuration for all flavors is specified in <code>defaultConfig</code>, and each
-flavor can override any value. The build file above assigns a different package name to each flavor:
-since each flavor definition creates a different app, they each need a distinct package name.</p>
+flavor can override any value. The build file above uses the <code>applicationId</code> property
+to assign a different package name to each flavor: since each flavor definition creates a
+different app, they each need a distinct package name.</p>
<p class="note"><strong>Note:</strong> To distribute your app using
<a href="{@docRoot}google/play/publishing/multiple-apks.html">Multiple APK Support</a> in
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index e080375..83a8ed5 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -31,7 +31,6 @@
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
-import android.graphics.drawable.ColorDrawable.ColorState;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.Shader;
@@ -712,9 +711,11 @@
final Resources r = a.getResources();
final BitmapState state = mBitmapState;
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
// Extract the theme attributes, if any.
- final int[] themeAttrs = a.extractThemeAttrs();
- state.mThemeAttrs = themeAttrs;
+ state.mThemeAttrs = a.extractThemeAttrs();
final int srcResId = a.getResourceId(R.styleable.BitmapDrawable_src, 0);
if (srcResId != 0) {
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index 3716182..4f050e0 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -45,7 +45,6 @@
@ViewDebug.ExportedProperty(deepExport = true, prefix = "state_")
private ColorState mColorState;
- private ColorStateList mTint;
private PorterDuffColorFilter mTintFilter;
private boolean mMutated;
@@ -191,7 +190,7 @@
@Override
public boolean isStateful() {
- return mTint != null && mTint.isStateful();
+ return mColorState.mTint != null && mColorState.mTint.isStateful();
}
@Override
@@ -215,60 +214,42 @@
super.inflate(r, parser, attrs, theme);
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ColorDrawable);
- inflateStateFromTypedArray(a);
+ updateStateFromTypedArray(a);
a.recycle();
}
/**
- * Initializes the constant state from the values in the typed array.
- */
- private void inflateStateFromTypedArray(TypedArray a) {
- final ColorState state = mColorState;
-
- // Extract the theme attributes, if any.
- final int[] themeAttrs = a.extractThemeAttrs();
- state.mThemeAttrs = themeAttrs;
-
- if (themeAttrs == null || themeAttrs[R.styleable.ColorDrawable_color] == 0) {
- final int color = a.getColor(R.styleable.ColorDrawable_color, 0);
- state.mBaseColor = color;
- state.mUseColor = color;
- }
- }
-
- @Override
- public void applyTheme(Theme t) {
- super.applyTheme(t);
-
- final ColorState state = mColorState;
- if (state == null) {
- throw new RuntimeException("Can't apply theme to <color> with no constant state");
- }
-
- final int[] themeAttrs = state.mThemeAttrs;
- if (themeAttrs != null) {
- final TypedArray a = t.resolveAttributes(themeAttrs, R.styleable.ColorDrawable);
- updateStateFromTypedArray(a);
- a.recycle();
- }
- }
-
- /**
* Updates the constant state from the values in the typed array.
*/
private void updateStateFromTypedArray(TypedArray a) {
final ColorState state = mColorState;
- if (a.hasValue(R.styleable.ColorDrawable_color)) {
- final int color = a.getColor(R.styleable.ColorDrawable_color, 0);
- state.mBaseColor = color;
- state.mUseColor = color;
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
+ state.mBaseColor = a.getColor(R.styleable.ColorDrawable_color, state.mBaseColor);
+ state.mUseColor = state.mBaseColor;
+ }
+
+ @Override
+ public void applyTheme(Theme t) {
+ super.applyTheme(t);
+
+ final ColorState state = mColorState;
+ if (state == null || state.mThemeAttrs == null) {
+ return;
}
+
+ final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ColorDrawable);
+ updateStateFromTypedArray(a);
+ a.recycle();
}
@Override
public ConstantState getConstantState() {
- mColorState.mChangingConfigurations = getChangingConfigurations();
return mColorState;
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index c2759ec..b050a02 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -238,9 +238,9 @@
* may change, requiring that it be re-created.
*
* @param configs A mask of the changing configuration parameters, as
- * defined by {@link android.content.res.Configuration}.
+ * defined by {@link android.content.pm.ActivityInfo}.
*
- * @see android.content.res.Configuration
+ * @see android.content.pm.ActivityInfo
*/
public void setChangingConfigurations(int configs) {
mChangingConfigurations = configs;
@@ -255,9 +255,9 @@
* drawables they hold.
*
* @return Returns a mask of the changing configuration parameters, as
- * defined by {@link android.content.res.Configuration}.
+ * defined by {@link android.content.pm.ActivityInfo}.
*
- * @see android.content.res.Configuration
+ * @see android.content.pm.ActivityInfo
*/
public int getChangingConfigurations() {
return mChangingConfigurations;
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 0ccce93..6e9b776 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -1011,6 +1011,9 @@
private void updateStateFromTypedArray(TypedArray a) {
final GradientState state = mGradientState;
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
@@ -1152,6 +1155,9 @@
private void updateGradientDrawablePadding(TypedArray a) {
final GradientState st = mGradientState;
+ // Account for any configuration changes.
+ st.mChangingConfigurations |= a.getChangingConfigurations();
+
// Extract the theme attributes, if any.
st.mAttrPadding = a.extractThemeAttrs();
@@ -1170,6 +1176,9 @@
private void updateDrawableCorners(TypedArray a) {
final GradientState st = mGradientState;
+ // Account for any configuration changes.
+ st.mChangingConfigurations |= a.getChangingConfigurations();
+
// Extract the theme attributes, if any.
st.mAttrCorners = a.extractThemeAttrs();
@@ -1201,6 +1210,10 @@
private void updateGradientDrawableStroke(TypedArray a) {
final GradientState st = mGradientState;
+ // Account for any configuration changes.
+ st.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
st.mAttrStroke = a.extractThemeAttrs();
// We have an explicit stroke defined, so the default stroke width
@@ -1227,7 +1240,13 @@
}
private void updateGradientDrawableSolid(TypedArray a) {
- mGradientState.mAttrSolid = a.extractThemeAttrs();
+ final GradientState st = mGradientState;
+
+ // Account for any configuration changes.
+ st.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ st.mAttrSolid = a.extractThemeAttrs();
final ColorStateList colorStateList = a.getColorStateList(
R.styleable.GradientDrawableSolid_color);
@@ -1240,6 +1259,9 @@
throws XmlPullParserException {
final GradientState st = mGradientState;
+ // Account for any configuration changes.
+ st.mChangingConfigurations |= a.getChangingConfigurations();
+
// Extract the theme attributes, if any.
st.mAttrGradient = a.extractThemeAttrs();
@@ -1351,6 +1373,9 @@
private void updateGradientDrawableSize(TypedArray a) {
final GradientState st = mGradientState;
+ // Account for any configuration changes.
+ st.mChangingConfigurations |= a.getChangingConfigurations();
+
// Extract the theme attributes, if any.
st.mAttrSize = a.extractThemeAttrs();
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index e91ffb1..d214a47 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -16,6 +16,8 @@
package android.graphics.drawable;
+import com.android.internal.R;
+
import android.annotation.NonNull;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -48,9 +50,11 @@
* @attr ref android.R.styleable#InsetDrawable_insetBottom
*/
public class InsetDrawable extends Drawable implements Drawable.Callback {
- // Most of this is copied from ScaleDrawable.
- private InsetState mInsetState;
+ private static final String LOG_TAG = "InsetDrawable";
+
private final Rect mTmpRect = new Rect();
+
+ private InsetState mInsetState;
private boolean mMutated;
/*package*/ InsetDrawable() {
@@ -79,59 +83,81 @@
@Override
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
- int type;
-
- TypedArray a = r.obtainAttributes(attrs,
- com.android.internal.R.styleable.InsetDrawable);
-
- super.inflateWithAttributes(r, parser, a,
- com.android.internal.R.styleable.InsetDrawable_visible);
-
- int drawableRes = a.getResourceId(com.android.internal.R.styleable.
- InsetDrawable_drawable, 0);
-
- int inLeft = a.getDimensionPixelOffset(com.android.internal.R.styleable.
- InsetDrawable_insetLeft, 0);
- int inTop = a.getDimensionPixelOffset(com.android.internal.R.styleable.
- InsetDrawable_insetTop, 0);
- int inRight = a.getDimensionPixelOffset(com.android.internal.R.styleable.
- InsetDrawable_insetRight, 0);
- int inBottom = a.getDimensionPixelOffset(com.android.internal.R.styleable.
- InsetDrawable_insetBottom, 0);
-
+ final TypedArray a = r.obtainAttributes(attrs, R.styleable.InsetDrawable);
+ super.inflateWithAttributes(r, parser, a, R.styleable.InsetDrawable_visible);
+ updateStateFromTypedArray(a);
a.recycle();
- Drawable dr;
- if (drawableRes != 0) {
- dr = r.getDrawable(drawableRes);
- } else {
+ // Load inner XML elements.
+ if (mInsetState.mDrawable == null) {
+ int type;
while ((type=parser.next()) == XmlPullParser.TEXT) {
}
if (type != XmlPullParser.START_TAG) {
throw new XmlPullParserException(
parser.getPositionDescription()
- + ": <inset> tag requires a 'drawable' attribute or "
- + "child tag defining a drawable");
+ + ": <inset> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
}
- dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
- }
-
- if (dr == null) {
- Log.w("drawable", "No drawable specified for <inset>");
- }
-
- mInsetState.mDrawable = dr;
- mInsetState.mInsetLeft = inLeft;
- mInsetState.mInsetRight = inRight;
- mInsetState.mInsetTop = inTop;
- mInsetState.mInsetBottom = inBottom;
-
- if (dr != null) {
+ final Drawable dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
+ mInsetState.mDrawable = dr;
dr.setCallback(this);
}
+
+ // Verify state.
+ if (mInsetState.mDrawable == null) {
+ Log.w(LOG_TAG, "No drawable specified for <inset>");
+ }
}
- // overrides from Drawable.Callback
+ private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
+ final InsetState state = mInsetState;
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
+ final Drawable dr = a.getDrawable(R.styleable.InsetDrawable_drawable);
+ if (dr != null) {
+ state.mDrawable = dr;
+ dr.setCallback(this);
+ }
+
+ state.mInsetLeft = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetLeft, state.mInsetLeft);
+ state.mInsetTop = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetTop, state.mInsetTop);
+ state.mInsetRight = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetRight, state.mInsetRight);
+ state.mInsetBottom = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetBottom, state.mInsetBottom);
+ }
+
+ @Override
+ public void applyTheme(Theme t) {
+ super.applyTheme(t);
+
+ final InsetState state = mInsetState;
+ if (state == null || state.mThemeAttrs == null) {
+ return;
+ }
+
+ final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.InsetDrawable);
+ try {
+ updateStateFromTypedArray(a);
+ } catch (XmlPullParserException e) {
+ throw new RuntimeException(e);
+ } finally {
+ a.recycle();
+ }
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return mInsetState != null && mInsetState.mThemeAttrs != null;
+ }
@Override
public void invalidateDrawable(Drawable who) {
@@ -157,8 +183,6 @@
}
}
- // overrides from Drawable
-
@Override
public void draw(Canvas canvas) {
mInsetState.mDrawable.draw(canvas);
@@ -316,9 +340,11 @@
}
final static class InsetState extends ConstantState {
- Drawable mDrawable;
+ int[] mThemeAttrs;
int mChangingConfigurations;
+ Drawable mDrawable;
+
int mInsetLeft;
int mInsetTop;
int mInsetRight;
@@ -329,6 +355,8 @@
InsetState(InsetState orig, InsetDrawable owner, Resources res) {
if (orig != null) {
+ mThemeAttrs = orig.mThemeAttrs;
+ mChangingConfigurations = orig.mChangingConfigurations;
if (res != null) {
mDrawable = orig.mDrawable.getConstantState().newDrawable(res);
} else {
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 2f22392..fa68bc5 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -155,9 +155,11 @@
private void updateStateFromTypedArray(TypedArray a) {
final LayerState state = mLayerState;
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
// Extract the theme attributes, if any.
- final int[] themeAttrs = a.extractThemeAttrs();
- state.mThemeAttrs = themeAttrs;
+ state.mThemeAttrs = a.extractThemeAttrs();
mOpacityOverride = a.getInt(R.styleable.LayerDrawable_opacity, mOpacityOverride);
@@ -172,7 +174,8 @@
*/
private void inflateLayers(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
- TypedArray a;
+ final LayerState state = mLayerState;
+
final int innerDepth = parser.getDepth() + 1;
int type;
int depth;
@@ -186,28 +189,12 @@
continue;
}
- a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawableItem);
-
- final int[] themeAttrs = a.extractThemeAttrs();
- final int left = a.getDimensionPixelOffset(
- R.styleable.LayerDrawableItem_left, 0);
- final int top = a.getDimensionPixelOffset(
- R.styleable.LayerDrawableItem_top, 0);
- final int right = a.getDimensionPixelOffset(
- R.styleable.LayerDrawableItem_right, 0);
- final int bottom = a.getDimensionPixelOffset(
- R.styleable.LayerDrawableItem_bottom, 0);
- final int drawableRes = a.getResourceId(
- R.styleable.LayerDrawableItem_drawable, 0);
- final int id = a.getResourceId(
- R.styleable.LayerDrawableItem_id, View.NO_ID);
-
+ final ChildDrawable layer = new ChildDrawable();
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawableItem);
+ updateLayerFromTypedArray(layer, a);
a.recycle();
- final Drawable dr;
- if (drawableRes != 0) {
- dr = r.getDrawable(drawableRes, theme);
- } else {
+ if (layer.mDrawable == null) {
while ((type = parser.next()) == XmlPullParser.TEXT) {
}
if (type != XmlPullParser.START_TAG) {
@@ -215,10 +202,41 @@
+ ": <item> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
- dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
+ layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme);
}
- addLayer(dr, themeAttrs, id, left, top, right, bottom);
+ if (layer.mDrawable != null) {
+ state.mChildrenChangingConfigurations |=
+ layer.mDrawable.getChangingConfigurations();
+ layer.mDrawable.setCallback(this);
+ }
+
+ addLayer(layer);
+ }
+ }
+
+ private void updateLayerFromTypedArray(ChildDrawable layer, TypedArray a) {
+ final LayerState state = mLayerState;
+
+ // Account for any configuration changes.
+ state.mChildrenChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ layer.mThemeAttrs = a.extractThemeAttrs();
+
+ layer.mInsetL = a.getDimensionPixelOffset(
+ R.styleable.LayerDrawableItem_left, layer.mInsetL);
+ layer.mInsetT = a.getDimensionPixelOffset(
+ R.styleable.LayerDrawableItem_top, layer.mInsetT);
+ layer.mInsetR = a.getDimensionPixelOffset(
+ R.styleable.LayerDrawableItem_right, layer.mInsetR);
+ layer.mInsetB = a.getDimensionPixelOffset(
+ R.styleable.LayerDrawableItem_bottom, layer.mInsetB);
+ layer.mId = a.getResourceId(R.styleable.LayerDrawableItem_id, layer.mId);
+
+ final Drawable dr = a.getDrawable(R.styleable.LayerDrawableItem_drawable);
+ if (dr != null) {
+ layer.mDrawable = dr;
}
}
@@ -231,19 +249,23 @@
return;
}
- final int[] themeAttrs = state.mThemeAttrs;
- if (themeAttrs != null) {
- final TypedArray a = t.resolveAttributes(themeAttrs, R.styleable.LayerDrawable);
+ if (state.mThemeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.LayerDrawable);
updateStateFromTypedArray(a);
a.recycle();
}
- // TODO: Update layer positions from cached typed arrays.
-
final ChildDrawable[] array = mLayerState.mChildren;
final int N = mLayerState.mNum;
for (int i = 0; i < N; i++) {
final ChildDrawable layer = array[i];
+ if (layer.mThemeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(layer.mThemeAttrs,
+ R.styleable.LayerDrawableItem);
+ updateLayerFromTypedArray(layer, a);
+ a.recycle();
+ }
+
final Drawable d = layer.mDrawable;
if (d.canApplyTheme()) {
d.applyTheme(t);
@@ -297,19 +319,7 @@
return false;
}
- /**
- * Add a new layer to this drawable. The new layer is identified by an id.
- *
- * @param layer The drawable to add as a layer.
- * @param themeAttrs Theme attributes extracted from the layer.
- * @param id The id of the new layer.
- * @param left The left padding of the new layer.
- * @param top The top padding of the new layer.
- * @param right The right padding of the new layer.
- * @param bottom The bottom padding of the new layer.
- */
- void addLayer(Drawable layer, int[] themeAttrs, int id, int left, int top, int right,
- int bottom) {
+ void addLayer(ChildDrawable layer) {
final LayerState st = mLayerState;
final int N = st.mChildren != null ? st.mChildren.length : 0;
final int i = st.mNum;
@@ -322,10 +332,25 @@
st.mChildren = nu;
}
- mLayerState.mChildrenChangingConfigurations |= layer.getChangingConfigurations();
+ st.mChildren[i] = layer;
+ st.mNum++;
+ st.invalidateCache();
+ }
+ /**
+ * Add a new layer to this drawable. The new layer is identified by an id.
+ *
+ * @param layer The drawable to add as a layer.
+ * @param themeAttrs Theme attributes extracted from the layer.
+ * @param id The id of the new layer.
+ * @param left The left padding of the new layer.
+ * @param top The top padding of the new layer.
+ * @param right The right padding of the new layer.
+ * @param bottom The bottom padding of the new layer.
+ */
+ ChildDrawable addLayer(Drawable layer, int[] themeAttrs, int id, int left, int top, int right,
+ int bottom) {
final ChildDrawable childDrawable = new ChildDrawable();
- st.mChildren[i] = childDrawable;
childDrawable.mId = id;
childDrawable.mThemeAttrs = themeAttrs;
childDrawable.mDrawable = layer;
@@ -334,10 +359,13 @@
childDrawable.mInsetT = top;
childDrawable.mInsetR = right;
childDrawable.mInsetB = bottom;
- st.mNum++;
- st.invalidateCache();
+ addLayer(childDrawable);
+
+ mLayerState.mChildrenChangingConfigurations |= layer.getChangingConfigurations();
layer.setCallback(this);
+
+ return childDrawable;
}
/**
@@ -566,13 +594,24 @@
}
/**
- * Builds an Outline from the first child Drawable, if present.
+ * Populates <code>outline</code> with the first available layer outline.
+ * Returns <code>true</code> if an outline is available, <code>false</code>
+ * otherwise.
+ *
+ * @param outline Outline in which to place the first available layer outline
+ * @return <code>true</code> if an outline is available
*/
@Override
public boolean getOutline(@NonNull Outline outline) {
- if (mLayerState.mNum < 1) return false;
- final Drawable firstChild = mLayerState.mChildren[0].mDrawable;
- return firstChild.getOutline(outline);
+ final LayerState state = mLayerState;
+ final ChildDrawable[] children = state.mChildren;
+ final int N = state.mNum;
+ for (int i = 0; i < N; i++) {
+ if (children[i].mDrawable.getOutline(outline)) {
+ return true;
+ }
+ }
+ return false;
}
@Override
@@ -893,7 +932,7 @@
public Drawable mDrawable;
public int[] mThemeAttrs;
public int mInsetL, mInsetT, mInsetR, mInsetB;
- public int mId;
+ public int mId = View.NO_ID;
ChildDrawable() {
// Default empty constructor.
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 6642bdd..24bbf7c 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -382,9 +382,11 @@
final Resources r = a.getResources();
final NinePatchState state = mNinePatchState;
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
// Extract the theme attributes, if any.
- final int[] themeAttrs = a.extractThemeAttrs();
- state.mThemeAttrs = themeAttrs;
+ state.mThemeAttrs = a.extractThemeAttrs();
state.mDither = a.getBoolean(R.styleable.NinePatchDrawable_dither, state.mDither);
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index be8df59..87f5989 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -25,6 +25,7 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
+import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff.Mode;
@@ -357,6 +358,9 @@
private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
final RippleState state = mState;
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
// Extract the theme attributes, if any.
state.mTouchThemeAttrs = a.extractThemeAttrs();
@@ -497,8 +501,29 @@
}
}
+ /**
+ * Populates <code>outline</code> with the first available layer outline,
+ * excluding the mask layer. Returns <code>true</code> if an outline is
+ * available, <code>false</code> otherwise.
+ *
+ * @param outline Outline in which to place the first available layer outline
+ * @return <code>true</code> if an outline is available
+ */
@Override
- public void draw(Canvas canvas) {
+ public boolean getOutline(@NonNull Outline outline) {
+ final LayerState state = mLayerState;
+ final ChildDrawable[] children = state.mChildren;
+ final int N = state.mNum;
+ for (int i = 0; i < N; i++) {
+ if (children[i].mId != R.id.mask && children[i].mDrawable.getOutline(outline)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void draw(@NonNull Canvas canvas) {
final boolean isProjected = isProjected();
final boolean hasMask = mMask != null;
final boolean drawNonMaskContent = mLayerState.mNum > (hasMask ? 1 : 0);
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index 86765dd..9802529 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -422,6 +422,9 @@
final ShapeState state = mShapeState;
final Paint paint = state.mPaint;
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index b37204d..150f210 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -44,7 +44,6 @@
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Stack;
/**
@@ -308,6 +307,9 @@
private void updateStateFromTypedArray(TypedArray a) {
final VectorDrawableState state = mVectorState;
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
@@ -322,8 +324,9 @@
}
}
- private VPathRenderer inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
- throws XmlPullParserException, IOException {
+ private VPathRenderer inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs,
+ Theme theme) throws XmlPullParserException, IOException {
+ final VectorDrawableState state = mVectorState;
final VPathRenderer pathRenderer = new VPathRenderer();
boolean noSizeTag = true;
@@ -349,12 +352,15 @@
mVGTargetsMap.put(path.getPathName(), path);
}
noPathTag = false;
+ state.mChangingConfigurations |= path.mChangingConfigurations;
} else if (SHAPE_SIZE.equals(tagName)) {
pathRenderer.parseSize(res, attrs);
noSizeTag = false;
+ state.mChangingConfigurations |= pathRenderer.mChangingConfigurations;
} else if (SHAPE_VIEWPORT.equals(tagName)) {
pathRenderer.parseViewport(res, attrs);
noViewportTag = false;
+ state.mChangingConfigurations |= pathRenderer.mChangingConfigurations;
} else if (SHAPE_GROUP.equals(tagName)) {
VGroup newChildGroup = new VGroup();
newChildGroup.inflate(res, attrs, theme);
@@ -363,6 +369,7 @@
if (newChildGroup.getGroupName() != null) {
mVGTargetsMap.put(newChildGroup.getGroupName(), newChildGroup);
}
+ state.mChangingConfigurations |= newChildGroup.mChangingConfigurations;
}
} else if (eventType == XmlPullParser.END_TAG) {
final String tagName = parser.getName();
@@ -483,6 +490,8 @@
private ColorFilter mColorFilter;
private PathMeasure mPathMeasure;
+ private int mChangingConfigurations;
+
private float mBaseWidth = 0;
private float mBaseHeight = 0;
private float mViewportWidth = 0;
@@ -509,6 +518,7 @@
mBaseHeight = copy.mBaseHeight;
mViewportWidth = copy.mViewportHeight;
mViewportHeight = copy.mViewportHeight;
+ mChangingConfigurations = copy.mChangingConfigurations;
}
public boolean canApplyTheme() {
@@ -609,7 +619,8 @@
mFinalPathMatrix.set(vGroup.mStackedMatrix);
mFinalPathMatrix.postScale(scale, scale, mViewportWidth / 2f, mViewportHeight / 2f);
- mFinalPathMatrix.postTranslate(w / 2f - mViewportWidth / 2f, h / 2f - mViewportHeight / 2f);
+ mFinalPathMatrix.postTranslate(
+ w / 2f - mViewportWidth / 2f, h / 2f - mViewportHeight / 2f);
ArrayList<VPath> paths = vGroup.getPaths();
for (int i = 0; i < paths.size(); i++) {
@@ -687,8 +698,14 @@
private void parseViewport(Resources r, AttributeSet attrs)
throws XmlPullParserException {
final TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableViewport);
- mViewportWidth = a.getFloat(R.styleable.VectorDrawableViewport_viewportWidth, mViewportWidth);
- mViewportHeight = a.getFloat(R.styleable.VectorDrawableViewport_viewportHeight, mViewportHeight);
+
+ // Account for any configuration changes.
+ mChangingConfigurations |= a.getChangingConfigurations();
+
+ mViewportWidth = a.getFloat(
+ R.styleable.VectorDrawableViewport_viewportWidth, mViewportWidth);
+ mViewportHeight = a.getFloat(
+ R.styleable.VectorDrawableViewport_viewportHeight, mViewportHeight);
if (mViewportWidth <= 0) {
throw new XmlPullParserException(a.getPositionDescription() +
@@ -704,6 +721,10 @@
private void parseSize(Resources r, AttributeSet attrs)
throws XmlPullParserException {
final TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableSize);
+
+ // Account for any configuration changes.
+ mChangingConfigurations |= a.getChangingConfigurations();
+
mBaseWidth = a.getDimension(R.styleable.VectorDrawableSize_width, mBaseWidth);
mBaseHeight = a.getDimension(R.styleable.VectorDrawableSize_height, mBaseHeight);
@@ -739,6 +760,7 @@
// parents' local matrices with the current one.
private final Matrix mStackedMatrix = new Matrix();
+ private int mChangingConfigurations;
private int[] mThemeAttrs;
private String mGroupName = null;
@@ -847,13 +869,19 @@
return mThemeAttrs != null;
}
- public void applyTheme(Theme t) {
- if (mThemeAttrs == null) {
- return;
- }
+ public void inflate(Resources res, AttributeSet attrs, Theme theme) {
+ final TypedArray a = obtainAttributes(res, theme, attrs,
+ R.styleable.VectorDrawableGroup);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
- final TypedArray a = t.resolveAttributes(
- mThemeAttrs, R.styleable.VectorDrawablePath);
+ private void updateStateFromTypedArray(TypedArray a) {
+ // Account for any configuration changes.
+ mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ mThemeAttrs = a.extractThemeAttrs();
mRotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation, mRotate);
mPivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX, mPivotX);
@@ -863,58 +891,22 @@
mTranslateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX, mTranslateX);
mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY);
mGroupAlpha = a.getFloat(R.styleable.VectorDrawableGroup_alpha, mGroupAlpha);
- updateLocalMatrix();
- if (a.hasValue(R.styleable.VectorDrawableGroup_name)) {
- mGroupName = a.getString(R.styleable.VectorDrawableGroup_name);
+
+ final String groupName = a.getString(R.styleable.VectorDrawableGroup_name);
+ if (groupName != null) {
+ mGroupName = groupName;
}
- a.recycle();
+
+ updateLocalMatrix();
}
- public void inflate(Resources res, AttributeSet attrs, Theme theme) {
- final TypedArray a = obtainAttributes(res, theme, attrs, R.styleable.VectorDrawableGroup);
- final int[] themeAttrs = a.extractThemeAttrs();
-
- mThemeAttrs = themeAttrs;
- // NOTE: The set of attributes loaded here MUST match the
- // set of attributes loaded in applyTheme.
-
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_rotation] == 0) {
- mRotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation, mRotate);
+ public void applyTheme(Theme t) {
+ if (mThemeAttrs == null) {
+ return;
}
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_pivotX] == 0) {
- mPivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX, mPivotX);
- }
-
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_pivotY] == 0) {
- mPivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY, mPivotY);
- }
-
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_scaleX] == 0) {
- mScaleX = a.getFloat(R.styleable.VectorDrawableGroup_scaleX, mScaleX);
- }
-
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_scaleY] == 0) {
- mScaleY = a.getFloat(R.styleable.VectorDrawableGroup_scaleY, mScaleY);
- }
-
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_translateX] == 0) {
- mTranslateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX, mTranslateX);
- }
-
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_translateY] == 0) {
- mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY);
- }
-
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_name] == 0) {
- mGroupName = a.getString(R.styleable.VectorDrawableGroup_name);
- }
-
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_alpha] == 0) {
- mGroupAlpha = a.getFloat(R.styleable.VectorDrawableGroup_alpha, mGroupAlpha);
- }
-
- updateLocalMatrix();
+ final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.VectorDrawablePath);
+ updateStateFromTypedArray(a);
a.recycle();
}
@@ -939,6 +931,7 @@
}
private static class VPath {
+ private int mChangingConfigurations;
private int[] mThemeAttrs;
int mStrokeColor = 0;
@@ -1096,113 +1089,29 @@
mTrimPathOffset = trimPathOffset;
}
- public void inflate(Resources r, AttributeSet attrs, Theme theme) {
- final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawablePath);
- final int[] themeAttrs = a.extractThemeAttrs();
- mThemeAttrs = themeAttrs;
-
- // NOTE: The set of attributes loaded here MUST match the
- // set of attributes loaded in applyTheme.
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_clipToPath] == 0) {
- mClip = a.getBoolean(R.styleable.VectorDrawablePath_clipToPath, mClip);
- }
-
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_name] == 0) {
- mPathName = a.getString(R.styleable.VectorDrawablePath_name);
- }
-
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_pathData] == 0) {
- mNode = PathParser.createNodesFromPathData(a.getString(
- R.styleable.VectorDrawablePath_pathData));
- }
-
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_fill] == 0) {
- mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, mFillColor);
- }
-
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_fillOpacity] == 0) {
- mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, mFillOpacity);
- }
-
- if (themeAttrs == null
- || themeAttrs[R.styleable.VectorDrawablePath_strokeLineCap] == 0) {
- mStrokeLineCap = getStrokeLineCap(
- a.getInt(R.styleable.VectorDrawablePath_strokeLineCap, -1), mStrokeLineCap);
- }
-
- if (themeAttrs == null
- || themeAttrs[R.styleable.VectorDrawablePath_strokeLineJoin] == 0) {
- mStrokeLineJoin = getStrokeLineJoin(
- a.getInt(R.styleable.VectorDrawablePath_strokeLineJoin, -1), mStrokeLineJoin);
- }
-
- if (themeAttrs == null
- || themeAttrs[R.styleable.VectorDrawablePath_strokeMiterLimit] == 0) {
- mStrokeMiterlimit = a.getFloat(
- R.styleable.VectorDrawablePath_strokeMiterLimit, mStrokeMiterlimit);
- }
-
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_stroke] == 0) {
- mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_stroke, mStrokeColor);
- }
-
- if (themeAttrs == null
- || themeAttrs[R.styleable.VectorDrawablePath_strokeOpacity] == 0) {
- mStrokeOpacity = a.getFloat(
- R.styleable.VectorDrawablePath_strokeOpacity, mStrokeOpacity);
- }
-
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_strokeWidth] == 0) {
- mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth, mStrokeWidth);
- }
-
- if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_trimPathEnd] == 0) {
- mTrimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd, mTrimPathEnd);
- }
-
- if (themeAttrs == null
- || themeAttrs[R.styleable.VectorDrawablePath_trimPathOffset] == 0) {
- mTrimPathOffset = a.getFloat(
- R.styleable.VectorDrawablePath_trimPathOffset, mTrimPathOffset);
- }
-
- if (themeAttrs == null
- || themeAttrs[R.styleable.VectorDrawablePath_trimPathStart] == 0) {
- mTrimPathStart = a.getFloat(
- R.styleable.VectorDrawablePath_trimPathStart, mTrimPathStart);
- }
-
- updateColorAlphas();
-
- a.recycle();
- }
-
public boolean canApplyTheme() {
return mThemeAttrs != null;
}
- public void applyTheme(Theme t) {
- if (mThemeAttrs == null) {
- return;
- }
+ public void inflate(Resources r, AttributeSet attrs, Theme theme) {
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawablePath);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
- final TypedArray a = t.resolveAttributes(
- mThemeAttrs, R.styleable.VectorDrawablePath);
+ private void updateStateFromTypedArray(TypedArray a) {
+ // Account for any configuration changes.
+ mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ mThemeAttrs = a.extractThemeAttrs();
mClip = a.getBoolean(R.styleable.VectorDrawablePath_clipToPath, mClip);
-
- if (a.hasValue(R.styleable.VectorDrawablePath_name)) {
- mPathName = a.getString(R.styleable.VectorDrawablePath_name);
- }
-
- if (a.hasValue(R.styleable.VectorDrawablePath_pathData)) {
- mNode = PathParser.createNodesFromPathData(a.getString(
- R.styleable.VectorDrawablePath_pathData));
- }
-
+ mPathName = a.getString(R.styleable.VectorDrawablePath_name);
+ mNode = PathParser.createNodesFromPathData(a.getString(
+ R.styleable.VectorDrawablePath_pathData));
mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, mFillColor);
mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, mFillOpacity);
-
mStrokeLineCap = getStrokeLineCap(a.getInt(
R.styleable.VectorDrawablePath_strokeLineCap, -1), mStrokeLineCap);
mStrokeLineJoin = getStrokeLineJoin(a.getInt(
@@ -1213,7 +1122,6 @@
mStrokeOpacity = a.getFloat(
R.styleable.VectorDrawablePath_strokeOpacity, mStrokeOpacity);
mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth, mStrokeWidth);
-
mTrimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd, mTrimPathEnd);
mTrimPathOffset = a.getFloat(
R.styleable.VectorDrawablePath_trimPathOffset, mTrimPathOffset);
@@ -1221,6 +1129,15 @@
R.styleable.VectorDrawablePath_trimPathStart, mTrimPathStart);
updateColorAlphas();
+ }
+
+ public void applyTheme(Theme t) {
+ if (mThemeAttrs == null) {
+ return;
+ }
+
+ final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.VectorDrawablePath);
+ updateStateFromTypedArray(a);
a.recycle();
}
diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h
index 5e7f899..024bdfd 100644
--- a/libs/hwui/Outline.h
+++ b/libs/hwui/Outline.h
@@ -30,7 +30,7 @@
, mType(kOutlineType_None)
, mRadius(0) {}
- void setRoundRect(int left, int top, int right, int bottom, int radius) {
+ void setRoundRect(int left, int top, int right, int bottom, float radius) {
mType = kOutlineType_RoundRect;
mBounds.set(left, top, right, bottom);
mRadius = radius;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 89105ea..3cf625f 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -211,7 +211,8 @@
// This will also release the hardware layer if we have one as
// isRenderable() will return false, thus causing pushLayerUpdate
// to recycle the hardware layer
- setStagingDisplayList(NULL);
+ LOG_ALWAYS_FATAL_IF(mStagingDisplayListData || (mDisplayListData && !mNeedsDisplayListDataSync),
+ "View.destroyHardwareResources wasn't called!");
break;
}
@@ -260,7 +261,11 @@
mNeedsDisplayListDataSync = false;
// Do a push pass on the old tree to handle freeing DisplayListData
// that are no longer used
- TreeInfo oldTreeInfo(TreeInfo::MODE_MAYBE_DETACHING, info);
+ TreeInfo::TraversalMode mode = TreeInfo::MODE_MAYBE_DETACHING;
+ if (CC_UNLIKELY(info.mode == TreeInfo::MODE_DESTROY_RESOURCES)) {
+ mode = TreeInfo::MODE_DESTROY_RESOURCES;
+ }
+ TreeInfo oldTreeInfo(mode, info);
prepareSubTree(oldTreeInfo, mDisplayListData);
delete mDisplayListData;
mDisplayListData = mStagingDisplayListData;
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 153ee31..f262390 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -109,12 +109,12 @@
private static final String TAG = "AudioService";
/** Debug remote control client/display feature */
- protected static final boolean DEBUG_RC = false;
+ protected static final boolean DEBUG_RC = Log.isLoggable(TAG + ".RC", Log.DEBUG);
/** Debug volumes */
- protected static final boolean DEBUG_VOL = false;
+ protected static final boolean DEBUG_VOL = Log.isLoggable(TAG + ".VOL", Log.DEBUG);
/** debug calls to media session apis */
- private static final boolean DEBUG_SESSIONS = true;
+ private static final boolean DEBUG_SESSIONS = Log.isLoggable(TAG + ".SESSIONS", Log.DEBUG);
/** Allow volume changes to set ringer mode to silent? */
private static final boolean VOLUME_SETS_RINGER_MODE_SILENT = false;
@@ -841,7 +841,8 @@
/** @see AudioManager#adjustVolume(int, int) */
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage) {
- if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType);
+ if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType
+ + ", flags=" + flags);
int streamType;
if (mVolumeControlStream != -1) {
streamType = mVolumeControlStream;
@@ -871,7 +872,8 @@
if (mUseFixedVolume) {
return;
}
- if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream="+streamType+", dir="+direction);
+ if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream="+streamType+", dir="+direction
+ + ", flags="+flags);
ensureValidDirection(direction);
ensureValidStreamType(streamType);
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index 011da35..2854007 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -18,6 +18,7 @@
import android.content.ComponentName;
import android.media.tv.ITvInputSession;
+import android.net.Uri;
import android.os.Bundle;
import android.view.InputChannel;
@@ -34,4 +35,5 @@
void onVideoStreamChanged(int width, int height, boolean interlaced, int seq);
void onAudioStreamChanged(int channelCount, int seq);
void onClosedCaptionStreamChanged(boolean hasClosedCaption, int seq);
+ void onChannelRetuned(in Uri channelUri, int seq);
}
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 00f2922..5a57ccd 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -17,6 +17,7 @@
package android.media.tv;
import android.media.tv.ITvInputSession;
+import android.net.Uri;
import android.os.Bundle;
/**
@@ -30,4 +31,5 @@
void onVideoStreamChanged(int width, int height, boolean interlaced);
void onAudioStreamChanged(int channelCount);
void onClosedCaptionStreamChanged(boolean hasClosedCaption);
+ void onChannelRetuned(in Uri channelUri);
}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index daa7009..834ce64 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -125,6 +125,16 @@
}
/**
+ * This is called when the channel of this session is changed by the underlying TV input
+ * with out any {@link TvInputManager.Session#tune(Uri)} request.
+ *
+ * @param session A {@link TvInputManager.Session} associated with this callback
+ * @param channelUri The URI of a channel.
+ */
+ public void onChannelRetuned(Session session, Uri channelUri) {
+ }
+
+ /**
* This is called when a custom event has been sent from this session.
*
* @param session A {@link TvInputManager.Session} associated with this callback
@@ -194,6 +204,15 @@
});
}
+ public void postChannelRetuned(final Uri channelUri) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onChannelRetuned(mSession, channelUri);
+ }
+ });
+ }
+
public void postSessionEvent(final String eventType, final Bundle eventArgs) {
mHandler.post(new Runnable() {
@Override
@@ -318,6 +337,18 @@
}
@Override
+ public void onChannelRetuned(Uri channelUri, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postChannelRetuned(channelUri);
+ }
+ }
+
+ @Override
public void onSessionEvent(String eventType, Bundle eventArgs, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 0f4a930..1e512cd 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -286,6 +286,25 @@
}
/**
+ * Notifies the channel of the session is retuned by TV input.
+ *
+ * @param channelUri The URI of a channel.
+ */
+ public void dispatchChannelRetuned(final Uri channelUri) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "dispatchChannelRetuned");
+ mSessionCallback.onChannelRetuned(channelUri);
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in dispatchChannelRetuned");
+ }
+ }
+ });
+ }
+
+ /**
* Called when the session is released.
*/
public abstract void onRelease();
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index a913e59c..664a215 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -456,6 +456,16 @@
}
/**
+ * This is invoked when the channel of this TvView is changed by the underlying TV input
+ * with out any {@link TvView#tune(String, Uri)} request.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param channelUri The URI of a channel.
+ */
+ public void onChannelRetuned(String inputId, Uri channelUri) {
+ }
+
+ /**
* This is invoked when a custom event from the bound TV input is sent to this view.
*
* @param eventType The type of the event.
@@ -562,6 +572,16 @@
}
@Override
+ public void onChannelRetuned(Session session, Uri channelUri) {
+ if (DEBUG) {
+ Log.d(TAG, "onChannelChangedByTvInput(" + channelUri + ")");
+ }
+ if (mListener != null) {
+ mListener.onChannelRetuned(mInputId, channelUri);
+ }
+ }
+
+ @Override
public void onSessionEvent(TvInputManager.Session session, String eventType,
Bundle eventArgs) {
if (mListener != null) {
diff --git a/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp b/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp
index 7537e35..de6294d 100644
--- a/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp
+++ b/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp
@@ -52,11 +52,11 @@
int testDecodeBounds(SkImageDecoder* decoder, SkStream* stream,
SkBitmap* bitmap) {
int64_t startTime = getNowUs();
- SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
+ SkColorType prefColorType = kN32_SkColorType;
SkImageDecoder::Mode decodeMode = SkImageDecoder::kDecodeBounds_Mode;
// Decode the input stream and then use the bitmap.
- if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
+ if (!decoder->decode(stream, bitmap, prefColorType, decodeMode)) {
return nullObjectReturn("decoder->decode returned false");
} else {
int64_t delay = getNowUs() - startTime;
@@ -69,11 +69,11 @@
int testDecodePixels(SkImageDecoder* decoder, SkStream* stream,
SkBitmap* bitmap) {
int64_t startTime = getNowUs();
- SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
+ SkColorType prefColorType = kN32_SkColorType;
SkImageDecoder::Mode decodeMode = SkImageDecoder::kDecodePixels_Mode;
// Decode the input stream and then use the bitmap.
- if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
+ if (!decoder->decode(stream, bitmap, prefColorType, decodeMode)) {
return nullObjectReturn("decoder->decode returned false");
} else {
int64_t delay = getNowUs() - startTime;
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 7a21b92..67ed97c 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -499,19 +499,26 @@
private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target)
throws IOException, RemoteException {
- // TODO: extend to support copying all split APKs
+ copyFile(pkg.baseCodePath, "base.apk", target);
if (!ArrayUtils.isEmpty(pkg.splitNames)) {
- throw new UnsupportedOperationException("Copying split APKs not yet supported");
+ for (int i = 0; i < pkg.splitNames.length; i++) {
+ copyFile(pkg.splitCodePaths[i], "split_" + pkg.splitNames[i] + ".apk", target);
+ }
}
+ return PackageManager.INSTALL_SUCCEEDED;
+ }
+
+ private void copyFile(String sourcePath, String targetName,
+ IParcelFileDescriptorFactory target) throws IOException, RemoteException {
+ Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
InputStream in = null;
OutputStream out = null;
try {
- in = new FileInputStream(pkg.baseCodePath);
+ in = new FileInputStream(sourcePath);
out = new ParcelFileDescriptor.AutoCloseOutputStream(
- target.open(null, ParcelFileDescriptor.MODE_READ_WRITE));
+ target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));
Streams.copy(in, out);
- return PackageManager.INSTALL_SUCCEEDED;
} finally {
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(in);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
index 737e49b..276f795 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
@@ -67,7 +67,7 @@
// Whether the volume keys should be handled by keyguard. If true, then
// they will be handled here for specific media types such as music, otherwise
// the audio service will bring up the volume dialog.
- private static final boolean KEYGUARD_MANAGES_VOLUME = true;
+ private static final boolean KEYGUARD_MANAGES_VOLUME = false;
public static final boolean DEBUG = KeyguardConstants.DEBUG;
private static final String TAG = "KeyguardViewBase";
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 158227f..e6c6f9f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -18,7 +18,9 @@
import java.io.FileNotFoundException;
import java.security.SecureRandom;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -112,6 +114,9 @@
static final HashSet<String> sSecureGlobalKeys;
static final HashSet<String> sSystemGlobalKeys;
+ // Settings that cannot be modified if associated user restrictions are enabled.
+ static final Map<String, String> sRestrictedKeys;
+
private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid";
static {
@@ -125,6 +130,18 @@
// These must match Settings.System.MOVED_TO_GLOBAL
sSystemGlobalKeys = new HashSet<String>();
Settings.System.getNonLegacyMovedKeys(sSystemGlobalKeys);
+
+ sRestrictedKeys = new HashMap<String, String>();
+ sRestrictedKeys.put(Settings.Secure.LOCATION_MODE, UserManager.DISALLOW_SHARE_LOCATION);
+ sRestrictedKeys.put(Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ UserManager.DISALLOW_SHARE_LOCATION);
+ sRestrictedKeys.put(Settings.Global.INSTALL_NON_MARKET_APPS,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
+ sRestrictedKeys.put(Settings.Global.ADB_ENABLED, UserManager.DISALLOW_DEBUGGING_FEATURES);
+ sRestrictedKeys.put(Settings.Global.PACKAGE_VERIFIER_ENABLE,
+ UserManager.ENSURE_VERIFY_APPS);
+ sRestrictedKeys.put(Settings.Global.PREFERRED_NETWORK_MODE,
+ UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
}
private boolean settingMovedToGlobal(final String name) {
@@ -285,6 +302,15 @@
}
}
+ private void checkUserRestrictions(String setting) {
+ String userRestriction = sRestrictedKeys.get(setting);
+ if (!TextUtils.isEmpty(userRestriction)
+ && mUserManager.hasUserRestriction(userRestriction)) {
+ throw new SecurityException(
+ "Permission denial: user is restricted from changing this setting.");
+ }
+ }
+
// FileObserver for external modifications to the database file.
// Note that this is for platform developers only with
// userdebug/eng builds who should be able to tinker with the
@@ -783,6 +809,7 @@
try {
int numValues = values.length;
for (int i = 0; i < numValues; i++) {
+ checkUserRestrictions(values[i].getAsString(Settings.Secure.NAME));
if (db.insert(args.table, null, values[i]) < 0) return 0;
SettingsCache.populate(cache, values[i]);
if (LOCAL_LOGV) Log.v(TAG, args.table + " <- " + values[i]);
@@ -913,6 +940,8 @@
// Check write permissions only after determining which table the insert will touch
checkWritePermissions(args);
+ checkUserRestrictions(name);
+
// The global table is stored under the owner, always
if (TABLE_GLOBAL.equals(args.table)) {
desiredUserHandle = UserHandle.USER_OWNER;
@@ -987,6 +1016,7 @@
callingUser = UserHandle.USER_OWNER;
}
checkWritePermissions(args);
+ checkUserRestrictions(initialValues.getAsString(Settings.Secure.NAME));
final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
mutationCount.incrementAndGet();
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index ad27e41..23a4722 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -189,8 +189,6 @@
</intent-filter>
</activity>
- <service android:name=".recents.RecentsService" />
-
<!-- started from UsbDeviceSettingsManager -->
<activity android:name=".usb.UsbConfirmActivity"
android:exported="true"
diff --git a/packages/SystemUI/res/color/segmented_button_text_selector.xml b/packages/SystemUI/res/color/segmented_button_text_selector.xml
new file mode 100644
index 0000000..13c6169
--- /dev/null
+++ b/packages/SystemUI/res/color/segmented_button_text_selector.xml
@@ -0,0 +1,23 @@
+<?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. ?android:attr/colorControlHighlight"
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_selected="true" android:color="@android:color/white"/>
+ <item android:color="@color/segmented_button_text_inactive"/>
+
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_minus.xml b/packages/SystemUI/res/drawable/ic_qs_minus.xml
index 376cc28..7b76e0f 100644
--- a/packages/SystemUI/res/drawable/ic_qs_minus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_minus.xml
@@ -15,14 +15,14 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" >
<size
- android:width="64dp"
- android:height="64dp"/>
+ android:width="24dp"
+ android:height="24dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
android:fill="#FFFFFFFF"
- android:pathData="M7.0,11.0l0.0,2.0l10.0,0.0l0.0,-2.0L7.0,11.0zM12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,20.0c-4.4,0.0 -8.0,-3.6 -8.0,-8.0s3.6,-8.0 8.0,-8.0c4.4,0.0 8.0,3.6 8.0,8.0S16.4,20.0 12.0,20.0z"/>
+ android:pathData="M24.0,4.0C13.0,4.0 4.0,13.0 4.0,24.0s9.0,20.0 20.0,20.0c11.0,0.0 20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0zM34.0,26.0L14.0,26.0l0.0,-4.0l20.0,0.0L34.0,26.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_plus.xml b/packages/SystemUI/res/drawable/ic_qs_plus.xml
index a315f7f..4b9f506 100644
--- a/packages/SystemUI/res/drawable/ic_qs_plus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_plus.xml
@@ -15,14 +15,14 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" >
<size
- android:width="64dp"
- android:height="64dp"/>
+ android:width="24dp"
+ android:height="24dp"/>
<viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
<path
android:fill="#FFFFFFFF"
- android:pathData="M13.0,7.0l-2.0,0.0l0.0,4.0L7.0,11.0l0.0,2.0l4.0,0.0l0.0,4.0l2.0,0.0l0.0,-4.0l4.0,0.0l0.0,-2.0l-4.0,0.0L13.0,7.0zM12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,20.0c-4.4,0.0 -8.0,-3.6 -8.0,-8.0s3.6,-8.0 8.0,-8.0c4.4,0.0 8.0,3.6 8.0,8.0S16.4,20.0 12.0,20.0z"/>
+ android:pathData="M24.0,4.0C13.0,4.0 4.0,13.0 4.0,24.0s9.0,20.0 20.0,20.0c11.0,0.0 20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0zM34.0,26.0l-8.0,0.0l0.0,8.0l-4.0,0.0l0.0,-8.0l-8.0,0.0l0.0,-4.0l8.0,0.0l0.0,-8.0l4.0,0.0l0.0,8.0l8.0,0.0L34.0,26.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ringer_silent.xml b/packages/SystemUI/res/drawable/ic_ringer_silent.xml
index b5837f6..46b94e8 100644
--- a/packages/SystemUI/res/drawable/ic_ringer_silent.xml
+++ b/packages/SystemUI/res/drawable/ic_ringer_silent.xml
@@ -23,6 +23,6 @@
android:viewportHeight="24.0"/>
<path
- android:fill="#FFFFFFFF"
+ android:fill="@color/qs_subhead"
android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7C9.5,4.3 9.0,4.5 8.6,4.7l9.4,9.4L18.0,10.5zM17.7,19.0l2.0,2.0l1.3,-1.3L4.3,3.0L3.0,4.3l2.9,2.9C5.3,8.2 5.0,9.3 5.0,10.5L5.0,16.0l-2.0,2.0l0.0,1.0L17.7,19.0z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_vol_zen_off.xml b/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
deleted file mode 100644
index 477c36b..0000000
--- a/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
- <size
- android:width="32dp"
- android:height="32dp"/>
-
- <viewport
- android:viewportWidth="48.0"
- android:viewportHeight="48.0"/>
-
- <path
- android:fill="#4DFFFFFF"
- android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_vol_zen_on.xml b/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
deleted file mode 100644
index 0a43a7b..0000000
--- a/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
- <size
- android:width="32dp"
- android:height="32dp"/>
-
- <viewport
- android:viewportWidth="48.0"
- android:viewportHeight="48.0"/>
-
- <path
- android:fill="#FFFFFFFF"
- android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/notification_header_bg.xml b/packages/SystemUI/res/drawable/notification_header_bg.xml
index 5daec20..1f46502 100644
--- a/packages/SystemUI/res/drawable/notification_header_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_header_bg.xml
@@ -15,15 +15,7 @@
~ limitations under the License
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true">
- <shape>
- <solid android:color="@color/background_color_1_press" />
- </shape>
- </item>
- <item>
- <shape>
- <solid android:color="@color/background_color_1" />
- </shape>
- </item>
-</selector>
\ No newline at end of file
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight" >
+ <item android:drawable="@color/system_secondary_color"/>
+</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_zen_on.xml b/packages/SystemUI/res/drawable/qs_subhead_caret.xml
similarity index 65%
rename from packages/SystemUI/res/drawable/ic_qs_zen_on.xml
rename to packages/SystemUI/res/drawable/qs_subhead_caret.xml
index 4887b32..f140bd0 100644
--- a/packages/SystemUI/res/drawable/ic_qs_zen_on.xml
+++ b/packages/SystemUI/res/drawable/qs_subhead_caret.xml
@@ -15,14 +15,14 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" >
<size
- android:width="64dp"
- android:height="64dp"/>
+ android:width="24.0dp"
+ android:height="24.0dp"/>
<viewport
android:viewportWidth="48.0"
android:viewportHeight="48.0"/>
<path
- android:fill="#FFFFFFFF"
- android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
+ android:fill="@color/qs_subhead"
+ android:pathData="M14.0,20.0l10.0,10.0 10.0,-10.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/segmented_button.xml b/packages/SystemUI/res/drawable/segmented_button.xml
new file mode 100644
index 0000000..9669985
--- /dev/null
+++ b/packages/SystemUI/res/drawable/segmented_button.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_selected="true">
+ <shape android:shape="rectangle">
+ <solid android:color="@color/segmented_button_selected" />
+ <corners android:radius="@dimen/segmented_button_radius" />
+ </shape>
+ </item>
+ <item>
+ <ripple android:color="@color/segmented_button_selected" />
+ </item>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/segmented_buttons.xml b/packages/SystemUI/res/drawable/segmented_buttons.xml
new file mode 100644
index 0000000..bfbac97
--- /dev/null
+++ b/packages/SystemUI/res/drawable/segmented_buttons.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle" >
+ <solid android:color="@color/segmented_button_background" />
+ <corners android:radius="@dimen/segmented_button_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml b/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml
deleted file mode 100644
index 5992470..0000000
--- a/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
- <size
- android:width="18dp"
- android:height="18dp"/>
-
- <viewport
- android:viewportWidth="48.0"
- android:viewportHeight="48.0"/>
-
- <path
- android:fill="#FFFFFFFF"
- android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_zen_on.xml b/packages/SystemUI/res/drawable/stat_sys_zen_important.xml
similarity index 61%
copy from packages/SystemUI/res/drawable/ic_qs_zen_on.xml
copy to packages/SystemUI/res/drawable/stat_sys_zen_important.xml
index 4887b32..54a9b1b 100644
--- a/packages/SystemUI/res/drawable/ic_qs_zen_on.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_zen_important.xml
@@ -15,14 +15,14 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" >
<size
- android:width="64dp"
- android:height="64dp"/>
+ android:width="20dp"
+ android:height="20dp"/>
<viewport
- android:viewportWidth="48.0"
- android:viewportHeight="48.0"/>
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"/>
<path
android:fill="#FFFFFFFF"
- android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
+ android:pathData="M12.0,17.273l6.1800003,3.7269993 -1.6350002,-7.0290003 5.455,-4.7269993 -7.191,-0.6170006 -2.809,-6.627 -2.809,6.627 -7.191,0.6170006 5.455,4.7269993 -1.6349998,7.0290003z"/>
</vector>
diff --git a/packages/SystemUI/res/layout/segmented_button.xml b/packages/SystemUI/res/layout/segmented_button.xml
new file mode 100644
index 0000000..ef78220
--- /dev/null
+++ b/packages/SystemUI/res/layout/segmented_button.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/segmented_button_spacing"
+ android:layout_weight="1"
+ android:textColor="@color/segmented_button_text_selector"
+ android:background="@drawable/segmented_button"
+ android:textAppearance="@style/TextAppearance.QS.SegmentedButton"
+ android:minHeight="0dp"
+ android:padding="12dp" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 70589b7..7d674f8 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -26,72 +26,68 @@
android:paddingEnd="@dimen/notification_side_padding"
android:baselineAligned="false"
android:elevation="4dp"
+ android:background="@drawable/notification_header_bg"
+ android:clickable="true"
+ android:focusable="true"
>
- <View
- android:id="@+id/background"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/notification_header_bg"
- android:clickable="true"
- />
-
<View android:id="@+id/header_spacer"
android:layout_height="8dp"
android:layout_width="0dp" />
- <TextView
- android:id="@+id/header_emergency_calls_only"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_below="@id/header_spacer"
- android:paddingTop="12dp"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
- android:visibility="gone"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.EmergencyCallsOnly"
- android:text="@*android:string/emergency_calls_only" />
-
<com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
android:layout_width="40dp"
android:layout_height="@dimen/status_bar_header_height"
android:layout_alignParentEnd="true"
- android:background="@null"
+ android:background="@drawable/ripple_drawable"
android:scaleType="centerInside"
- android:padding="8dp" />
+ android:padding="8dp"/>
<ImageButton android:id="@+id/settings_button"
style="@android:style/Widget.Material.Button.Borderless"
android:layout_toStartOf="@id/multi_user_switch"
android:layout_width="48dp"
android:layout_height="@dimen/status_bar_header_height"
- android:layout_marginStart="8dp"
+ android:layout_marginRight="4dp"
+ android:background="@drawable/ripple_drawable"
android:src="@drawable/ic_settings_24dp"
android:contentDescription="@string/accessibility_desc_quick_settings"/>
- <FrameLayout android:id="@+id/system_icons_super_container"
+ <LinearLayout android:id="@+id/system_icons_super_container"
android:layout_width="wrap_content"
android:layout_height="@dimen/status_bar_header_height"
android:layout_toStartOf="@id/multi_user_switch"
android:layout_alignWithParentIfMissing="true"
android:layout_marginStart="16dp"
+ android:background="@drawable/ripple_drawable"
android:paddingEnd="2dp">
<FrameLayout android:id="@+id/system_icons_container"
android:layout_width="wrap_content"
android:layout_height="@dimen/status_bar_height"
android:layout_gravity="center_vertical"/>
- </FrameLayout>
+ <TextView android:id="@+id/battery_level"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="8dp"
+ android:paddingEnd="4dp"
+ android:textColor="#ffffff"
+ android:textSize="12sp"/>
+ </LinearLayout>
<TextView
- android:id="@+id/header_charging_info"
- android:layout_width="wrap_content"
+ android:id="@+id/header_emergency_calls_only"
android:layout_height="wrap_content"
- android:layout_toEndOf="@id/system_icons_super_container"
+ android:layout_width="wrap_content"
android:layout_below="@id/header_spacer"
+ android:layout_alignParentStart="true"
+ android:layout_toStartOf="@id/system_icons_super_container"
android:paddingTop="12dp"
+ android:paddingStart="16dp"
android:paddingEnd="16dp"
- android:paddingStart="4dp"
- style="@style/TextAppearance.StatusBar.Expanded.ChargingInfo"/>
+ android:visibility="gone"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.EmergencyCallsOnly"
+ android:text="@*android:string/emergency_calls_only" />
<RelativeLayout
android:id="@+id/datetime"
@@ -103,7 +99,7 @@
android:paddingBottom="16dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
- android:background="@drawable/ic_notify_button_bg"
+ android:background="?android:attr/selectableItemBackground"
android:enabled="false"
>
<com.android.systemui.statusbar.policy.Clock
@@ -128,29 +124,27 @@
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_header_height_keyguard"
android:layout_marginLeft="16dp"
- android:layout_toStartOf="@id/system_icons_container"
+ android:layout_toStartOf="@id/system_icons_super_container"
android:gravity="center_vertical"
android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="#ffffff"
android:singleLine="true" />
-
- <include
- android:id="@+id/qs_detail_header"
- layout="@layout/qs_detail_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- />
- <include
- android:id="@+id/brightness_container"
- layout="@layout/quick_settings_brightness_dialog"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- />
-
+ <include
+ android:id="@+id/qs_detail_header"
+ layout="@layout/qs_detail_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ />
+ <include
+ android:id="@+id/brightness_container"
+ layout="@layout/quick_settings_brightness_dialog"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ />
<TextView
android:id="@+id/header_debug_info"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 5afa967..7bd0aea 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -15,11 +15,11 @@
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+ android:layout_width="@dimen/volume_panel_width"
android:layout_height="wrap_content"
android:background="@drawable/qs_panel_background"
android:translationZ="@dimen/volume_panel_z"
- android:layout_margin="@dimen/volume_panel_z">
+ android:layout_marginBottom="@dimen/volume_panel_z">
<include layout="@layout/volume_panel" />
diff --git a/packages/SystemUI/res/layout/volume_panel.xml b/packages/SystemUI/res/layout/volume_panel.xml
index 046862f..b377a06 100644
--- a/packages/SystemUI/res/layout/volume_panel.xml
+++ b/packages/SystemUI/res/layout/volume_panel.xml
@@ -14,55 +14,23 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/visible_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal" >
+ android:orientation="vertical" >
<FrameLayout
android:id="@+id/slider_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="64dip"
- android:layout_toLeftOf="@+id/expand_button_divider" />
+ android:clipChildren="false" />
- <ImageView
- android:id="@+id/expand_button_divider"
- android:layout_width="wrap_content"
- android:layout_height="32dip"
- android:layout_gravity="top"
- android:layout_marginBottom="16dip"
- android:layout_marginTop="16dip"
- android:layout_toLeftOf="@+id/expand_button"
- android:scaleType="fitXY"
- android:src="?android:attr/dividerVertical" />
-
- <ImageView
- android:id="@+id/expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_gravity="top"
- style="@style/BorderlessButton.Tiny"
- android:padding="16dip" />
-
- <ImageView
- android:id="@+id/zen_panel_divider"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@+id/slider_panel"
- android:layout_marginLeft="16dip"
- android:layout_marginRight="16dip"
- android:scaleType="fitXY"
- android:src="?android:attr/dividerHorizontal" />
-
- <ViewStub
+ <ViewStub
android:id="@+id/zen_panel_stub"
- android:layout_below="@+id/zen_panel_divider"
- android:inflatedId="@+id/zen_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:inflatedId="@+id/zen_panel"
android:layout="@layout/zen_mode_panel" />
-</RelativeLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_panel_item.xml b/packages/SystemUI/res/layout/volume_panel_item.xml
index 6e5ab47..d91e2ab 100644
--- a/packages/SystemUI/res/layout/volume_panel_item.xml
+++ b/packages/SystemUI/res/layout/volume_panel_item.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
+<!--
+ Copyright (C) 2011 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.
@@ -15,33 +16,29 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="80dip"
- android:orientation="horizontal"
- android:layout_marginTop="8dip"
- android:layout_marginBottom="8dip"
- android:gravity="start|center_vertical">
+ android:layout_width="match_parent"
+ android:clipChildren="false"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal" >
<ImageView
- android:id="@+id/stream_icon"
- style="@style/BorderlessButton.Tiny"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="16dip"
- android:contentDescription="@null" />
- <FrameLayout
- android:id="@+id/seekbar_container"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1">
- <SeekBar
- android:id="@+id/seekbar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="16dip"
- android:paddingBottom="16dip"
- android:paddingStart="11dip"
- android:paddingEnd="11dip"
- android:layout_marginEnd="16dip" />
- </FrameLayout>
-</LinearLayout>
+ android:id="@+id/stream_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@null"
+ android:paddingBottom="12dip"
+ android:paddingLeft="16dip"
+ android:paddingRight="16dip"
+ android:paddingTop="12dip" />
+
+ <SeekBar
+ android:id="@+id/seekbar"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingBottom="0dip"
+ android:paddingEnd="17dip"
+ android:paddingStart="0dip"
+ android:paddingTop="0dip" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/zen_mode_condition.xml b/packages/SystemUI/res/layout/zen_mode_condition.xml
index 6ab8cf3..ad0d2dc 100644
--- a/packages/SystemUI/res/layout/zen_mode_condition.xml
+++ b/packages/SystemUI/res/layout/zen_mode_condition.xml
@@ -16,22 +16,23 @@
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/zen_mode_condition_detail_button_padding"
- android:layout_marginRight="@dimen/zen_mode_condition_detail_button_padding" >
+ android:layout_height="@dimen/qs_detail_item_height"
+ android:layout_marginStart="@dimen/zen_mode_condition_detail_button_padding"
+ android:layout_marginEnd="@dimen/zen_mode_condition_detail_button_padding" >
<RadioButton
android:id="@android:id/checkbox"
android:layout_width="40dp"
android:layout_marginStart="2dp"
- android:layout_height="@dimen/zen_mode_condition_height"
+ android:layout_marginEnd="1dp"
+ android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:gravity="center" />
<TextView
android:id="@android:id/title"
android:layout_width="match_parent"
- android:layout_height="@dimen/zen_mode_condition_height"
+ android:layout_height="match_parent"
android:layout_toEndOf="@android:id/checkbox"
android:layout_toStartOf="@android:id/button1"
android:ellipsize="end"
@@ -42,24 +43,24 @@
<ImageView
android:id="@android:id/button1"
style="@style/QSBorderlessButton"
- android:layout_width="@dimen/zen_mode_condition_height"
- android:layout_height="@dimen/zen_mode_condition_height"
- android:layout_alignParentEnd="true"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
android:layout_centerVertical="true"
- android:layout_marginEnd="@dimen/zen_mode_condition_height"
+ android:layout_marginEnd="@dimen/zen_mode_condition_detail_button_padding"
+ android:scaleType="center"
+ android:layout_toStartOf="@android:id/button2"
android:contentDescription="@string/accessibility_quick_settings_less_time"
- android:padding="@dimen/zen_mode_condition_detail_button_padding"
android:src="@drawable/ic_qs_minus" />
<ImageView
android:id="@android:id/button2"
style="@style/QSBorderlessButton"
- android:layout_width="@dimen/zen_mode_condition_height"
- android:layout_height="@dimen/zen_mode_condition_height"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
android:layout_alignParentEnd="true"
+ android:scaleType="center"
android:layout_centerVertical="true"
android:contentDescription="@string/accessibility_quick_settings_more_time"
- android:padding="@dimen/zen_mode_condition_detail_button_padding"
android:src="@drawable/ic_qs_plus" />
</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index 0420cbc..9d959fa 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -19,36 +19,75 @@
android:id="@+id/zen_mode_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@color/system_primary_color"
+ android:background="@drawable/qs_panel_background"
+ android:clipChildren="false"
android:orientation="vertical"
- android:paddingTop="@dimen/qs_panel_padding" >
+ android:paddingBottom="@dimen/qs_panel_padding" >
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_marginBottom="8dp"
- android:layout_marginStart="@dimen/qs_panel_padding"
- android:layout_marginEnd="@dimen/qs_panel_padding"
- android:text="@string/zen_mode_title"
- android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
-
- <LinearLayout
- android:id="@android:id/content"
+ <com.android.systemui.volume.SegmentedButtons
+ android:id="@+id/zen_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical" />
+ android:layout_marginLeft="@dimen/qs_panel_padding"
+ android:layout_marginRight="@dimen/qs_panel_padding"
+ android:layout_marginTop="8dp"
+ android:background="@drawable/segmented_buttons"
+ android:clipChildren="false" />
+
+ <FrameLayout
+ android:id="@+id/zen_subhead"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="@dimen/qs_panel_padding"
+ android:paddingRight="@dimen/qs_panel_padding"
+ android:paddingTop="@dimen/qs_panel_padding" >
+
+ <TextView
+ android:id="@+id/zen_subhead_collapsed"
+ android:layout_width="wrap_content"
+ android:layout_height="32dp"
+ android:background="@drawable/btn_borderless_rect"
+ android:clickable="true"
+ android:drawableEnd="@drawable/qs_subhead_caret"
+ android:gravity="center_vertical"
+ android:textAppearance="@style/TextAppearance.QS.Subhead" />
+
+ <TextView
+ android:id="@+id/zen_subhead_expanded"
+ android:layout_width="wrap_content"
+ android:layout_height="32dp"
+ android:clickable="true"
+ android:gravity="center_vertical"
+ android:textAppearance="@style/TextAppearance.QS.Subhead" />
+
+ <ImageView
+ android:id="@+id/zen_more_settings"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_gravity="end"
+ android:background="@drawable/btn_borderless_rect"
+ android:clickable="true"
+ android:contentDescription="@null"
+ android:scaleType="center"
+ android:src="@drawable/ic_settings_24dp" />
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/zen_conditions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="3dp" />
<TextView
- android:id="@android:id/button2"
- style="@style/QSBorderlessButton"
- android:layout_width="wrap_content"
+ android:id="@+id/zen_alarm_warning"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:layout_marginEnd="4dp"
- android:layout_gravity="end"
- android:text="@string/quick_settings_more_settings"
- android:textAppearance="@style/TextAppearance.QS.DetailButton" />
+ android:paddingBottom="8dp"
+ android:paddingLeft="@dimen/qs_panel_padding"
+ android:paddingRight="@dimen/qs_panel_padding"
+ android:paddingTop="8dp"
+ android:text="@string/zen_alarm_warning"
+ android:textAppearance="@style/TextAppearance.QS.Warning" />
</com.android.systemui.volume.ZenModePanel>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a93768b..69a22fc 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -40,6 +40,7 @@
<color name="system_error_color">#fff0592b</color>
<color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
<color name="qs_tile_text">#B3FFFFFF</color><!-- 70% white -->
+ <color name="qs_subhead">#66FFFFFF</color><!-- 40% white -->
<color name="status_bar_clock_color">#FFFFFFFF</color>
<!-- Tint color for the content on the notification overflow card. -->
@@ -79,10 +80,9 @@
<color name="notification_ripple_tinted_color">#30ffffff</color>
<!-- The color of the circle around the primary user in the user switcher -->
- <color name="current_user_border_color">@color/primary_color</color>
+ <color name="current_user_border_color">@color/system_accent_color</color>
- <!-- Our material color palette (deep teal) -->
- <color name="primary_color">#ff7fcac3</color>
- <color name="background_color_1">#ff384248</color>
- <color name="background_color_1_press">#ff54656e</color>
+ <color name="segmented_button_selected">#ff3d4549</color><!-- #33afbdc4 = 20% #afbdc4 = #ff3d4549 composite -->
+ <color name="segmented_button_background">#ff21272b</color>
+ <color name="segmented_button_text_inactive">#b3afbdc4</color><!-- 70% -->
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 2737f7e..5c7dc90 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -147,9 +147,6 @@
be 'platform' or 'noisy' (i.e. for noisy touch screens). -->
<string name="velocity_tracker_impl" translatable="false">platform</string>
- <!-- Wait on the touch feedback this long before performing an action. -->
- <integer name="feedback_start_delay">300</integer>
-
<!-- Set to true to enable the classic notification ticker that scrolls
Notification.tickerText across the status bar for what seems like an
eternity. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index dfb891d..ddacd82 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -169,14 +169,16 @@
<dimen name="qs_tile_spacing">4dp</dimen>
<dimen name="qs_panel_padding_bottom">8dp</dimen>
<dimen name="qs_detail_item_height">48dp</dimen>
- <dimen name="qs_detail_item_height_connected">72dp</dimen>
+ <dimen name="qs_detail_item_height_twoline">72dp</dimen>
+ <dimen name="segmented_button_spacing">4dp</dimen>
+ <dimen name="segmented_button_radius">2dp</dimen>
+
<!-- How far the expanded QS panel peeks from the header in collapsed state. -->
<dimen name="qs_peek_height">8dp</dimen>
<dimen name="zen_mode_condition_detail_button_padding">8dp</dimen>
- <dimen name="zen_mode_condition_height">48dp</dimen>
<!-- used by DessertCase -->
<dimen name="dessert_case_cell_size">192dp</dimen>
@@ -202,9 +204,6 @@
<!-- The translation in the Z index for each task above the last task. -->
<dimen name="recents_task_view_z_increment">10dp</dimen>
- <!-- The amount of bottom inset in the shadow outline. -->
- <dimen name="recents_task_view_shadow_outline_bottom_inset">5dp</dimen>
-
<!-- The amount to translate when animating the removal of a task. -->
<dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
@@ -287,10 +286,10 @@
<dimen name="keyguard_min_swipe_amount">75dp</dimen>
<!-- Volume panel dialog y offset -->
- <dimen name="volume_panel_top">16dp</dimen>
+ <dimen name="volume_panel_top">0dp</dimen>
<!-- Volume panel dialog width -->
- <dimen name="volume_panel_width">300dp</dimen>
+ <dimen name="volume_panel_width">344dp</dimen>
<!-- Volume panel z depth -->
<dimen name="volume_panel_z">3dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a5cfdbc..e0d48bd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -583,8 +583,14 @@
<!-- Description of the left direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
<string name="description_direction_left">"Slide left for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
- <!-- Zen mode: Short title. [CHAR LIMIT=40] -->
- <string name="zen_mode_title">Do not disturb</string>
+ <!-- Zen mode: Alarm warning. [CHAR LIMIT=40] -->
+ <string name="zen_alarm_warning">You won\'t hear your alarms</string>
+
+ <!-- Zen mode: No interruptions. [CHAR LIMIT=40] -->
+ <string name="zen_no_interruptions">No interruptions</string>
+
+ <!-- Zen mode: Only important interruptions. [CHAR LIMIT=40] -->
+ <string name="zen_important_interruptions">Priority interruptions only</string>
<!-- Text for overflow card on Keyguard when there is not enough space for all notifications on Keyguard. [CHAR LIMIT=1] -->
<string name="keyguard_more_overflow_text">+<xliff:g id="number_of_notifications" example="5">%d</xliff:g></string>
@@ -607,7 +613,16 @@
<string name="bugreport_tile_extended" translatable="false">%s\n%s (%s)</string>
<!-- Zen mode condition: no exit criteria. [CHAR LIMIT=NONE] -->
- <string name="zen_mode_forever">Until you turn this off</string>
+ <string name="zen_mode_forever">Indefinitely</string>
+
+ <!-- Interruption level: None. [CHAR LIMIT=20] -->
+ <string name="interruption_level_none">None</string>
+
+ <!-- Interruption level: Priority. [CHAR LIMIT=20] -->
+ <string name="interruption_level_priority">Priority</string>
+
+ <!-- Interruption level: All. [CHAR LIMIT=20] -->
+ <string name="interruption_level_all">All</string>
<!-- Indication on the keyguard that is shown when the device is charging. [CHAR LIMIT=40]-->
<string name="keyguard_indication_charging_time">Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
@@ -639,4 +654,7 @@
<!-- Battery saver notification action text. [CHAR LIMIT=60] -->
<string name="battery_saver_notification_action_text">Open battery saver settings</string>
+
+ <!-- Battery level for expanded quick settings [CHAR LIMIT=2] -->
+ <string name="battery_level_template"><xliff:g id="level" example="45">%d</xliff:g>%%</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index dd1b749..3f68c31 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -179,6 +179,23 @@
<item name="android:gravity">center</item>
</style>
+ <style name="TextAppearance.QS.Subhead">
+ <item name="android:textSize">14sp</item>
+ <item name="android:fontFamily">sans-serif-medium</item>
+ <item name="android:textColor">@color/qs_subhead</item>
+ </style>
+
+ <style name="TextAppearance.QS.Warning">
+ <item name="android:textSize">12sp</item>
+ <item name="android:textColor">@color/qs_subhead</item>
+ </style>
+
+ <style name="TextAppearance.QS.SegmentedButton">
+ <item name="android:textSize">12sp</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:fontFamily">sans-serif-medium</item>
+ </style>
+
<style name="BaseBrightnessDialogContainer">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
@@ -216,7 +233,7 @@
</style>
<style name="systemui_theme" parent="@android:style/Theme.DeviceDefault">
- <item name="android:colorPrimary">@color/primary_color</item>
+ <item name="android:colorPrimary">@color/system_primary_color</item>
<item name="android:colorControlActivated">@color/system_accent_color</item>
</style>
@@ -239,10 +256,10 @@
<item name="android:background">@drawable/btn_borderless_rect</item>
<item name="android:gravity">center</item>
</style>
- <style name="BorderlessButton" parent="@android:style/Widget.Material.Button.Borderless" />
- <style name="BorderlessButton.Tiny">
- <item name="android:minHeight">12dip</item>
- <item name="android:minWidth">12dip</item>
+ <!-- Window animations used for volume panel. -->
+ <style name="VolumePanelAnimation">
+ <item name="android:windowEnterAnimation">@*android:anim/dock_top_enter</item>
+ <item name="android:windowExitAnimation">@*android:anim/dock_top_exit</item>
</style>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
deleted file mode 100644
index 3bdea79..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
+++ /dev/null
@@ -1,182 +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.qs.tiles;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.AudioManager;
-import android.util.Log;
-import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
-import android.view.ViewGroup;
-
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.volume.VolumeComponent;
-import com.android.systemui.volume.VolumePanel;
-import com.android.systemui.volume.ZenModePanel;
-
-/** Quick settings tile: Notifications **/
-public class NotificationsTile extends QSTile<NotificationsTile.NotificationsState> {
- private final ZenModeController mZenController;
- private final AudioManager mAudioManager;
-
- public NotificationsTile(Host host) {
- super(host);
- mZenController = host.getZenModeController();
- mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- }
-
- @Override
- public DetailAdapter getDetailAdapter() {
- return mDetailAdapter;
- }
-
- @Override
- protected NotificationsState newTileState() {
- return new NotificationsState();
- }
-
- @Override
- public void setListening(boolean listening) {
- if (listening) {
- mZenController.addCallback(mCallback);
- final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
- mContext.registerReceiver(mReceiver, filter);
- } else {
- mZenController.removeCallback(mCallback);
- mContext.unregisterReceiver(mReceiver);
- }
- }
-
- @Override
- protected void handleClick() {
- showDetail(true);
- }
-
- @Override
- protected void handleUpdateState(NotificationsState state, Object arg) {
- state.visible = true;
- state.zen = arg instanceof Boolean ? (Boolean) arg : mZenController.isZen();
- state.ringerMode = mAudioManager.getRingerMode();
- if (state.zen) {
- state.iconId = R.drawable.ic_qs_zen_on;
- } else if (state.ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
- state.iconId = R.drawable.ic_qs_ringer_vibrate;
- } else if (state.ringerMode == AudioManager.RINGER_MODE_SILENT) {
- state.iconId = R.drawable.ic_qs_ringer_silent;
- } else {
- state.iconId = R.drawable.ic_qs_ringer_audible;
- }
- state.label = mContext.getString(R.string.quick_settings_notifications_label);
- }
-
- private final ZenModeController.Callback mCallback = new ZenModeController.Callback() {
- @Override
- public void onZenChanged(boolean zen) {
- if (DEBUG) Log.d(TAG, "onZenChanged " + zen);
- refreshState(zen);
- }
- };
-
- public static final class NotificationsState extends QSTile.State {
- public boolean zen;
- public int ringerMode;
-
- @Override
- public boolean copyTo(State other) {
- final NotificationsState o = (NotificationsState) other;
- final boolean changed = o.zen != zen || o.ringerMode != ringerMode;
- o.zen = zen;
- o.ringerMode = ringerMode;
- return super.copyTo(other) || changed;
- }
-
- @Override
- protected StringBuilder toStringBuilder() {
- final StringBuilder rt = super.toStringBuilder();
- rt.insert(rt.length() - 1, ",zen=" + zen + ",ringerMode=" + ringerMode);
- return rt;
- }
- }
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) {
- refreshState();
- }
- }
- };
-
- private final DetailAdapter mDetailAdapter = new DetailAdapter() {
-
- @Override
- public int getTitle() {
- return R.string.quick_settings_notifications_label;
- }
-
- @Override
- public Boolean getToggleState() {
- return null;
- }
-
- public void setToggleState(boolean state) {
- // noop
- }
-
- public Intent getSettingsIntent() {
- return ZenModePanel.ZEN_SETTINGS;
- }
-
- @Override
- public View createDetailView(Context context, View convertView, ViewGroup parent) {
- if (convertView != null) return convertView;
- final VolumeComponent volumeComponent = mHost.getVolumeComponent();
- final VolumePanel vp = new VolumePanel(mContext, parent, mZenController);
- final View v = vp.getContentView();
- v.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
- @Override
- public void onViewDetachedFromWindow(View v) {
- volumeComponent.setVolumePanel(null);
- }
-
- @Override
- public void onViewAttachedToWindow(View v) {
- vp.updateStates();
- volumeComponent.setVolumePanel(vp);
- }
- });
- vp.setZenModePanelCallback(new ZenModePanel.Callback() {
- @Override
- public void onMoreSettings() {
- mHost.startSettingsActivity(ZenModePanel.ZEN_SETTINGS);
- }
-
- @Override
- public void onInteraction() {
- // noop
- }
- });
- vp.postVolumeChanged(AudioManager.STREAM_RING, AudioManager.FLAG_SHOW_UI);
- return v;
- }
- };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 84eee24..5d1b16c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -266,7 +266,7 @@
final TextView summary = (TextView) item.findViewById(android.R.id.summary);
if (ap.isConnected) {
item.setMinimumHeight(mContext.getResources()
- .getDimensionPixelSize(R.dimen.qs_detail_item_height_connected));
+ .getDimensionPixelSize(R.dimen.qs_detail_item_height_twoline));
summary.setText(R.string.quick_settings_connected);
} else {
summary.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index 1b215c9..3e2ef94 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -40,7 +40,8 @@
import android.view.View;
import com.android.systemui.R;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import java.util.ArrayList;
@@ -200,7 +201,7 @@
final ActivityManager am = (ActivityManager)
mContext.getSystemService(Context.ACTIVITY_SERVICE);
final PackageManager pm = mContext.getPackageManager();
- final Bitmap thumbnail = Utilities.getThumbnail(am, td.persistentTaskId);
+ final Bitmap thumbnail = SystemServicesProxy.getThumbnail(am, td.persistentTaskId);
Drawable icon = getFullResIcon(td.resolveInfo, pm);
if (td.userId != UserHandle.myUserId()) {
// Need to badge the icon
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 52ccb59..3c4f030 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -22,126 +22,43 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
-import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.view.View;
-import android.view.WindowManager;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.TaskStackView;
+import com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm;
+import com.android.systemui.recents.views.TaskViewTransform;
-import java.lang.ref.WeakReference;
-import java.util.Iterator;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/** A proxy implementation for the recents component */
public class AlternateRecentsComponent implements ActivityOptions.OnAnimationStartedListener {
- /** A handler for messages from the recents implementation */
- class RecentsMessageHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MSG_UPDATE_FOR_CONFIGURATION) {
- Resources res = mContext.getResources();
- float statusBarHeight = res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
- Bundle replyData = msg.getData().getParcelable(KEY_CONFIGURATION_DATA);
- mSingleCountFirstTaskRect = replyData.getParcelable(KEY_SINGLE_TASK_STACK_RECT);
- mSingleCountFirstTaskRect.offset(0, (int) statusBarHeight);
- mTwoCountFirstTaskRect = replyData.getParcelable(KEY_TWO_TASK_STACK_RECT);
- mTwoCountFirstTaskRect.offset(0, (int) statusBarHeight);
- mMultipleCountFirstTaskRect = replyData.getParcelable(KEY_MULTIPLE_TASK_STACK_RECT);
- mMultipleCountFirstTaskRect.offset(0, (int) statusBarHeight);
- if (Console.Enabled) {
- Console.log(Constants.Log.App.RecentsComponent,
- "[RecentsComponent|RecentsMessageHandler|handleMessage]",
- "singleTaskRect: " + mSingleCountFirstTaskRect +
- " twoTaskRect: " + mTwoCountFirstTaskRect +
- " multipleTaskRect: " + mMultipleCountFirstTaskRect);
- }
-
- // If we had the update the animation rects as a result of onServiceConnected, then
- // we check for whether we need to toggle the recents here.
- if (mToggleRecentsUponServiceBound) {
- startRecentsActivity();
- mToggleRecentsUponServiceBound = false;
- }
- }
- }
- }
-
- /** A service connection to the recents implementation */
- class RecentsServiceConnection implements ServiceConnection {
- @Override
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.RecentsComponent,
- "[RecentsComponent|ServiceConnection|onServiceConnected]",
- "toggleRecents: " + mToggleRecentsUponServiceBound);
- }
- mService = new Messenger(service);
- mServiceIsBound = true;
-
- if (hasValidTaskRects()) {
- // Start recents if this new service connection was triggered by hitting recents
- if (mToggleRecentsUponServiceBound) {
- startRecentsActivity();
- mToggleRecentsUponServiceBound = false;
- }
- } else {
- // Otherwise, update the animation rects before starting the recents if requested
- updateAnimationRects();
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName className) {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.RecentsComponent,
- "[RecentsComponent|ServiceConnection|onServiceDisconnected]");
- }
- mService = null;
- mServiceIsBound = false;
- }
- }
-
- final public static int MSG_UPDATE_FOR_CONFIGURATION = 0;
- final public static int MSG_UPDATE_TASK_THUMBNAIL = 1;
- final public static int MSG_PRELOAD_TASKS = 2;
- final public static int MSG_CANCEL_PRELOAD_TASKS = 3;
- final public static int MSG_SHOW_RECENTS = 4;
- final public static int MSG_HIDE_RECENTS = 5;
- final public static int MSG_TOGGLE_RECENTS = 6;
- final public static int MSG_START_ENTER_ANIMATION = 7;
-
final public static String EXTRA_FROM_HOME = "recents.triggeredOverHome";
final public static String EXTRA_FROM_APP_THUMBNAIL = "recents.animatingWithThumbnail";
final public static String EXTRA_FROM_APP_FULL_SCREENSHOT = "recents.thumbnail";
final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "recents.triggeredFromAltTab";
- final public static String KEY_CONFIGURATION_DATA = "recents.data.updateForConfiguration";
- final public static String KEY_WINDOW_RECT = "recents.windowRect";
- final public static String KEY_SYSTEM_INSETS = "recents.systemInsets";
- final public static String KEY_SINGLE_TASK_STACK_RECT = "recents.singleCountTaskRect";
- final public static String KEY_TWO_TASK_STACK_RECT = "recents.twoCountTaskRect";
- final public static String KEY_MULTIPLE_TASK_STACK_RECT = "recents.multipleCountTaskRect";
+ final public static String EXTRA_TRIGGERED_FROM_TASK_ID = "recents.activeTaskId";
final static int sMinToggleDelay = 425;
final static String sToggleRecentsAction = "com.android.systemui.recents.SHOW_RECENTS";
final static String sRecentsPackage = "com.android.systemui";
final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
- final static String sRecentsService = "com.android.systemui.recents.RecentsService";
static Bitmap sLastScreenshot;
static RecentsComponent.Callbacks sRecentsComponentCallbacks;
@@ -150,37 +67,37 @@
SystemServicesProxy mSystemServicesProxy;
// Recents service binding
- Messenger mService = null;
- Messenger mMessenger;
- RecentsMessageHandler mHandler;
+ Handler mHandler;
boolean mBootCompleted = false;
- boolean mServiceIsBound = false;
- boolean mToggleRecentsUponServiceBound;
- RecentsServiceConnection mConnection = new RecentsServiceConnection();
+
+ // Task launching
+ RecentsConfiguration mConfig;
+ Rect mWindowRect;
+ Rect mTaskStackBounds;
+ TaskViewTransform mTmpTransform = new TaskViewTransform();
+ int mStatusBarHeight;
// Variables to keep track of if we need to start recents after binding
View mStatusBarView;
boolean mTriggeredFromAltTab;
-
- Rect mSingleCountFirstTaskRect = new Rect();
- Rect mTwoCountFirstTaskRect = new Rect();
- Rect mMultipleCountFirstTaskRect = new Rect();
long mLastToggleTime;
public AlternateRecentsComponent(Context context) {
mContext = context;
mSystemServicesProxy = new SystemServicesProxy(context);
- mHandler = new RecentsMessageHandler();
- mMessenger = new Messenger(mHandler);
+ mHandler = new Handler();
+ mConfig = RecentsConfiguration.reinitialize(context);
+ mWindowRect = mSystemServicesProxy.getWindowRect();
+ mTaskStackBounds = new Rect();
+ mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mTaskStackBounds);
+ mStatusBarHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
}
public void onStart() {
if (Console.Enabled) {
Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|start]");
}
-
- // Try to create a long-running connection to the recents service
- bindToRecentsService(false);
}
public void onBootCompleted() {
@@ -194,12 +111,6 @@
}
mStatusBarView = statusBarView;
mTriggeredFromAltTab = triggeredFromAltTab;
- if (!mServiceIsBound) {
- // Try to create a long-running connection to the recents service before toggling
- // recents
- bindToRecentsService(true);
- return;
- }
try {
startRecentsActivity();
@@ -214,18 +125,14 @@
Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|hideRecents]");
}
- if (mServiceIsBound && mBootCompleted) {
- if (isRecentsTopMost(null)) {
- // Notify recents to close it
- try {
- Bundle data = new Bundle();
- Message msg = Message.obtain(null, MSG_HIDE_RECENTS,
- triggeredFromAltTab ? 1 : 0, 0);
- msg.setData(data);
- mService.send(msg);
- } catch (RemoteException re) {
- re.printStackTrace();
- }
+ if (mBootCompleted) {
+ if (isRecentsTopMost(getTopMostTask(), null)) {
+ // Notify recents to hide itself
+ Intent intent = new Intent(RecentsActivity.ACTION_HIDE_RECENTS_ACTIVITY);
+ intent.setPackage(mContext.getPackageName());
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(RecentsActivity.EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
+ mContext.sendBroadcast(intent);
}
}
}
@@ -237,17 +144,10 @@
Constants.Log.App.TimeRecentsStartupKey);
Console.logStartTracingTime(Constants.Log.App.TimeRecentsLaunchTask,
Constants.Log.App.TimeRecentsLaunchKey);
- Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|toggleRecents]",
- "serviceIsBound: " + mServiceIsBound);
+ Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|toggleRecents]", "");
}
mStatusBarView = statusBarView;
mTriggeredFromAltTab = false;
- if (!mServiceIsBound) {
- // Try to create a long-running connection to the recents service before toggling
- // recents
- bindToRecentsService(true);
- return;
- }
try {
toggleRecentsActivity();
@@ -265,94 +165,26 @@
}
public void onConfigurationChanged(Configuration newConfig) {
- updateAnimationRects();
+ mConfig = RecentsConfiguration.reinitialize(mContext);
+ mWindowRect = mSystemServicesProxy.getWindowRect();
+ mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mTaskStackBounds);
+ sLastScreenshot = null;
}
- /** Binds to the recents implementation */
- private void bindToRecentsService(boolean toggleRecentsUponConnection) {
- mToggleRecentsUponServiceBound = toggleRecentsUponConnection;
- Intent intent = new Intent();
- intent.setClassName(sRecentsPackage, sRecentsService);
- mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
- }
-
- /** Returns whether we have valid task rects to animate to. */
- boolean hasValidTaskRects() {
- return mSingleCountFirstTaskRect != null && mSingleCountFirstTaskRect.width() > 0 &&
- mSingleCountFirstTaskRect.height() > 0 && mTwoCountFirstTaskRect != null &&
- mTwoCountFirstTaskRect.width() > 0 && mTwoCountFirstTaskRect.height() > 0 &&
- mMultipleCountFirstTaskRect != null && mMultipleCountFirstTaskRect.width() > 0 &&
- mMultipleCountFirstTaskRect.height() > 0;
- }
-
- /** Updates each of the task animation rects. */
- void updateAnimationRects() {
- if (mServiceIsBound) {
- Resources res = mContext.getResources();
- int statusBarHeight = res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
- int navBarHeight = res.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height);
- Rect rect = new Rect();
- WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- wm.getDefaultDisplay().getRectSize(rect);
-
- // Try and update the recents configuration
- try {
- Bundle data = new Bundle();
- data.putParcelable(KEY_WINDOW_RECT, rect);
- data.putParcelable(KEY_SYSTEM_INSETS, new Rect(0, statusBarHeight, 0, 0));
- Message msg = Message.obtain(null, MSG_UPDATE_FOR_CONFIGURATION, 0, 0);
- msg.setData(data);
- msg.replyTo = mMessenger;
- mService.send(msg);
- } catch (RemoteException re) {
- re.printStackTrace();
- }
- }
- }
-
- /** Loads the first task thumbnail */
- Bitmap loadFirstTaskThumbnail() {
+ /** Gets the top task. */
+ ActivityManager.RunningTaskInfo getTopMostTask() {
SystemServicesProxy ssp = mSystemServicesProxy;
List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1);
-
- for (ActivityManager.RunningTaskInfo t : tasks) {
- return ssp.getTaskThumbnail(t.id);
+ if (!tasks.isEmpty()) {
+ return tasks.get(0);
}
return null;
}
- /** Returns the proper rect to use for the animation, given the number of tasks. */
- Rect getAnimationTaskRect(List<ActivityManager.RecentTaskInfo> tasks) {
- // NOTE: Currently there's no method to get the number of non-home tasks, so we have to
- // compute this ourselves
- SystemServicesProxy ssp = mSystemServicesProxy;
- Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
- while (iter.hasNext()) {
- ActivityManager.RecentTaskInfo t = iter.next();
-
- // Skip tasks in the home stack
- if (ssp.isInHomeStack(t.persistentId)) {
- iter.remove();
- continue;
- }
- }
- if (tasks.size() <= 1) {
- return mSingleCountFirstTaskRect;
- } else if (tasks.size() <= 2) {
- return mTwoCountFirstTaskRect;
- } else {
- return mMultipleCountFirstTaskRect;
- }
- }
-
/** Returns whether the recents is currently running */
- boolean isRecentsTopMost(AtomicBoolean isHomeTopMost) {
+ boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask, AtomicBoolean isHomeTopMost) {
SystemServicesProxy ssp = mSystemServicesProxy;
- List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1);
- if (!tasks.isEmpty()) {
- ActivityManager.RunningTaskInfo topTask = tasks.get(0);
+ if (topTask != null) {
ComponentName topActivity = topTask.topActivity;
// Check if the front most activity is recents
@@ -382,39 +214,37 @@
// If Recents is the front most activity, then we should just communicate with it directly
// to launch the first task or dismiss itself
+ ActivityManager.RunningTaskInfo topTask = getTopMostTask();
AtomicBoolean isTopTaskHome = new AtomicBoolean();
- if (isRecentsTopMost(isTopTaskHome)) {
- // Notify recents to close itself
- try {
- Bundle data = new Bundle();
- Message msg = Message.obtain(null, MSG_TOGGLE_RECENTS, 0, 0);
- msg.setData(data);
- mService.send(msg);
+ if (isRecentsTopMost(topTask, isTopTaskHome)) {
+ // Notify recents to toggle itself
+ Intent intent = new Intent(RecentsActivity.ACTION_TOGGLE_RECENTS_ACTIVITY);
+ intent.setPackage(mContext.getPackageName());
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcast(intent);
- // Time this path
- if (Console.Enabled) {
- Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
- Constants.Log.App.TimeRecentsStartupKey, "sendToggleRecents");
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "sendToggleRecents");
- }
- } catch (RemoteException re) {
- re.printStackTrace();
+ // Time this path
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+ Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
}
mLastToggleTime = System.currentTimeMillis();
return;
} else {
// Otherwise, start the recents activity
- startRecentsActivity(isTopTaskHome.get());
+ startRecentsActivity(topTask, isTopTaskHome.get());
}
}
/** Starts the recents activity if it is not already running */
void startRecentsActivity() {
// Check if the top task is in the home stack, and start the recents activity
+ ActivityManager.RunningTaskInfo topTask = getTopMostTask();
AtomicBoolean isTopTaskHome = new AtomicBoolean();
- if (!isRecentsTopMost(isTopTaskHome)) {
- startRecentsActivity(isTopTaskHome.get());
+ if (!isRecentsTopMost(topTask, isTopTaskHome)) {
+ startRecentsActivity(topTask, isTopTaskHome.get());
}
}
@@ -422,8 +252,6 @@
* Creates the activity options for a unknown state->recents transition.
*/
ActivityOptions getUnknownTransitionActivityOptions() {
- // Reset the last screenshot
- consumeLastScreenshot();
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_unknown_enter,
R.anim.recents_from_unknown_exit, mHandler, this);
@@ -433,8 +261,6 @@
* Creates the activity options for a home->recents transition.
*/
ActivityOptions getHomeTransitionActivityOptions() {
- // Reset the last screenshot
- consumeLastScreenshot();
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_launcher_enter,
R.anim.recents_from_launcher_exit, mHandler, this);
@@ -444,13 +270,13 @@
* Creates the activity options for an app->recents transition. If this method sets the static
* screenshot, then we will use that for the transition.
*/
- ActivityOptions getThumbnailTransitionActivityOptions(Rect taskRect) {
+ ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask) {
// Recycle the last screenshot
consumeLastScreenshot();
// Take the full screenshot
if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
- sLastScreenshot = mSystemServicesProxy.takeScreenshot();
+ sLastScreenshot = mSystemServicesProxy.takeAppScreenshot();
if (sLastScreenshot != null) {
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_app_enter,
@@ -459,8 +285,10 @@
}
// If the screenshot fails, then load the first task thumbnail and use that
- Bitmap firstThumbnail = loadFirstTaskThumbnail();
+ Bitmap firstThumbnail = mSystemServicesProxy.getTaskThumbnail(topTask.id);
if (firstThumbnail != null) {
+ Rect taskRect = getThumbnailTransitionRect(topTask.id);
+
// Create the new thumbnail for the animation down
// XXX: We should find a way to optimize this so we don't need to create a new bitmap
Bitmap thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
@@ -480,8 +308,46 @@
return getUnknownTransitionActivityOptions();
}
+ /** Returns the transition rect for the given task id. */
+ Rect getThumbnailTransitionRect(int runningTaskId) {
+ // Get the stack of tasks that we are animating into
+ TaskStack stack = RecentsTaskLoader.getShallowTaskStack(mSystemServicesProxy);
+ TaskStackView tsv = new TaskStackView(mContext, stack);
+ TaskStackViewLayoutAlgorithm algo = tsv.getStackAlgorithm();
+ tsv.computeRects(mTaskStackBounds.width(), mTaskStackBounds.height() - mStatusBarHeight, 0, 0);
+ tsv.setStackScrollToInitialState();
+
+ // Find the running task in the TaskStack
+ Task task = null;
+ ArrayList<Task> tasks = stack.getTasks();
+ if (runningTaskId != -1) {
+ // Otherwise, try and find the task with the
+ int taskCount = tasks.size();
+ for (int i = taskCount - 1; i >= 0; i--) {
+ Task t = tasks.get(i);
+ if (t.key.id == runningTaskId) {
+ task = t;
+ break;
+ }
+ }
+ }
+ if (task == null) {
+ // If no task is specified or we can not find the task just use the front most one
+ task = tasks.get(tasks.size() - 1);
+ }
+
+ // Get the transform for the running task
+ TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
+ stack.getGroupIndexForTask(task, groupTaskIndex);
+ mTmpTransform = algo.getStackTransform(groupTaskIndex.groupIndex, groupTaskIndex.taskIndex,
+ tsv.getStackScroll(), mTmpTransform);
+ mTmpTransform.rect.offset(mTaskStackBounds.left, mTaskStackBounds.top);
+ mTmpTransform.rect.offset(0, mStatusBarHeight);
+ return new Rect(mTmpTransform.rect);
+ }
+
/** Starts the recents activity */
- void startRecentsActivity(boolean isTopTaskHome) {
+ void startRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
// If Recents is not the front-most activity and we should animate into it. If
// the activity at the root of the top task stack in the home stack, then we just do a
// simple transition. Otherwise, we animate to the rects defined by the Recents service,
@@ -489,34 +355,34 @@
SystemServicesProxy ssp = mSystemServicesProxy;
List<ActivityManager.RecentTaskInfo> recentTasks =
ssp.getRecentTasks(3, UserHandle.CURRENT.getIdentifier());
- Rect taskRect = getAnimationTaskRect(recentTasks);
- boolean useThumbnailTransition = !isTopTaskHome &&
- hasValidTaskRects();
+ boolean useThumbnailTransition = !isTopTaskHome;
boolean hasRecentTasks = !recentTasks.isEmpty();
if (useThumbnailTransition) {
// Try starting with a thumbnail transition
- ActivityOptions opts = getThumbnailTransitionActivityOptions(taskRect);
+ ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask);
if (opts != null) {
if (sLastScreenshot != null) {
- startAlternateRecentsActivity(opts, EXTRA_FROM_APP_FULL_SCREENSHOT);
+ startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_APP_FULL_SCREENSHOT);
} else {
- startAlternateRecentsActivity(opts, EXTRA_FROM_APP_THUMBNAIL);
+ startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_APP_THUMBNAIL);
}
} else {
// Fall through below to the non-thumbnail transition
useThumbnailTransition = false;
}
- } else {
+ }
+
+ if (!useThumbnailTransition) {
// If there is no thumbnail transition, but is launching from home into recents, then
// use a quick home transition and do the animation from home
if (hasRecentTasks) {
ActivityOptions opts = getHomeTransitionActivityOptions();
- startAlternateRecentsActivity(opts, EXTRA_FROM_HOME);
+ startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_HOME);
} else {
// Otherwise we do the normal fade from an unknown source
ActivityOptions opts = getUnknownTransitionActivityOptions();
- startAlternateRecentsActivity(opts, null);
+ startAlternateRecentsActivity(topTask, opts, null);
}
}
@@ -528,7 +394,8 @@
}
/** Starts the recents activity */
- void startAlternateRecentsActivity(ActivityOptions opts, String extraFlag) {
+ void startAlternateRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+ ActivityOptions opts, String extraFlag) {
Intent intent = new Intent(sToggleRecentsAction);
intent.setClassName(sRecentsPackage, sRecentsActivity);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -538,6 +405,7 @@
intent.putExtra(extraFlag, true);
}
intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab);
+ intent.putExtra(EXTRA_TRIGGERED_FROM_TASK_ID, (topTask != null) ? topTask.id : -1);
if (opts != null) {
mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
UserHandle.USER_CURRENT));
@@ -576,11 +444,9 @@
@Override
public void onAnimationStarted() {
// Notify recents to start the enter animation
- try {
- Message msg = Message.obtain(null, MSG_START_ENTER_ANIMATION, 0, 0);
- mService.send(msg);
- } catch (RemoteException re) {
- re.printStackTrace();
- }
+ Intent intent = new Intent(RecentsActivity.ACTION_START_ENTER_ANIMATION);
+ intent.setPackage(mContext.getPackageName());
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcast(intent);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 7a49a04..8a80b76 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -25,10 +25,12 @@
public static final boolean Verbose = false;
public static class App {
+ // Enables the simulated task affiliations
+ public static final boolean EnableSimulatedTaskGroups = false;
// Enables the screenshot app->Recents transition
public static final boolean EnableScreenshotAppTransition = false;
// Enables the filtering of tasks according to their grouping
- public static final boolean EnableTaskFiltering = true;
+ public static final boolean EnableTaskFiltering = false;
// Enables clipping of tasks against each other
public static final boolean EnableTaskStackClipping = true;
// Enables tapping on the TaskBar to launch the task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 19a38c7..bf894a7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -35,6 +35,11 @@
import android.view.ViewStub;
import android.widget.Toast;
import com.android.systemui.R;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.DebugTrigger;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.SpaceNode;
import com.android.systemui.recents.model.TaskStack;
@@ -51,6 +56,11 @@
RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks {
+ final static String EXTRA_TRIGGERED_FROM_ALT_TAB = "extra_triggered_from_alt_tab";
+ final static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
+ final static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
+ final static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
+
RecentsView mRecentsView;
SystemBarScrimViews mScrimViews;
ViewStub mEmptyViewStub;
@@ -123,8 +133,8 @@
Console.log(Constants.Log.App.SystemUIHandshake,
"[RecentsActivity|serviceBroadcast]", action, Console.AnsiRed);
}
- if (action.equals(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY)) {
- if (intent.getBooleanExtra(RecentsService.EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
+ if (action.equals(ACTION_HIDE_RECENTS_ACTIVITY)) {
+ if (intent.getBooleanExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
// Dismiss recents, launching the focused task
dismissRecentsIfVisible();
} else {
@@ -138,13 +148,13 @@
new ViewAnimation.TaskViewExitContext(exitTrigger));
}
}
- } else if (action.equals(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
+ } else if (action.equals(ACTION_TOGGLE_RECENTS_ACTIVITY)) {
// Try and unfilter and filtered stacks
if (!mRecentsView.unfilterFilteredStacks()) {
// If there are no filtered stacks, dismiss recents and launch the first task
dismissRecentsIfVisible();
}
- } else if (action.equals(RecentsService.ACTION_START_ENTER_ANIMATION)) {
+ } else if (action.equals(ACTION_START_ENTER_ANIMATION)) {
// Try and start the enter animation (or restart it on configuration changed)
ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null);
mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(
@@ -438,9 +448,9 @@
// Register the broadcast receiver to handle messages from our service
IntentFilter filter = new IntentFilter();
- filter.addAction(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY);
- filter.addAction(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
- filter.addAction(RecentsService.ACTION_START_ENTER_ANIMATION);
+ filter.addAction(ACTION_HIDE_RECENTS_ACTIVITY);
+ filter.addAction(ACTION_TOGGLE_RECENTS_ACTIVITY);
+ filter.addAction(ACTION_START_ENTER_ANIMATION);
registerReceiver(mServiceBroadcastReceiver, filter);
// Start listening for widget package changes if there is one bound
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
index 3754340..43d7a54 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
@@ -19,6 +19,7 @@
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoader;
/** Our special app widget host for the Search widget */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 3041a3c..a2407eb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -28,6 +28,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.systemui.R;
+import com.android.systemui.recents.misc.Console;
/** A static Recents configuration for the current context
@@ -75,7 +76,6 @@
public int taskViewRemoveAnimTranslationXPx;
public int taskViewTranslationZMinPx;
public int taskViewTranslationZIncrementPx;
- public int taskViewShadowOutlineBottomInsetPx;
public int taskViewRoundedCornerRadiusPx;
public int taskViewHighlightPx;
@@ -202,8 +202,6 @@
taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
taskViewTranslationZIncrementPx =
res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment);
- taskViewShadowOutlineBottomInsetPx =
- res.getDimensionPixelSize(R.dimen.recents_task_view_shadow_outline_bottom_inset);
// Task bar colors
taskBarViewDefaultBackgroundColor =
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
deleted file mode 100644
index 49149a6..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ /dev/null
@@ -1,212 +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;
-
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.TaskStackView;
-import com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm;
-import com.android.systemui.recents.views.TaskViewTransform;
-
-import java.lang.ref.WeakReference;
-
-
-/** The message handler to process Recents SysUI messages */
-class SystemUIMessageHandler extends Handler {
- WeakReference<Context> mContext;
-
- SystemUIMessageHandler(Context context) {
- // Keep a weak ref to the context instead of a strong ref
- mContext = new WeakReference<Context>(context);
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.SystemUIHandshake,
- "[RecentsService|handleMessage]", msg);
- }
-
- Context context = mContext.get();
- if (context == null) return;
-
- if (msg.what == AlternateRecentsComponent.MSG_UPDATE_FOR_CONFIGURATION) {
- RecentsTaskLoader.initialize(context);
- RecentsConfiguration config = RecentsConfiguration.reinitialize(context);
- config.updateOnConfigurationChange();
-
- try {
- Bundle data = msg.getData();
- Rect windowRect = data.getParcelable(AlternateRecentsComponent.KEY_WINDOW_RECT);
- Rect systemInsets = data.getParcelable(AlternateRecentsComponent.KEY_SYSTEM_INSETS);
-
- // NOTE: None of the rects computed below need to be offset for the status bar,
- // since that is done when we compute the animation itself in the Recents component
-
- // Create a dummy task stack & compute the rect for the thumbnail to animate to
- TaskStack stack = new TaskStack();
- TaskStackView tsv = new TaskStackView(context, stack);
- TaskStackViewLayoutAlgorithm algo = tsv.getStackAlgorithm();
- Bundle replyData = new Bundle();
- TaskViewTransform transform;
-
- // Get the task stack and search bar bounds
- Rect taskStackBounds = new Rect();
- config.getTaskStackBounds(windowRect.width(), windowRect.height(), taskStackBounds);
-
- // Calculate the target task rect for when there is one task.
-
- // NOTE: Since the nav bar height is already accounted for in the windowRect, don't
- // pass in a left or bottom inset
- stack.addTask(new Task());
- tsv.computeRects(taskStackBounds.width(), taskStackBounds.height() -
- systemInsets.top - systemInsets.bottom, 0, 0);
- tsv.setStackScrollToInitialState();
- transform = algo.getStackTransform(0, tsv.getStackScroll());
- transform.rect.offset(taskStackBounds.left, taskStackBounds.top);
- replyData.putParcelable(AlternateRecentsComponent.KEY_SINGLE_TASK_STACK_RECT,
- new Rect(transform.rect));
-
- // Also calculate the target task rect when there are two tasks.
- stack.addTask(new Task());
- tsv.computeRects(taskStackBounds.width(), taskStackBounds.height() -
- systemInsets.top - systemInsets.bottom, 0, 0);
- tsv.setStackScrollToInitialState();
- transform = algo.getStackTransform(1, tsv.getStackScroll());
- transform.rect.offset(taskStackBounds.left, taskStackBounds.top);
- replyData.putParcelable(AlternateRecentsComponent.KEY_TWO_TASK_STACK_RECT,
- new Rect(transform.rect));
-
- // Also calculate the target task rect when there are two tasks.
- stack.addTask(new Task());
- tsv.computeRects(taskStackBounds.width(), taskStackBounds.height() -
- systemInsets.top - systemInsets.bottom, 0, 0);
- tsv.setStackScrollToInitialState();
- transform = algo.getStackTransform(2, tsv.getStackScroll());
- transform.rect.offset(taskStackBounds.left, taskStackBounds.top);
- replyData.putParcelable(AlternateRecentsComponent.KEY_MULTIPLE_TASK_STACK_RECT,
- new Rect(transform.rect));
-
- data.putParcelable(AlternateRecentsComponent.KEY_CONFIGURATION_DATA, replyData);
- Message reply = Message.obtain(null,
- AlternateRecentsComponent.MSG_UPDATE_FOR_CONFIGURATION, 0, 0);
- reply.setData(data);
- msg.replyTo.send(reply);
- } catch (RemoteException re) {
- re.printStackTrace();
- }
- } else if (msg.what == AlternateRecentsComponent.MSG_HIDE_RECENTS) {
- // Send a broadcast to hide recents
- Intent intent = new Intent(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY);
- intent.setPackage(context.getPackageName());
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- if (msg.arg1 != 0) {
- intent.putExtra(RecentsService.EXTRA_TRIGGERED_FROM_ALT_TAB, true);
- }
- context.sendBroadcast(intent);
- } else if (msg.what == AlternateRecentsComponent.MSG_TOGGLE_RECENTS) {
- // Send a broadcast to toggle recents
- Intent intent = new Intent(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
- intent.setPackage(context.getPackageName());
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- context.sendBroadcast(intent);
-
- // Time this path
- if (Console.Enabled) {
- Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
- Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
- }
- } else if (msg.what == AlternateRecentsComponent.MSG_START_ENTER_ANIMATION) {
- // Send a broadcast to start the enter animation
- Intent intent = new Intent(RecentsService.ACTION_START_ENTER_ANIMATION);
- intent.setPackage(context.getPackageName());
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- context.sendBroadcast(intent);
- }
- }
-}
-
-/* Service */
-public class RecentsService extends Service {
- final static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
- final static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
- final static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
- final static String EXTRA_TRIGGERED_FROM_ALT_TAB = "extra_triggered_from_alt_tab";
-
- Messenger mSystemUIMessenger = new Messenger(new SystemUIMessageHandler(this));
-
- @Override
- public void onCreate() {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onCreate]");
- }
- super.onCreate();
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onBind]");
- }
- return mSystemUIMessenger.getBinder();
- }
-
- @Override
- public boolean onUnbind(Intent intent) {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onUnbind]");
- }
- return super.onUnbind(intent);
- }
-
- @Override
- public void onRebind(Intent intent) {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onRebind]");
- }
- super.onRebind(intent);
- }
-
- @Override
- public void onDestroy() {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onDestroy]");
- }
- super.onDestroy();
- }
-
- @Override
- public void onTrimMemory(int level) {
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- if (loader != null) {
- loader.onTrimMemory(level);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Console.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Console.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/recents/Console.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/Console.java
index 0cb74b9..28ac9d3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Console.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Console.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
import android.content.ComponentCallbacks2;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/DebugTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DebugTrigger.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/recents/DebugTrigger.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/DebugTrigger.java
index d90e2be..d000985 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/DebugTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DebugTrigger.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
import android.os.Handler;
import android.os.SystemClock;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
index f0b2cb6..4456066 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
import android.os.Handler;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/NamedCounter.java b/packages/SystemUI/src/com/android/systemui/recents/misc/NamedCounter.java
new file mode 100644
index 0000000..ec3c39c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/NamedCounter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.misc;
+
+/**
+ * Used to generate successive incremented names.
+ */
+public class NamedCounter {
+
+ int mCount;
+ String mPrefix = "";
+ String mSuffix = "";
+
+ public NamedCounter(String prefix, String suffix) {
+ mPrefix = prefix;
+ mSuffix = suffix;
+ }
+
+ /** Returns the next name. */
+ public String nextName() {
+ String name = mPrefix + mCount + mSuffix;
+ mCount++;
+ return name;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
index d525546..31825af 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 3765d1c..05c0f58 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -30,14 +30,17 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -47,7 +50,9 @@
import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.WindowManager;
+import com.android.systemui.recents.Constants;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -128,6 +133,7 @@
rti.baseIntent = new Intent();
rti.baseIntent.setComponent(cn);
rti.description = description;
+ rti.firstActiveTime = rti.lastActiveTime = i;
if (i % 2 == 0) {
rti.taskDescription = new ActivityManager.TaskDescription(description,
Bitmap.createBitmap(mDummyIcon),
@@ -208,7 +214,7 @@
return thumbnail;
}
- Bitmap thumbnail = Utilities.getThumbnail(mAm, taskId);
+ Bitmap thumbnail = SystemServicesProxy.getThumbnail(mAm, taskId);
if (thumbnail != null) {
// We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
// left pixel, then assume the whole thumbnail is transparent. Generally, proper
@@ -224,6 +230,25 @@
return thumbnail;
}
+ /**
+ * Returns a task thumbnail from the activity manager
+ */
+ public static Bitmap getThumbnail(ActivityManager activityManager, int taskId) {
+ ActivityManager.TaskThumbnail taskThumbnail = activityManager.getTaskThumbnail(taskId);
+ Bitmap thumbnail = taskThumbnail.mainThumbnail;
+ ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
+ if (thumbnail == null && descriptor != null) {
+ thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor());
+ }
+ if (descriptor != null) {
+ try {
+ descriptor.close();
+ } catch (IOException e) {
+ }
+ }
+ return thumbnail;
+ }
+
/** Moves a task to the front with the specified activity options */
public void moveTaskToFront(int taskId, ActivityOptions opts) {
if (mAm == null) return;
@@ -371,6 +396,17 @@
}
/**
+ * Returns the window rect.
+ */
+ public Rect getWindowRect() {
+ Rect windowRect = new Rect();
+ if (mWm == null) return windowRect;
+
+ mWm.getDefaultDisplay().getRectSize(windowRect);
+ return windowRect;
+ }
+
+ /**
* Takes a screenshot of the current surface.
*/
public Bitmap takeScreenshot() {
@@ -378,4 +414,11 @@
mDisplay.getDisplayInfo(di);
return SurfaceControl.screenshot(di.getNaturalWidth(), di.getNaturalHeight());
}
+
+ /**
+ * Takes a screenshot of the current app.
+ */
+ public Bitmap takeAppScreenshot() {
+ return takeScreenshot();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
similarity index 83%
rename from packages/SystemUI/src/com/android/systemui/recents/Utilities.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 6a7cfcc..bda195b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
import android.app.ActivityManager;
import android.graphics.Bitmap;
@@ -23,6 +23,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.ParcelFileDescriptor;
+import com.android.systemui.recents.RecentsConfiguration;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
@@ -95,21 +96,4 @@
throws IllegalAccessException, InvocationTargetException {
sPropertyMethod.invoke(null, property, value);
}
-
- /** Retrieves a task thumbnail from the activity manager */
- public static Bitmap getThumbnail(ActivityManager activityManager, int taskId) {
- ActivityManager.TaskThumbnail taskThumbnail = activityManager.getTaskThumbnail(taskId);
- Bitmap thumbnail = taskThumbnail.mainThumbnail;
- final ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
- if (thumbnail == null && descriptor != null) {
- thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor());
- }
- if (descriptor != null) {
- try {
- descriptor.close();
- } catch (IOException e) {
- }
- }
- return thumbnail;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
index cbe39e3..2d50659 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
@@ -21,7 +21,7 @@
import android.content.Context;
import android.os.Looper;
import com.android.internal.content.PackageMonitor;
-import com.android.systemui.recents.SystemServicesProxy;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import java.util.HashSet;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 9d8d746..29f1440 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -28,10 +28,10 @@
import android.os.HandlerThread;
import android.os.UserHandle;
import android.util.Pair;
-import com.android.systemui.recents.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.SystemServicesProxy;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import java.util.ArrayList;
import java.util.Collections;
@@ -368,10 +368,9 @@
return mSystemServicesProxy;
}
- private List<ActivityManager.RecentTaskInfo> getRecentTasks() {
+ private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp) {
long t1 = System.currentTimeMillis();
- SystemServicesProxy ssp = mSystemServicesProxy;
List<ActivityManager.RecentTaskInfo> tasks =
ssp.getRecentTasks(50, UserHandle.CURRENT.getIdentifier());
Collections.reverse(tasks);
@@ -402,7 +401,7 @@
// Get the recent tasks
SystemServicesProxy ssp = mSystemServicesProxy;
- List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks();
+ List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
// Add each task to the task stack
t1 = System.currentTimeMillis();
@@ -430,7 +429,7 @@
// Create a new task
Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, activityLabel,
- activityIcon, activityColor, t.userId, t.lastActiveTime);
+ activityIcon, activityColor, t.userId, t.firstActiveTime, t.lastActiveTime);
// Preload the specified number of apps
if (i >= (taskCount - preloadCount)) {
@@ -495,6 +494,9 @@
"" + (System.currentTimeMillis() - t1) + "ms");
}
+ // Simulate the groupings that we describe
+ stack.createSimulatedAffiliatedGroupings();
+
// Start the task loader
mLoader.start(context);
@@ -509,6 +511,24 @@
return root;
}
+ /** Creates a lightweight stack of the current recent tasks, without thumbnails and icons. */
+ public static TaskStack getShallowTaskStack(SystemServicesProxy ssp) {
+ List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
+ TaskStack stack = new TaskStack();
+
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ ActivityManager.RecentTaskInfo t = tasks.get(i);
+ ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent(), t.userId);
+ if (info == null) continue;
+
+ stack.addTask(new Task(t.persistentId, true, t.baseIntent, null, null, 0, 0,
+ t.firstActiveTime, t.lastActiveTime));
+ }
+ stack.createSimulatedAffiliatedGroupings();
+ return stack;
+ }
+
/** Acquires the task resource data directly from the pool. */
public void loadTaskData(Task t) {
Drawable applicationIcon = mApplicationIconCache.get(t.key);
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 abfb221..002395f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -19,7 +19,7 @@
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.Utilities;
/**
@@ -39,12 +39,14 @@
public final int id;
public final Intent baseIntent;
public final int userId;
+ public long firstActiveTime;
public long lastActiveTime;
- public TaskKey(int id, Intent intent, int userId, long lastActiveTime) {
+ public TaskKey(int id, Intent intent, int userId, long firstActiveTime, long lastActiveTime) {
this.id = id;
this.baseIntent = intent;
this.userId = userId;
+ this.firstActiveTime = firstActiveTime;
this.lastActiveTime = lastActiveTime;
}
@@ -76,6 +78,7 @@
}
public TaskKey key;
+ public TaskGrouping group;
public Drawable applicationIcon;
public Drawable activityIcon;
public String activityLabel;
@@ -92,8 +95,9 @@
}
public Task(int id, boolean isActive, Intent intent, String activityTitle,
- Drawable activityIcon, int colorPrimary, int userId, long lastActiveTime) {
- this.key = new TaskKey(id, intent, userId, lastActiveTime);
+ Drawable activityIcon, int colorPrimary, int userId, long firstActiveTime,
+ long lastActiveTime) {
+ this.key = new TaskKey(id, intent, userId, firstActiveTime, lastActiveTime);
this.activityLabel = activityTitle;
this.activityIcon = activityIcon;
this.colorPrimary = colorPrimary;
@@ -107,6 +111,14 @@
mCb = cb;
}
+ /** Set the grouping */
+ public void setGroup(TaskGrouping group) {
+ if (group != null && this.group != null) {
+ throw new RuntimeException("This task is already assigned to a group.");
+ }
+ this.group = group;
+ }
+
/** Notifies the callback listeners that this task has been loaded */
public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon) {
this.applicationIcon = applicationIcon;
@@ -134,6 +146,11 @@
@Override
public String toString() {
- return "Task: " + key.baseIntent.getComponent().getPackageName() + " [" + super.toString() + "]";
+ String groupAffiliation = "no group";
+ if (group != null) {
+ groupAffiliation = group.affiliation;
+ }
+ return "Task (" + groupAffiliation + "): " + key.baseIntent.getComponent().getPackageName() +
+ " [" + super.toString() + "]";
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
new file mode 100644
index 0000000..3a18bce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
@@ -0,0 +1,53 @@
+package com.android.systemui.recents.model;
+
+import java.util.ArrayList;
+
+/** Represents a grouping of tasks witihin a stack. */
+public class TaskGrouping {
+
+ String affiliation;
+ long latestActiveTimeInGroup;
+
+ ArrayList<Task.TaskKey> mTasks = new ArrayList<Task.TaskKey>();
+
+ /** Creates a group with a specified affiliation. */
+ public TaskGrouping(String affiliation) {
+ this.affiliation = affiliation;
+ }
+
+ /** Adds a new task to this group. */
+ void addTask(Task t) {
+ mTasks.add(t.key);
+ if (t.key.lastActiveTime > latestActiveTimeInGroup) {
+ latestActiveTimeInGroup = t.key.lastActiveTime;
+ }
+ t.setGroup(this);
+ }
+
+ /** Removes a task from this group. */
+ void removeTask(Task t) {
+ mTasks.remove(t.key);
+ latestActiveTimeInGroup = 0;
+ int taskCount = mTasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ long lastActiveTime = mTasks.get(i).lastActiveTime;
+ if (lastActiveTime > latestActiveTimeInGroup) {
+ latestActiveTimeInGroup = lastActiveTime;
+ }
+ }
+ t.setGroup(null);
+ }
+
+ /** Gets the front task */
+ public boolean isFrontMostTask(Task t) {
+ return t.key.equals(mTasks.get(mTasks.size() - 1));
+ }
+
+ /** Finds the index of a given task in a group. */
+ public int indexOf(Task t) {
+ return mTasks.indexOf(t.key);
+ }
+
+ /** Returns the number of tasks in this group. */
+ public int getTaskCount() { return mTasks.size(); }
+}
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 24e01bd..ffd2857 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -16,9 +16,13 @@
package com.android.systemui.recents.model;
-import android.content.Context;
+import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.misc.NamedCounter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
@@ -36,6 +40,7 @@
class FilteredTaskList {
ArrayList<Task> mTasks = new ArrayList<Task>();
ArrayList<Task> mFilteredTasks = new ArrayList<Task>();
+ HashMap<Task.TaskKey, Integer> mTaskIndices = new HashMap<Task.TaskKey, Integer>();
TaskFilter mFilter;
/** Sets the task filter, saving the current touch state */
@@ -83,7 +88,7 @@
/** Returns the index of this task in the list of filtered tasks */
int indexOf(Task t) {
- return mFilteredTasks.indexOf(t);
+ return mTaskIndices.get(t.key);
}
/** Returns the size of the list of filtered tasks */
@@ -93,7 +98,7 @@
/** Returns whether the filtered list contains this task */
boolean contains(Task t) {
- return mFilteredTasks.contains(t);
+ return mTaskIndices.containsKey(t.key);
}
/** Updates the list of filtered tasks whenever the base task list changes */
@@ -110,6 +115,17 @@
} else {
mFilteredTasks.addAll(mTasks);
}
+ updateFilteredTaskIndices();
+ }
+
+ /** Updates the mapping of tasks to indices. */
+ private void updateFilteredTaskIndices() {
+ mTaskIndices.clear();
+ int taskCount = mFilteredTasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task t = mFilteredTasks.get(i);
+ mTaskIndices.put(t.key, i);
+ }
}
/** Returns whether this task list is filtered */
@@ -127,7 +143,8 @@
* The task stack contains a list of multiple tasks.
*/
public class TaskStack {
- /* Task stack callbacks */
+
+ /** Task stack callbacks */
public interface TaskStackCallbacks {
/* Notifies when a task has been added to the stack */
public void onStackTaskAdded(TaskStack stack, Task t);
@@ -139,11 +156,25 @@
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;
+ }
+ }
+
FilteredTaskList mTaskList = new FilteredTaskList();
TaskStackCallbacks mCb;
- public TaskStack() {
- }
+ ArrayList<TaskGrouping> mGroups = new ArrayList<TaskGrouping>();
+ HashMap<String, TaskGrouping> mAffinitiesGroups = new HashMap<String, TaskGrouping>();
+ HashMap<TaskGrouping, Integer> mGroupsIndices = new HashMap<TaskGrouping, Integer>();
/** Sets the callbacks for this task stack */
public void setCallbacks(TaskStackCallbacks cb) {
@@ -161,8 +192,16 @@
/** Removes a task */
public void removeTask(Task t) {
if (mTaskList.contains(t)) {
+ // Remove the task from the list
mTaskList.remove(t);
+ // Remove it from the group as well, and if it is empty, remove the group
+ TaskGrouping group = t.group;
+ group.removeTask(t);
+ if (group.getTaskCount() == 0) {
+ removeGroup(group);
+ }
if (mCb != null) {
+ // Notify that a task has been removed
mCb.onStackTaskRemoved(this, t);
}
}
@@ -170,10 +209,20 @@
/** Sets a few tasks in one go */
public void setTasks(List<Task> tasks) {
- int taskCount = mTaskList.getTasks().size();
+ ArrayList<Task> taskList = mTaskList.getTasks();
+ int taskCount = taskList.size();
for (int i = 0; i < taskCount; i++) {
- Task t = mTaskList.getTasks().get(i);
+ Task t = taskList.get(i);
+ // Remove the task from the list
+ mTaskList.remove(t);
+ // Remove it from the group as well, and if it is empty, remove the group
+ TaskGrouping group = t.group;
+ group.removeTask(t);
+ if (group.getTaskCount() == 0) {
+ removeGroup(group);
+ }
if (mCb != null) {
+ // Notify that a task has been removed
mCb.onStackTaskRemoved(this, t);
}
}
@@ -185,6 +234,11 @@
}
}
+ /** Gets the front task */
+ public Task getFrontMostTask() {
+ return mTaskList.getTasks().get(mTaskList.size() - 1);
+ }
+
/** Gets the tasks */
public ArrayList<Task> getTasks() {
return mTaskList.getTasks();
@@ -200,10 +254,7 @@
return mTaskList.indexOf(t);
}
- /** Tests whether a task is in this current task stack */
- public boolean containsTask(Task t) {
- return mTaskList.contains(t);
- }
+ /******** Filtering ********/
/** Filters the stack into tasks similar to the one specified */
public void filterTasks(final Task t) {
@@ -238,6 +289,135 @@
return mTaskList.hasFilter();
}
+ /******** Grouping ********/
+
+ /** Adds a group to the set */
+ public void addGroup(TaskGrouping group) {
+ mGroups.add(group);
+ mAffinitiesGroups.put(group.affiliation, group);
+ updateTaskGroupingIndices();
+ }
+
+ public void removeGroup(TaskGrouping group) {
+ // XXX: Ensure that there are no more tasks in this group
+ mGroups.remove(group);
+ mAffinitiesGroups.remove(group.affiliation);
+ mGroupsIndices.remove(group);
+ updateTaskGroupingIndices();
+ }
+
+ /** Adds a mapping from a task to a group. */
+ public void addTaskToGroup(TaskGrouping group, Task task) {
+ if (!mAffinitiesGroups.containsKey(group.affiliation)) {
+ throw new RuntimeException("Unexpected group");
+ }
+ group.addTask(task);
+ }
+
+ /** Returns the group with the specified affiliation. */
+ public TaskGrouping getGroupWithAffiliation(String affiliation) {
+ return mAffinitiesGroups.get(affiliation);
+ }
+
+ /** Returns the number of groups. */
+ public int getGroupingCount() {
+ return mGroups.size();
+ }
+
+ /** Returns the group and task indices for a given task. */
+ public void getGroupIndexForTask(Task task, GroupTaskIndex indices) {
+ indices.groupIndex = mGroupsIndices.get(task.group);
+ indices.taskIndex = task.group.indexOf(task);
+ }
+
+ /**
+ * Temporary: This method will simulate affiliation groups by
+ */
+ public void createSimulatedAffiliatedGroupings() {
+ if (Constants.DebugFlags.App.EnableSimulatedTaskGroups) {
+ HashMap<Task.TaskKey, Task> taskMap = new HashMap<Task.TaskKey, Task>();
+ // Sort all tasks by increasing firstActiveTime of the task
+ ArrayList<Task> tasks = mTaskList.getTasks();
+ Collections.sort(tasks, new Comparator<Task>() {
+ @Override
+ public int compare(Task task, Task task2) {
+ return (int) (task.key.firstActiveTime - task2.key.firstActiveTime);
+ }
+ });
+ // Create groups when sequential packages are the same
+ NamedCounter counter = new NamedCounter("task-group", "");
+ int taskCount = tasks.size();
+ String prevPackage = "";
+ String prevAffiliation = "";
+ for (int i = 0; i < taskCount; i++) {
+ Task t = tasks.get(i);
+ String packageName = t.key.baseIntent.getComponent().getPackageName();
+ TaskGrouping group;
+ if (packageName.equals(prevPackage)) {
+ group = getGroupWithAffiliation(prevAffiliation);
+ } else {
+ String affiliation = counter.nextName();
+ group = new TaskGrouping(affiliation);
+ addGroup(group);
+ prevAffiliation = affiliation;
+ prevPackage = packageName;
+ }
+ group.addTask(t);
+ taskMap.put(t.key, t);
+ }
+ // Sort groups by increasing latestActiveTime of the group
+ Collections.sort(mGroups, new Comparator<TaskGrouping>() {
+ @Override
+ public int compare(TaskGrouping taskGrouping, TaskGrouping taskGrouping2) {
+ return (int) (taskGrouping.latestActiveTimeInGroup -
+ taskGrouping2.latestActiveTimeInGroup);
+ }
+ });
+ updateTaskGroupingIndices();
+ // Sort group tasks by increasing firstActiveTime of the task, and also build a new list of
+ // tasks
+ int taskIndex = 0;
+ int groupCount = mGroups.size();
+ for (int i = 0; i < groupCount; i++) {
+ TaskGrouping group = mGroups.get(i);
+ Collections.sort(group.mTasks, new Comparator<Task.TaskKey>() {
+ @Override
+ public int compare(Task.TaskKey taskKey, Task.TaskKey taskKey2) {
+ return (int) (taskKey.firstActiveTime - taskKey2.firstActiveTime);
+ }
+ });
+ ArrayList<Task.TaskKey> groupTasks = group.mTasks;
+ int groupTaskCount = groupTasks.size();
+ for (int j = 0; j < groupTaskCount; j++) {
+ tasks.set(taskIndex, taskMap.get(groupTasks.get(j)));
+ taskIndex++;
+ }
+ }
+ mTaskList.set(tasks);
+ } else {
+ // Create a group per task
+ NamedCounter counter = new NamedCounter("task-group", "");
+ ArrayList<Task> tasks = mTaskList.getTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task t = tasks.get(i);
+ TaskGrouping group = new TaskGrouping(counter.nextName());
+ addGroup(group);
+ group.addTask(t);
+ }
+ }
+ }
+
+ /** Updates the mapping of tasks to indices. */
+ private void updateTaskGroupingIndices() {
+ mGroupsIndices.clear();
+ int groupsCount = mGroups.size();
+ for (int i = 0; i < groupsCount; i++) {
+ TaskGrouping g = mGroups.get(i);
+ mGroupsIndices.put(g, i);
+ }
+ }
+
@Override
public String toString() {
String str = "Tasks:\n";
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
index 6568b1a..49a7ff9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -31,7 +30,7 @@
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.systemui.R;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
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 f203d3e..87d45ba 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -34,7 +34,7 @@
import android.view.View;
import android.view.WindowInsets;
import android.widget.FrameLayout;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.model.RecentsPackageMonitor;
@@ -438,23 +438,25 @@
}
// Upfront the processing of the thumbnail
- TaskViewTransform transform;
+ TaskViewTransform transform = new TaskViewTransform();
View sourceView = tv;
int offsetX = 0;
int offsetY = 0;
int stackScroll = stackView.getStackScroll();
+ TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
+ stack.getGroupIndexForTask(task, groupTaskIndex);
if (tv == null) {
// If there is no actual task view, then use the stack view as the source view
// and then offset to the expected transform rect, but bound this to just
// outside the display rect (to ensure we don't animate from too far away)
sourceView = stackView;
- transform = stackView.getStackAlgorithm().getStackTransform(stack.indexOfTask(task),
- stackScroll);
+ transform = stackView.getStackAlgorithm().getStackTransform(groupTaskIndex.groupIndex,
+ groupTaskIndex.taskIndex, stackScroll, transform);
offsetX = transform.rect.left;
offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
} else {
- transform = stackView.getStackAlgorithm().getStackTransform(stack.indexOfTask(task),
- stackScroll);
+ transform = stackView.getStackAlgorithm().getStackTransform(groupTaskIndex.groupIndex,
+ groupTaskIndex.taskIndex, stackScroll, transform);
}
// Compute the thumbnail to scale up from
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
index cae6bd7..8409227 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
@@ -28,7 +28,7 @@
import android.view.VelocityTracker;
import android.view.View;
import android.view.animation.LinearInterpolator;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.Constants;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index 9c60603..4ed3b59 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -34,7 +34,7 @@
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
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 dd47fdd..4a1faee 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -31,18 +31,19 @@
import android.widget.FrameLayout;
import android.widget.OverScroller;
import com.android.systemui.R;
-import com.android.systemui.recents.Console;
import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.DozeTrigger;
import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.ReferenceCountedTrigger;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.DozeTrigger;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsPackageMonitor;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Set;
@@ -180,7 +181,18 @@
mStackViewsDirty = true;
}
- /** Finds the child view given a specific task */
+ /** Returns a mapping of child view to Task. */
+ HashMap<Task, TaskView> getTaskChildViewMap() {
+ HashMap<Task, TaskView> taskViewMap = new HashMap<Task, TaskView>();
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView tv = (TaskView) getChildAt(i);
+ taskViewMap.put(tv.getTask(), tv);
+ }
+ return taskViewMap;
+ }
+
+ /** Finds the child view given a specific task. */
TaskView getChildViewForTask(Task t) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -225,9 +237,11 @@
}
// Update the stack transforms
+ TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
for (int i = taskCount - 1; i >= 0; i--) {
- TaskViewTransform transform = mStackAlgorithm.getStackTransform(i, stackScroll,
- taskTransforms.get(i));
+ mStack.getGroupIndexForTask(tasks.get(i), groupTaskIndex);
+ TaskViewTransform transform = mStackAlgorithm.getStackTransform(groupTaskIndex.groupIndex,
+ groupTaskIndex.taskIndex, stackScroll, taskTransforms.get(i));
if (transform.visible) {
if (frontMostVisibleIndex < 0) {
frontMostVisibleIndex = i;
@@ -253,6 +267,11 @@
if (visibleRangeOut != null) {
visibleRangeOut[0] = frontMostVisibleIndex;
visibleRangeOut[1] = backMostVisibleIndex;
+ if (Console.Enabled) {
+ Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel,
+ "[TaskStackView|updateStackTransforms]",
+ "Back: " + backMostVisibleIndex + " Front: " + frontMostVisibleIndex);
+ }
}
}
@@ -278,58 +297,57 @@
"mStackViewsDirty: " + mStackViewsDirty, Console.AnsiYellow);
}
if (mStackViewsDirty) {
- // XXX: Consider using TaskViewTransform pool to prevent allocations
- // XXX: Iterate children views, update transforms and remove all that are not visible
- // For all remaining tasks, update transforms and if visible add the view
-
// Get all the task transforms
- int[] visibleRange = mTmpVisibleRange;
- int stackScroll = getStackScroll();
ArrayList<Task> tasks = mStack.getTasks();
+ int stackScroll = getStackScroll();
+ int[] visibleRange = mTmpVisibleRange;
updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false);
+ TaskViewTransform tmpTransform = new TaskViewTransform();
+ TaskStack.GroupTaskIndex gti = new TaskStack.GroupTaskIndex();
- // Update the visible state of all the tasks
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- TaskViewTransform transform = mCurrentTaskTransforms.get(i);
- TaskView tv = getChildViewForTask(task);
-
- if (transform.visible) {
- if (tv == null) {
- tv = mViewPool.pickUpViewFromPool(task, task);
- // When we are picking up a new view from the view pool, prepare it for any
- // following animation by putting it in a reasonable place
- if (mStackViewsAnimationDuration > 0 && i != 0) {
- int fromIndex = (transform.t < 0) ?
- Math.max(0, (visibleRange[1] - 1)) :
- Math.min(taskCount - 1, (visibleRange[0] + 1));
- tv.updateViewPropertiesToTaskTransform(
- mStackAlgorithm.getStackTransform(fromIndex, stackScroll), 0);
- }
- }
- } else {
- if (tv != null) {
- mViewPool.returnViewToPool(tv);
- }
- }
- }
-
- // Update all the remaining view children
- // NOTE: We have to iterate in reverse where because we are removing views directly
+ // Return all the invisible children to the pool
+ HashMap<Task, TaskView> taskChildViewMap = getTaskChildViewMap();
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
TaskView tv = (TaskView) getChildAt(i);
Task task = tv.getTask();
int taskIndex = mStack.indexOfTask(task);
- if (taskIndex < 0 || !mCurrentTaskTransforms.get(taskIndex).visible) {
+ if (taskIndex < visibleRange[1] || taskIndex > visibleRange[0]) {
+ taskChildViewMap.remove(task);
mViewPool.returnViewToPool(tv);
- } else {
- tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(taskIndex),
- mStackViewsAnimationDuration);
}
}
+ // Pick up all the newly visible children and update all the existing children
+ boolean isValidVisibleRange = visibleRange[0] != -1 && visibleRange[1] != -1;
+ for (int i = visibleRange[0]; isValidVisibleRange && i >= visibleRange[1]; i--) {
+ Task task = tasks.get(i);
+ TaskViewTransform transform = mCurrentTaskTransforms.get(i);
+ TaskView tv = taskChildViewMap.get(task);
+ int taskIndex = mStack.indexOfTask(task);
+
+ if (tv == null) {
+ tv = mViewPool.pickUpViewFromPool(task, task);
+ if (mStackViewsAnimationDuration > 0) {
+ // For items in the list, put them in start animating them from the
+ // approriate ends of the list where they are expected to appear
+ Task fromTask = (transform.t < 0) ?
+ tasks.get(visibleRange[1]) :
+ tasks.get(visibleRange[0]);
+ mStack.getGroupIndexForTask(fromTask, gti);
+ tmpTransform = mStackAlgorithm.getStackTransform(
+ (transform.t < 0) ? gti.groupIndex - 1 : gti.groupIndex + 1,
+ (transform.t < 0) ? gti.taskIndex - 1 : gti.taskIndex + 1,
+ stackScroll, tmpTransform);
+ tv.updateViewPropertiesToTaskTransform(tmpTransform, 0);
+ }
+ }
+
+ // Update and animate the task into place
+ tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(taskIndex),
+ mStackViewsAnimationDuration);
+ }
+
if (Console.Enabled) {
Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel,
" [TaskStackView|viewChildren]", "" + getChildCount());
@@ -357,7 +375,7 @@
}
/** Computes the initial stack scroll for the stack. */
int getInitialStackScroll() {
- if (mStack.getTaskCount() > 2) {
+ if (mStack.getGroupingCount() > 2) {
return mMaxScroll - mStackAlgorithm.mTaskRect.height() / 2;
}
return mMaxScroll;
@@ -477,7 +495,7 @@
/** Updates the min and max virtual scroll bounds */
void updateMinMaxScroll(boolean boundScrollToNewMinMax) {
// Compute the min and max scroll values
- mStackAlgorithm.computeMinMaxScroll(mStack.getTaskCount());
+ mStackAlgorithm.computeMinMaxScroll(mStack.getGroupingCount());
mMinScroll = mStackAlgorithm.mMinScroll;
mMaxScroll = mStackAlgorithm.mMaxScroll;
@@ -794,30 +812,37 @@
return;
}
- // Animate all the task views into view
- TaskViewTransform transform = mStackAlgorithm.getStackTransform(mStack.getTaskCount() - 1,
- getInitialStackScroll());
- ctx.taskRect = transform.rect;
- ctx.stackRectSansPeek = mStackAlgorithm.mStackRectSansPeek;
- int childCount = getChildCount();
- for (int i = childCount - 1; i >= 0; i--) {
- TaskView tv = (TaskView) getChildAt(i);
- ctx.stackViewIndex = i;
- ctx.stackViewCount = childCount;
- ctx.isFrontMost = (i == (getChildCount() - 1));
- ctx.transform = mStackAlgorithm.getStackTransform(
- mStack.indexOfTask(tv.getTask()), getStackScroll());
- tv.startEnterRecentsAnimation(ctx);
- }
-
- // Add a runnable to the post animation ref counter to clear all the views
- ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
- @Override
- public void run() {
- // Start dozing
- mUIDozeTrigger.startDozing();
+ if (mStack.getTaskCount() > 0) {
+ // Animate all the task views into view
+ TaskViewTransform transform = new TaskViewTransform();
+ TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
+ mStack.getGroupIndexForTask(mStack.getFrontMostTask(), groupTaskIndex);
+ mStackAlgorithm.getStackTransform(groupTaskIndex.groupIndex, groupTaskIndex.taskIndex,
+ getInitialStackScroll(), transform);
+ ctx.taskRect = transform.rect;
+ ctx.stackRectSansPeek = mStackAlgorithm.mStackRectSansPeek;
+ int childCount = getChildCount();
+ for (int i = childCount - 1; i >= 0; i--) {
+ TaskView tv = (TaskView) getChildAt(i);
+ ctx.stackViewIndex = i;
+ ctx.stackViewCount = childCount;
+ ctx.isFrontMost = (i == (getChildCount() - 1));
+ ctx.transform = new TaskViewTransform();
+ mStack.getGroupIndexForTask(tv.getTask(), groupTaskIndex);
+ mStackAlgorithm.getStackTransform(groupTaskIndex.groupIndex, groupTaskIndex.taskIndex,
+ getStackScroll(), ctx.transform);
+ tv.startEnterRecentsAnimation(ctx);
}
- });
+
+ // Add a runnable to the post animation ref counter to clear all the views
+ ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ // Start dozing
+ mUIDozeTrigger.startDozing();
+ }
+ });
+ }
}
/** Requests this task stacks to start it's exit-recents animation. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index daa18bc..ab47757 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -19,7 +19,7 @@
import android.graphics.Rect;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.Utilities;
/* The layout logic for a TaskStackView */
public class TaskStackViewLayoutAlgorithm {
@@ -94,17 +94,11 @@
}
}
- /** Update/get the transform (creates a new TaskViewTransform) */
- public TaskViewTransform getStackTransform(int indexInStack, int stackScroll) {
- TaskViewTransform transform = new TaskViewTransform();
- return getStackTransform(indexInStack, stackScroll, transform);
- }
-
/** Update/get the transform */
- public TaskViewTransform getStackTransform(int indexInStack, int stackScroll,
- TaskViewTransform transformOut) {
+ public TaskViewTransform getStackTransform(int groupIndexInStack, int taskIndexInGroup,
+ int stackScroll, TaskViewTransform transformOut) {
// Return early if we have an invalid index
- if (indexInStack < 0) {
+ if (groupIndexInStack < 0) {
transformOut.reset();
return transformOut;
}
@@ -113,7 +107,7 @@
int numPeekCards = StackPeekNumCards;
float overlapHeight = StackOverlapPct * mTaskRect.height();
float peekHeight = StackPeekHeightPct * mStackRect.height();
- float t = ((indexInStack * overlapHeight) - stackScroll) / overlapHeight;
+ float t = ((groupIndexInStack * overlapHeight) - stackScroll) / overlapHeight;
float boundedT = Math.max(t, -(numPeekCards + 1));
// Set the scale relative to its position
@@ -133,6 +127,7 @@
} else {
transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
}
+ transformOut.translationY += 100 * taskIndexInGroup;
// Set the z translation
int minZ = mConfig.taskViewTranslationZMinPx;
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 304d45c..e186e2e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -22,7 +22,7 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewParent;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.Constants;
/* Handles touch events for a TaskStackView. */
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 6b06945..0d0fccc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -25,18 +25,14 @@
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.util.AttributeSet;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.model.Task;
@@ -61,6 +57,7 @@
Task mTask;
boolean mTaskDataLoaded;
boolean mIsFocused;
+ boolean mIsStub;
boolean mClipViewInStack;
Rect mTmpRect = new Rect();
Paint mLayerPaint = new Paint();
@@ -133,8 +130,8 @@
// Update the outline
Outline o = new Outline();
- o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight() -
- mConfig.taskViewShadowOutlineBottomInsetPx, mConfig.taskViewRoundedCornerRadiusPx);
+ o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(),
+ mConfig.taskViewRoundedCornerRadiusPx);
setOutline(o);
}
@@ -469,6 +466,21 @@
mBarView.disableHwLayers();
}
+ /** Sets the stubbed state of this task view. */
+ void setStubState(boolean isStub) {
+ if (!mIsStub && isStub) {
+ // This is now a stub task view, so clip to the bar height, hide the thumbnail
+ setClipBounds(new Rect(0, 0, getMeasuredWidth(), mBarView.getMeasuredHeight()));
+ mThumbnailView.setVisibility(View.INVISIBLE);
+ // Temporary
+ mBarView.mActivityDescription.setText("Stub");
+ } else if (mIsStub && !isStub) {
+ setClipBounds(null);
+ mThumbnailView.setVisibility(View.VISIBLE);
+ }
+ mIsStub = isStub;
+ }
+
/**
* Returns whether this view should be clipped, or any views below should clip against this
* view.
@@ -573,7 +585,9 @@
mThumbnailView.rebindToTask(mTask);
mBarView.rebindToTask(mTask);
// Rebind any listeners
- mBarView.mApplicationIcon.setOnClickListener(this);
+ if (Constants.DebugFlags.App.EnableTaskFiltering) {
+ mBarView.mApplicationIcon.setOnClickListener(this);
+ }
mBarView.mDismissButton.setOnClickListener(this);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
if (mConfig.developerOptionsEnabled) {
@@ -592,7 +606,9 @@
mThumbnailView.unbindFromTask();
mBarView.unbindFromTask();
// Unbind any listeners
- mBarView.mApplicationIcon.setOnClickListener(null);
+ if (Constants.DebugFlags.App.EnableTaskFiltering) {
+ mBarView.mApplicationIcon.setOnClickListener(null);
+ }
mBarView.mDismissButton.setOnClickListener(null);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
mBarView.mApplicationIcon.setOnLongClickListener(null);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
index fa97d2c..ec24198 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
@@ -17,7 +17,7 @@
package com.android.systemui.recents.views;
import android.graphics.Rect;
-import com.android.systemui.recents.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
/* Common code related to view animations */
public class ViewAnimation {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index 0a3fdef..a11e610 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -60,7 +60,7 @@
} else if (mDemoMode && command.equals(COMMAND_STATUS)) {
String volume = args.getString("volume");
if (volume != null) {
- int iconId = volume.equals("zen") ? R.drawable.stat_sys_ringer_zen
+ int iconId = volume.equals("important") ? R.drawable.stat_sys_zen_important
: volume.equals("silent") ? R.drawable.stat_sys_ringer_silent
: volume.equals("vibrate") ? R.drawable.stat_sys_ringer_vibrate
: 0;
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 c9606ca..bea5d49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -147,7 +147,7 @@
protected void onFinishInflate() {
super.onFinishInflate();
mHeader = (StatusBarHeaderView) findViewById(R.id.header);
- mHeader.getBackgroundView().setOnClickListener(this);
+ mHeader.setOnClickListener(this);
mHeader.setOverlayParent(this);
mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
mQsContainer = findViewById(R.id.quick_settings_container);
@@ -484,6 +484,9 @@
mInitialTouchY = event.getX();
mInitialTouchX = event.getY();
}
+ if (mExpandedHeight != 0) {
+ handleQsDown(event);
+ }
if (mQsTracking || mQsExpanded) {
onQsTouch(event);
if (!mConflictingQsExpansionGesture) {
@@ -498,6 +501,17 @@
return true;
}
+ private void handleQsDown(MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN
+ && shouldQuickSettingsIntercept(event.getX(), event.getY(), 0)) {
+ mQsTracking = true;
+ onQsExpansionStarted();
+ mInitialHeightOnTouch = mQsExpansionHeight;
+ mInitialTouchY = event.getX();
+ mInitialTouchX = event.getY();
+ }
+ }
+
@Override
protected boolean flingExpands(float vel, float vectorVel) {
boolean expands = super.flingExpands(vel, vectorVel);
@@ -1120,7 +1134,7 @@
@Override
public void onClick(View v) {
- if (v == mHeader.getBackgroundView()) {
+ if (v == mHeader) {
onQsExpansionStarted();
if (mQsExpanded) {
flingSettings(0 /* vel */, false /* expand */);
@@ -1251,4 +1265,9 @@
public boolean isQsExpanded() {
return mQsExpanded;
}
+
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return true;
+ }
}
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 f697098..6532b71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -538,15 +538,6 @@
R.id.notification_panel);
mNotificationPanel.setStatusBar(this);
- // make the header non-responsive to clicks
- mNotificationPanel.findViewById(R.id.header).setOnTouchListener(
- new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- return true; // e eats everything
- }
- });
-
if (!ActivityManager.isHighEndGfx()) {
mStatusBarWindow.setBackground(null);
mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
@@ -700,7 +691,6 @@
// updateCarrierLabelVisibility(false);
}
- mBatteryController.setStatusBarHeaderView(mHeader);
mFlashlightController = new FlashlightController(mContext);
// Set up the quick settings tile panel
@@ -720,6 +710,8 @@
mHeader.setUserInfoController(mUserInfoController);
mUserInfoController.reloadUserInfo();
+ mHeader.setBatteryController(mBatteryController);
+
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mBroadcastReceiver.onReceive(mContext,
new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
@@ -738,6 +730,14 @@
return mStatusBarView;
}
+ @Override
+ protected void setZenMode(int mode) {
+ super.setZenMode(mode);
+ if (mIconPolicy != null) {
+ mIconPolicy.setZenMode(mode);
+ }
+ }
+
private void startKeyguard() {
KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index b6f5ae0..186b618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -24,6 +24,7 @@
import android.content.IntentFilter;
import android.media.AudioManager;
import android.os.Handler;
+import android.provider.Settings.Global;
import android.util.Log;
import com.android.internal.telephony.IccCardConstants;
@@ -64,7 +65,7 @@
private boolean mVolumeVisible;
// zen mode
- private boolean mZen;
+ private int mZen;
// bluetooth device status
private boolean mBluetoothEnabled = false;
@@ -155,7 +156,7 @@
updateVolume();
}
- public void setZenMode(boolean zen) {
+ public void setZenMode(int zen) {
mZen = zen;
updateVolume();
}
@@ -202,21 +203,21 @@
private final void updateVolume() {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
final int ringerMode = audioManager.getRingerMode();
- final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT ||
- ringerMode == AudioManager.RINGER_MODE_VIBRATE ||
- mZen;
-
- final int iconId;
+ int iconId = 0;
String contentDescription = null;
- if (mZen) {
- iconId = R.drawable.stat_sys_ringer_zen;
- contentDescription = mContext.getString(R.string.zen_mode_title);
+ boolean visible = false;
+ if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
+ visible = true;
+ iconId = R.drawable.stat_sys_ringer_silent;
+ contentDescription = mContext.getString(R.string.accessibility_ringer_silent);
+ } else if (mZen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
+ visible = true;
+ iconId = R.drawable.stat_sys_zen_important;
+ contentDescription = mContext.getString(R.string.zen_important_interruptions);
} else if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
+ visible = true;
iconId = R.drawable.stat_sys_ringer_vibrate;
contentDescription = mContext.getString(R.string.accessibility_ringer_vibrate);
- } else {
- iconId = R.drawable.stat_sys_ringer_silent;
- contentDescription = mContext.getString(R.string.accessibility_ringer_silent);
}
if (visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 04b1443..5fbade1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -21,19 +21,16 @@
import android.os.HandlerThread;
import android.os.Looper;
-import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.tiles.AirplaneModeTile;
import com.android.systemui.qs.tiles.BluetoothTile;
-import com.android.systemui.qs.tiles.BugreportTile;
import com.android.systemui.qs.tiles.CastTile;
import com.android.systemui.qs.tiles.CellularTile;
import com.android.systemui.qs.tiles.ColorInversionTile;
import com.android.systemui.qs.tiles.FlashlightTile;
-import com.android.systemui.qs.tiles.LocationTile;
-import com.android.systemui.qs.tiles.NotificationsTile;
-import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.qs.tiles.HotspotTile;
+import com.android.systemui.qs.tiles.LocationTile;
+import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.qs.tiles.WifiTile;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.policy.BluetoothController;
@@ -93,7 +90,6 @@
mTiles.add(new ColorInversionTile(this));
mTiles.add(new CellularTile(this));
mTiles.add(new AirplaneModeTile(this));
- mTiles.add(new NotificationsTile(this));
mTiles.add(new RotationLockTile(this));
mTiles.add(new LocationTile(this));
mTiles.add(new CastTile(this));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 2119316..70a664f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.graphics.Outline;
import android.graphics.Rect;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -36,24 +37,20 @@
import com.android.systemui.qs.QSTile;
import com.android.systemui.settings.BrightnessController;
import com.android.systemui.settings.ToggleSlider;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.UserInfoController;
/**
* The view to manage the header area in the expanded status bar.
*/
-public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener {
-
- /**
- * How much the header expansion gets rubberbanded while expanding the panel.
- */
- private static final float EXPANSION_RUBBERBAND_FACTOR = 0.35f;
+public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener,
+ BatteryController.BatteryStateChangeCallback {
private boolean mExpanded;
private boolean mListening;
private boolean mOverscrolled;
private boolean mKeyguardShowing;
- private View mBackground;
private ViewGroup mSystemIconsContainer;
private View mSystemIconsSuperContainer;
private View mDateTime;
@@ -66,10 +63,9 @@
private View mBrightnessContainer;
private View mQsDetailHeader;
private View mEmergencyCallsOnly;
- private TextView mChargingInfo;
+ private TextView mBatteryLevel;
private boolean mShowEmergencyCallsOnly;
- private boolean mShowChargingInfo;
private boolean mKeyguardUserSwitcherShowing;
private int mCollapsedHeight;
@@ -84,6 +80,7 @@
private ActivityStarter mActivityStarter;
private BrightnessController mBrightnessController;
+ private BatteryController mBatteryController;
private QSPanel mQSPanel;
private final Rect mClipBounds = new Rect();
@@ -96,9 +93,9 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mBackground = findViewById(R.id.background);
mSystemIconsSuperContainer = findViewById(R.id.system_icons_super_container);
mSystemIconsContainer = (ViewGroup) findViewById(R.id.system_icons_container);
+ mSystemIconsSuperContainer.setOnClickListener(this);
mDateTime = findViewById(R.id.datetime);
mKeyguardCarrierText = findViewById(R.id.keyguard_carrier_text);
mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
@@ -112,7 +109,7 @@
mQsDetailHeader = findViewById(R.id.qs_detail_header);
mQsDetailHeader.setAlpha(0);
mEmergencyCallsOnly = findViewById(R.id.header_emergency_calls_only);
- mChargingInfo = (TextView) findViewById(R.id.header_charging_info);
+ mBatteryLevel = (TextView) findViewById(R.id.battery_level);
loadDimens();
updateVisibilities();
addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@@ -145,6 +142,10 @@
mActivityStarter = activityStarter;
}
+ public void setBatteryController(BatteryController batteryController) {
+ mBatteryController = batteryController;
+ }
+
public int getCollapsedHeight() {
return mKeyguardShowing ? mKeyguardHeight : mCollapsedHeight;
}
@@ -159,6 +160,7 @@
}
mListening = listening;
updateBrightnessControllerState();
+ updateBatteryListening();
}
public void setExpanded(boolean expanded, boolean overscrolled) {
@@ -220,7 +222,11 @@
private void updateVisibilities() {
boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
- mBackground.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE);
+ if (onKeyguardAndCollapsed) {
+ setBackground(null);
+ } else {
+ setBackgroundResource(R.drawable.notification_header_bg);
+ }
mDateTime.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE);
mKeyguardCarrierText.setVisibility(onKeyguardAndCollapsed ? View.VISIBLE : View.GONE);
mDate.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
@@ -235,38 +241,23 @@
}
mEmergencyCallsOnly.setVisibility(mExpanded && !mOverscrolled && mShowEmergencyCallsOnly
? VISIBLE : GONE);
- mChargingInfo.setVisibility(mExpanded && !mOverscrolled && mShowChargingInfo
- && !mShowEmergencyCallsOnly ? VISIBLE : GONE);
mMultiUserSwitch.setVisibility(mExpanded || !mKeyguardUserSwitcherShowing
? VISIBLE : GONE);
+ mBatteryLevel.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
}
private void updateSystemIconsLayoutParams() {
RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsSuperContainer.getLayoutParams();
- boolean systemIconsAboveClock = mExpanded && !mOverscrolled
- && mShowChargingInfo && !mShowEmergencyCallsOnly;
- lp.setMarginEnd(0);
- if (systemIconsAboveClock) {
- lp.addRule(ALIGN_PARENT_START);
- lp.removeRule(START_OF);
+ lp.addRule(RelativeLayout.START_OF, mExpanded
+ ? mSettingsButton.getId()
+ : mMultiUserSwitch.getId());
+ lp.removeRule(ALIGN_PARENT_START);
+ if (mMultiUserSwitch.getVisibility() == GONE) {
+ lp.setMarginEnd(mSystemIconsSwitcherHiddenExpandedMargin);
} else {
- lp.addRule(RelativeLayout.START_OF, mExpanded
- ? mSettingsButton.getId()
- : mMultiUserSwitch.getId());
- lp.removeRule(ALIGN_PARENT_START);
- if (mMultiUserSwitch.getVisibility() == GONE) {
- lp.setMarginEnd(mSystemIconsSwitcherHiddenExpandedMargin);
- }
+ lp.setMarginEnd(0);
}
mSystemIconsSuperContainer.setLayoutParams(lp);
-
- RelativeLayout.LayoutParams clockLp = (LayoutParams) mDateTime.getLayoutParams();
- if (systemIconsAboveClock) {
- clockLp.addRule(BELOW, mChargingInfo.getId());
- } else {
- clockLp.addRule(BELOW, mEmergencyCallsOnly.getId());
- }
- mDateTime.setLayoutParams(clockLp);
}
private void updateBrightnessControllerState() {
@@ -277,9 +268,24 @@
}
}
+ private void updateBatteryListening() {
+ if (mListening) {
+ mBatteryController.addStateChangedCallback(this);
+ } else {
+ mBatteryController.removeStateChangedCallback(this);
+ }
+ }
+
+ @Override
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+ mBatteryLevel.setText(getResources().getString(R.string.battery_level_template, level));
+ }
+
private void updateClickTargets() {
+ setClickable(!mKeyguardShowing || mExpanded);
mDateTime.setClickable(mExpanded);
mMultiUserSwitch.setClickable(mExpanded);
+ mSystemIconsSuperContainer.setClickable(mExpanded);
}
private void updateZTranslation() {
@@ -326,10 +332,6 @@
setOutline(mOutline);
}
- public View getBackgroundView() {
- return mBackground;
- }
-
public void attachSystemIcons(LinearLayout systemIcons) {
mSystemIconsContainer.addView(systemIcons);
mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
@@ -355,6 +357,7 @@
updateZTranslation();
updatePadding();
updateMultiUserSwitch();
+ updateClickTargets();
}
public void setUserInfoController(UserInfoController userInfoController) {
@@ -369,6 +372,8 @@
public void onClick(View v) {
if (v == mSettingsButton) {
startSettingsActivity();
+ } else if (v == mSystemIconsSuperContainer) {
+ startBatteryActivity();
}
}
@@ -376,6 +381,10 @@
mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS));
}
+ private void startBatteryActivity() {
+ mActivityStarter.startActivity(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY));
+ }
+
public void setQSPanel(QSPanel qsp) {
mQSPanel = qsp;
if (mQSPanel != null) {
@@ -392,22 +401,9 @@
mShowEmergencyCallsOnly = show;
if (mExpanded) {
updateVisibilities();
- updateSystemIconsLayoutParams();
}
}
- public void setShowChargingInfo(boolean showChargingInfo) {
- mShowChargingInfo = showChargingInfo;
- if (mExpanded) {
- updateVisibilities();
- updateSystemIconsLayoutParams();
- }
- }
-
- public void setChargingInfo(CharSequence chargingInfo) {
- mChargingInfo.setText(chargingInfo);
- }
-
public void setKeyguardUserSwitcherShowing(boolean showing) {
// STOPSHIP: NOT CALLED PROPERLY WHEN GOING TO FULL SHADE AND RETURNING!?!
mKeyguardUserSwitcherShowing = showing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 4cf66a3..ccfe18d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -36,29 +36,18 @@
public class BatteryController extends BroadcastReceiver {
private static final String TAG = "StatusBar.BatteryController";
- private ArrayList<BatteryStateChangeCallback> mChangeCallbacks =
- new ArrayList<BatteryStateChangeCallback>();
-
- private Context mContext;
- private StatusBarHeaderView mStatusBarHeaderView;
- private IBatteryStats mBatteryInfo;
+ private ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
private int mLevel;
private boolean mPluggedIn;
private boolean mCharging;
private boolean mCharged;
-
public interface BatteryStateChangeCallback {
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging);
}
public BatteryController(Context context) {
- mContext = context;
-
- mBatteryInfo = IBatteryStats.Stub.asInterface(
- ServiceManager.getService(BatteryStats.SERVICE_NAME));
-
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
context.registerReceiver(this, filter);
@@ -66,17 +55,19 @@
public void addStateChangedCallback(BatteryStateChangeCallback cb) {
mChangeCallbacks.add(cb);
+ cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
}
- public void setStatusBarHeaderView(StatusBarHeaderView statusBarHeaderView) {
- mStatusBarHeaderView = statusBarHeaderView;
- updateStatusBarHeaderView();
+ public void removeStateChangedCallback(BatteryStateChangeCallback cb) {
+ mChangeCallbacks.remove(cb);
}
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- mLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+ mLevel = (int)(100f
+ * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
+ / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
@@ -84,43 +75,9 @@
mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;
- updateStatusBarHeaderView();
for (BatteryStateChangeCallback cb : mChangeCallbacks) {
cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
}
}
}
-
- private void updateStatusBarHeaderView() {
- if (mStatusBarHeaderView != null) {
- mStatusBarHeaderView.setShowChargingInfo(mPluggedIn);
- mStatusBarHeaderView.setChargingInfo(computeChargingInfo());
- }
- }
-
- private String computeChargingInfo() {
- if (!mPluggedIn || !mCharged && !mCharging) {
- return mContext.getResources().getString(R.string.expanded_header_battery_not_charging);
- }
-
- if (mCharged) {
- return mContext.getResources().getString(R.string.expanded_header_battery_charged);
- }
-
- // Try fetching charging time from battery stats.
- try {
- long chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
- if (chargingTimeRemaining > 0) {
- String chargingTimeFormatted =
- Formatter.formatShortElapsedTime(mContext, chargingTimeRemaining);
- return mContext.getResources().getString(
- R.string.expanded_header_battery_charging_with_time, chargingTimeFormatted);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling IBatteryStats: ", e);
- }
-
- // Fall back to simple charging label.
- return mContext.getResources().getString(R.string.expanded_header_battery_charging);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index 4cf72a2..61902a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -22,14 +22,15 @@
public interface ZenModeController {
void addCallback(Callback callback);
void removeCallback(Callback callback);
- void setZen(boolean zen);
- boolean isZen();
+ void setZen(int zen);
+ int getZen();
void requestConditions(boolean request);
void setExitConditionId(Uri exitConditionId);
Uri getExitConditionId();
+ boolean hasNextAlarm();
public static class Callback {
- public void onZenChanged(boolean zen) {}
+ public void onZenChanged(int zen) {}
public void onExitConditionChanged(Uri exitConditionId) {}
public void onConditionsChanged(Condition[] conditions) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index da8fd32..7703966 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -28,6 +28,7 @@
import android.service.notification.ZenModeConfig;
import android.util.Slog;
+import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.qs.GlobalSetting;
import java.util.ArrayList;
@@ -44,15 +45,17 @@
private final GlobalSetting mConfigSetting;
private final INotificationManager mNoMan;
private final LinkedHashMap<Uri, Condition> mConditions = new LinkedHashMap<Uri, Condition>();
+ private final LockPatternUtils mUtils;
private boolean mRequesting;
public ZenModeControllerImpl(Context context, Handler handler) {
mContext = context;
+ mUtils = new LockPatternUtils(mContext);
mModeSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) {
@Override
protected void handleValueChanged(int value) {
- fireZenChanged(value != 0);
+ fireZenChanged(value);
}
};
mConfigSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE_CONFIG_ETAG) {
@@ -78,13 +81,13 @@
}
@Override
- public boolean isZen() {
- return mModeSetting.getValue() != 0;
+ public int getZen() {
+ return mModeSetting.getValue();
}
@Override
- public void setZen(boolean zen) {
- mModeSetting.setValue(zen ? 1 : 0);
+ public void setZen(int zen) {
+ mModeSetting.setValue(zen);
}
@Override
@@ -122,7 +125,12 @@
return null;
}
- private void fireZenChanged(boolean zen) {
+ @Override
+ public boolean hasNextAlarm() {
+ return mUtils.getNextAlarm() != null;
+ }
+
+ private void fireZenChanged(int zen) {
for (Callback cb : mCallbacks) {
cb.onZenChanged(zen);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
new file mode 100644
index 0000000..d339dd4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
@@ -0,0 +1,93 @@
+/*
+ * 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.volume;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+
+import java.util.Objects;
+
+public class SegmentedButtons extends LinearLayout {
+
+ private final Context mContext;
+ private final LayoutInflater mInflater;
+
+ private Callback mCallback;
+ private Object mSelectedValue;
+
+ public SegmentedButtons(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ mInflater = LayoutInflater.from(mContext);
+ setOrientation(HORIZONTAL);
+ }
+
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ public Object getSelectedValue() {
+ return mSelectedValue;
+ }
+
+ public void setSelectedValue(Object value) {
+ if (Objects.equals(value, mSelectedValue)) return;
+ mSelectedValue = value;
+ for (int i = 0; i < getChildCount(); i++) {
+ final View c = getChildAt(i);
+ final Object tag = c.getTag();
+ c.setSelected(Objects.equals(mSelectedValue, tag));
+ }
+ fireOnSelected();
+ }
+
+ public void addButton(int labelResId, Object value) {
+ final Button b = (Button) mInflater.inflate(R.layout.segmented_button, this, false);
+ b.setText(labelResId);
+ final LayoutParams lp = (LayoutParams) b.getLayoutParams();
+ if (getChildCount() == 0) {
+ lp.leftMargin = lp.rightMargin = 0; // first button has no margin
+ }
+ b.setLayoutParams(lp);
+ addView(b);
+ b.setTag(value);
+ b.setOnClickListener(mClick);
+ }
+
+ private void fireOnSelected() {
+ if (mCallback != null) {
+ mCallback.onSelected(mSelectedValue);
+ }
+ }
+
+ private final View.OnClickListener mClick = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setSelectedValue(v.getTag());
+ }
+ };
+
+ public interface Callback {
+ void onSelected(Object value);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index 53daaae..08216c4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -16,8 +16,6 @@
package com.android.systemui.volume;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
@@ -38,24 +36,21 @@
import android.media.session.MediaController;
import android.media.session.MediaController.VolumeInfo;
import android.net.Uri;
-import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
+import android.provider.Settings.Global;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
@@ -71,7 +66,8 @@
* @hide
*/
public class VolumePanel extends Handler {
- private static boolean LOGD = false;
+ private static final String TAG = "VolumePanel";
+ private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
@@ -88,8 +84,8 @@
private static final int MAX_VOLUME = 100;
private static final int FREE_DELAY = 10000;
private static final int TIMEOUT_DELAY = 3000;
+ private static final int TIMEOUT_DELAY_SHORT = 1500;
private static final int TIMEOUT_DELAY_EXPANDED = 10000;
- private static final float ICON_PULSE_SCALE = 1.3f;
private static final int MSG_VOLUME_CHANGED = 0;
private static final int MSG_FREE_RESOURCES = 1;
@@ -105,6 +101,7 @@
private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
private static final int MSG_LAYOUT_DIRECTION = 12;
private static final int MSG_ZEN_MODE_CHANGED = 13;
+ private static final int MSG_USER_ACTIVITY = 14;
// Pseudo stream type for master volume
private static final int STREAM_MASTER = -100;
@@ -114,10 +111,10 @@
protected final Context mContext;
private final AudioManager mAudioManager;
private final ZenModeController mZenController;
- private final Interpolator mFastOutSlowInInterpolator;
private boolean mRingIsSilent;
private boolean mVoiceCapable;
private boolean mZenModeCapable;
+ private boolean mZenPanelExpanded;
private int mTimeoutDelay = TIMEOUT_DELAY;
// True if we want to play tones on the system stream when the master stream is specified.
@@ -135,18 +132,12 @@
private final ViewGroup mPanel;
/** Contains the slider and its touchable icons */
private final ViewGroup mSliderPanel;
- /** The button that expands the dialog to show the zen panel */
- private final ImageView mExpandButton;
- /** Dummy divider icon that needs to vanish with the expand button */
- private final View mExpandDivider;
/** The zen mode configuration panel view stub */
private final ViewStub mZenPanelStub;
/** The zen mode configuration panel view, once inflated */
private ZenModePanel mZenPanel;
- /** Dummy divider icon that needs to vanish with the zen panel */
- private final View mZenPanelDivider;
- private ZenModePanel.Callback mZenPanelCallback;
+ private Callback mCallback;
/** Currently active stream that shows up at the top of the list of sliders */
private int mActiveStreamType = -1;
@@ -231,7 +222,6 @@
ViewGroup group;
ImageView icon;
SeekBar seekbarView;
- View seekbarContainer;
int iconRes;
int iconMuteRes;
}
@@ -280,13 +270,11 @@
public VolumePanel(Context context, ViewGroup parent, ZenModeController zenController) {
- mTag = String.format("VolumePanel%s.%08x", parent == null ? "Dialog" : "", hashCode());
+ mTag = String.format("%s.%s.%08x", TAG, parent == null ? "Dialog" : "Embed", hashCode());
mContext = context;
mParent = parent;
mZenController = zenController;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.fast_out_slow_in);
// For now, only show master volume if master volume is supported
final Resources res = context.getResources();
@@ -318,12 +306,11 @@
lp.token = null;
// Offset from the top
lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
- lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.volume_panel_width);
- lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
+ lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
lp.format = PixelFormat.TRANSLUCENT;
- lp.windowAnimations = R.style.Animation_VolumePanel;
+ lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
+ lp.gravity = Gravity.TOP;
window.setAttributes(lp);
- window.setGravity(Gravity.TOP);
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.requestFeature(Window.FEATURE_NO_TITLE);
window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
@@ -337,7 +324,7 @@
public void onDismiss(DialogInterface dialog) {
mActiveStreamType = -1;
mAudioManager.forceVolumeControlStream(mActiveStreamType);
- collapse();
+ setZenPanelVisible(false);
}
});
@@ -362,19 +349,14 @@
}
mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
- mExpandButton = (ImageView) mView.findViewById(com.android.systemui.R.id.expand_button);
- mExpandDivider = mView.findViewById(com.android.systemui.R.id.expand_button_divider);
mZenPanelStub = (ViewStub)mView.findViewById(com.android.systemui.R.id.zen_panel_stub);
- mZenPanelDivider = mView.findViewById(com.android.systemui.R.id.zen_panel_divider);
mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
mZenModeCapable = !useMasterVolume && mZenController != null;
- mZenPanelDivider.setVisibility(View.GONE);
- mExpandButton.setOnClickListener(mClickListener);
- updateZenMode(mZenController == null ? false : mZenController.isZen());
+ updateZenMode(mZenController != null ? mZenController.getZen() : Global.ZEN_MODE_OFF);
mZenController.addCallback(mZenCallback);
final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
@@ -502,17 +484,7 @@
toggle(sc);
}
});
- sc.icon.setOnLongClickListener(new OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- resetTimeout();
- longToggle(sc);
- return true;
- }
- });
}
- sc.seekbarContainer =
- sc.group.findViewById(com.android.systemui.R.id.seekbar_container);
sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
@@ -533,29 +505,20 @@
}
}
- private void longToggle(StreamControl sc) {
- if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
- mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
- postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
- } else {
- mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
- postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI); // disable the slider
- }
- }
-
private void reorderSliders(int activeStreamType) {
mSliderPanel.removeAllViews();
final StreamControl active = mStreamControls.get(activeStreamType);
if (active == null) {
- Log.e("VolumePanel", "Missing stream type! - " + activeStreamType);
+ Log.e(TAG, "Missing stream type! - " + activeStreamType);
mActiveStreamType = -1;
} else {
mSliderPanel.addView(active.group);
mActiveStreamType = activeStreamType;
active.group.setVisibility(View.VISIBLE);
updateSlider(active);
- updateZenMode(mZenController == null ? false : mZenController.isZen());
+ updateTimeoutDelay();
+ setZenPanelVisible(isNotificationOrRing(mActiveStreamType));
}
}
@@ -575,6 +538,7 @@
private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
final boolean wasEnabled = sc.seekbarView.isEnabled();
+ final boolean isRinger = isNotificationOrRing(sc.streamType);
if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
// never disable touch interactions for remote playback, the muting is not tied to
// the state of the phone.
@@ -583,40 +547,37 @@
(sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
(sConfirmSafeVolumeDialog != null)) {
sc.seekbarView.setEnabled(false);
- } else if (isNotificationOrRing(sc.streamType)
- && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
+ } else if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
sc.seekbarView.setEnabled(false);
+ sc.icon.setEnabled(false);
+ sc.icon.setClickable(false);
} else {
sc.seekbarView.setEnabled(true);
+ sc.icon.setEnabled(true);
}
- // pulse the ringer icon when the disabled slider is touched in silent mode
- if (sc.icon.isClickable() && wasEnabled != sc.seekbarView.isEnabled()) {
+ // show the silent hint when the disabled slider is touched in silent mode
+ if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
if (sc.seekbarView.isEnabled()) {
- sc.seekbarContainer.setOnTouchListener(null);
+ sc.group.setOnTouchListener(null);
+ sc.icon.setClickable(true);
} else {
- sc.seekbarContainer.setOnTouchListener(new View.OnTouchListener() {
+ final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
resetTimeout();
- pulseIcon(sc.icon);
+ showSilentHint();
return false;
}
- });
+ };
+ sc.group.setOnTouchListener(showHintOnTouch);
}
}
}
- private void pulseIcon(final ImageView icon) {
- if (icon.getScaleX() != 1) return; // already running
- icon.animate().cancel();
- icon.animate().scaleX(ICON_PULSE_SCALE).scaleY(ICON_PULSE_SCALE)
- .setInterpolator(mFastOutSlowInInterpolator)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- icon.animate().scaleX(1).scaleY(1).setListener(null);
- }
- });
+ private void showSilentHint() {
+ if (mZenPanel != null) {
+ mZenPanel.showSilentHint();
+ }
}
private static boolean isNotificationOrRing(int streamType) {
@@ -624,47 +585,50 @@
|| streamType == AudioManager.STREAM_NOTIFICATION;
}
- public void setZenModePanelCallback(ZenModePanel.Callback callback) {
- mZenPanelCallback = callback;
+ public void setCallback(Callback callback) {
+ mCallback = callback;
}
- private void expand() {
- if (LOGD) Log.d(mTag, "expand mZenPanel=" + mZenPanel);
- if (mZenPanel == null) {
- mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
- final boolean isDialog = mDialog != null;
- mZenPanel.init(mZenController, isDialog ? 'D' : 'E', isDialog);
- mZenPanel.setCallback(new ZenModePanel.Callback() {
- @Override
- public void onMoreSettings() {
- if (mZenPanelCallback != null) {
- mZenPanelCallback.onMoreSettings();
- }
- }
-
- @Override
- public void onInteraction() {
- resetTimeout();
- if (mZenPanelCallback != null) {
- mZenPanelCallback.onInteraction();
- }
- }
- });
- }
- mZenPanel.setVisibility(View.VISIBLE);
- mZenPanelDivider.setVisibility(View.VISIBLE);
- mTimeoutDelay = TIMEOUT_DELAY_EXPANDED;
- resetTimeout();
+ private void updateTimeoutDelay() {
+ mTimeoutDelay = mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
+ : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED : TIMEOUT_DELAY;
}
- private void collapse() {
- if (LOGD) Log.d(mTag, "collapse mZenPanel=" + mZenPanel);
- if (mZenPanel != null) {
- mZenPanel.setVisibility(View.GONE);
+ private void setZenPanelVisible(boolean visible) {
+ if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
+ if (visible) {
+ if (mZenPanel == null) {
+ mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
+ mZenPanel.init(mZenController, mDialog != null ? 'D' : 'E');
+ mZenPanel.setCallback(new ZenModePanel.Callback() {
+ @Override
+ public void onMoreSettings() {
+ if (mCallback != null) {
+ mCallback.onZenSettings();
+ }
+ }
+
+ @Override
+ public void onInteraction() {
+ resetTimeout();
+ }
+
+ @Override
+ public void onExpanded(boolean expanded) {
+ if (mZenPanelExpanded == expanded) return;
+ mZenPanelExpanded = expanded;
+ updateTimeoutDelay();
+ resetTimeout();
+ }
+ });
+ }
+ mZenPanel.setVisibility(View.VISIBLE);
+ resetTimeout();
+ } else {
+ if (mZenPanel != null) {
+ mZenPanel.setVisibility(View.GONE);
+ }
}
- mZenPanelDivider.setVisibility(View.GONE);
- mTimeoutDelay = TIMEOUT_DELAY;
- resetTimeout();
}
public void updateStates() {
@@ -675,25 +639,14 @@
}
}
- private void updateZenMode(boolean zen) {
- if (mZenModeCapable) {
- final boolean show = isNotificationOrRing(mActiveStreamType);
- mExpandButton.setVisibility(show ? View.VISIBLE : View.GONE);
- mExpandDivider.setVisibility(show ? View.VISIBLE : View.GONE);
- mExpandButton.setImageResource(zen ? com.android.systemui.R.drawable.ic_vol_zen_on
- : com.android.systemui.R.drawable.ic_vol_zen_off);
- if (show && !zen) {
- collapse();
- }
- } else {
- mExpandButton.setVisibility(View.GONE);
- mExpandDivider.setVisibility(View.GONE);
- }
+ private void updateZenMode(int zen) {
+ final boolean show = mZenModeCapable && isNotificationOrRing(mActiveStreamType);
+ setZenPanelVisible(show);
}
- public void postZenModeChanged(boolean zen) {
+ public void postZenModeChanged(int zen) {
removeMessages(MSG_ZEN_MODE_CHANGED);
- obtainMessage(MSG_ZEN_MODE_CHANGED, zen ? 1 : 0, 0).sendToTarget();
+ obtainMessage(MSG_ZEN_MODE_CHANGED, zen).sendToTarget();
}
public void postVolumeChanged(int streamType, int flags) {
@@ -955,8 +908,8 @@
}
// Pulse the slider icon if an adjustment was suppressed due to silent mode.
- if (sc != null && (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
- pulseIcon(sc.icon);
+ if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
+ showSilentHint();
}
}
@@ -1233,15 +1186,23 @@
break;
case MSG_ZEN_MODE_CHANGED:
- updateZenMode(msg.arg1 != 0);
+ updateZenMode(msg.arg1);
+ break;
+
+ case MSG_USER_ACTIVITY:
+ if (mCallback != null) {
+ mCallback.onInteraction();
+ }
break;
}
}
- public void resetTimeout() {
+ private void resetTimeout() {
if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis());
removeMessages(MSG_TIMEOUT);
sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
+ removeMessages(MSG_USER_ACTIVITY);
+ sendEmptyMessage(MSG_USER_ACTIVITY);
}
private void forceTimeout() {
@@ -1273,29 +1234,8 @@
}
};
- private final View.OnClickListener mClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (v == mExpandButton && mZenController != null) {
- final boolean newZen = !mZenController.isZen();
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- mZenController.setZen(newZen);
- }
- });
- if (newZen) {
- expand();
- } else {
- collapse();
- }
- }
- resetTimeout();
- }
- };
-
private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
- public void onZenChanged(boolean zen) {
+ public void onZenChanged(int zen) {
postZenModeChanged(zen);
}
};
@@ -1305,4 +1245,9 @@
onRemoteVolumeUpdateIfShown();
}
};
+
+ public interface Callback {
+ void onZenSettings();
+ void onInteraction();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index c1f92ff..51be833 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -1,8 +1,8 @@
package com.android.systemui.volume;
+import android.app.ActivityManagerNative;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Resources;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.media.IRemoteVolumeController;
@@ -17,8 +17,8 @@
import android.provider.Settings;
import android.util.Log;
-import com.android.systemui.R;
import com.android.systemui.SystemUI;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
@@ -80,17 +80,19 @@
private void initPanel() {
mPanel = new VolumePanel(mContext, null, new ZenModeControllerImpl(mContext, mHandler));
- final int delay = mContext.getResources().getInteger(R.integer.feedback_start_delay);
- mPanel.setZenModePanelCallback(new ZenModePanel.Callback() {
+ mPanel.setCallback(new VolumePanel.Callback() {
@Override
- public void onMoreSettings() {
+ public void onZenSettings() {
mHandler.removeCallbacks(mStartZenSettings);
- mHandler.postDelayed(mStartZenSettings, delay);
+ mHandler.post(mStartZenSettings);
}
@Override
public void onInteraction() {
- mDialogPanel.resetTimeout();
+ final KeyguardViewMediator kvm = getComponent(KeyguardViewMediator.class);
+ if (kvm != null) {
+ kvm.userActivity();
+ }
}
});
mDialogPanel = mPanel;
@@ -108,6 +110,11 @@
@Override
public void run() {
mDialogPanel.postDismiss();
+ try {
+ // Dismiss the lock screen when Settings starts.
+ ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+ } catch (RemoteException e) {
+ }
final Intent intent = ZenModePanel.ZEN_SETTINGS;
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 9917944..afa5bfe 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -16,6 +16,8 @@
package com.android.systemui.volume;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -25,6 +27,7 @@
import android.os.Looper;
import android.os.Message;
import android.provider.Settings;
+import android.provider.Settings.Global;
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.util.AttributeSet;
@@ -32,7 +35,8 @@
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView;
@@ -47,12 +51,19 @@
import java.util.Objects;
public class ZenModePanel extends LinearLayout {
- private static final boolean DEBUG = false;
+ private static final String TAG = "ZenModePanel";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final int[] MINUTE_BUCKETS = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
+ private static final int[] MINUTE_BUCKETS = DEBUG
+ ? new int[] { 1, 2, 5, 15, 30, 45, 60, 120, 180, 240, 480 }
+ : new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
private static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0];
private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1];
private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60);
+ private static final int FOREVER_CONDITION_INDEX = 0;
+ private static final int TIME_CONDITION_INDEX = 1;
+ private static final int FIRST_CONDITION_INDEX = 2;
+ private static final float SILENT_HINT_PULSE_SCALE = 1.1f;
private static final int SECONDS_MS = 1000;
private static final int MINUTES_MS = 60 * SECONDS_MS;
@@ -63,73 +74,110 @@
private final LayoutInflater mInflater;
private final H mHandler = new H();
private final Favorites mFavorites;
+ private final Interpolator mFastOutSlowInInterpolator;
private char mLogTag = '?';
private String mTag;
- private LinearLayout mConditions;
+
+ private SegmentedButtons mZenButtons;
+ private View mZenSubhead;
+ private TextView mZenSubheadCollapsed;
+ private TextView mZenSubheadExpanded;
private View mMoreSettings;
+ private LinearLayout mZenConditions;
+ private View mAlarmWarning;
+
private Callback mCallback;
private ZenModeController mController;
private boolean mRequestingConditions;
private Uri mExitConditionId;
private int mBucketIndex = -1;
- private boolean mShowing;
+ private boolean mExpanded;
+ private int mAttachedZen;
+ private String mExitConditionText;
public ZenModePanel(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
mFavorites = new Favorites();
mInflater = LayoutInflater.from(new ContextThemeWrapper(context, R.style.QSWhiteTheme));
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
+ android.R.interpolator.fast_out_slow_in);
updateTag();
if (DEBUG) Log.d(mTag, "new ZenModePanel");
}
private void updateTag() {
- mTag = "ZenModePanel/" + mLogTag + "/" + Integer.toHexString(System.identityHashCode(this));
+ mTag = TAG + "/" + mLogTag + "/" + Integer.toHexString(System.identityHashCode(this));
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mConditions = (LinearLayout) findViewById(android.R.id.content);
- mMoreSettings = findViewById(android.R.id.button2);
+
+ mZenButtons = (SegmentedButtons) findViewById(R.id.zen_buttons);
+ mZenButtons.addButton(R.string.interruption_level_none, Global.ZEN_MODE_NO_INTERRUPTIONS);
+ mZenButtons.addButton(R.string.interruption_level_priority,
+ Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ mZenButtons.addButton(R.string.interruption_level_all, Global.ZEN_MODE_OFF);
+ mZenButtons.setCallback(mZenButtonsCallback);
+
+ mZenSubhead = findViewById(R.id.zen_subhead);
+
+ mZenSubheadCollapsed = (TextView) findViewById(R.id.zen_subhead_collapsed);
+ mZenSubheadCollapsed.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setExpanded(true);
+ fireInteraction();
+ }
+ });
+
+ mZenSubheadExpanded = (TextView) findViewById(R.id.zen_subhead_expanded);
+ mZenSubheadExpanded.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setExpanded(false);
+ fireInteraction();
+ }
+ });
+
+ mMoreSettings = findViewById(R.id.zen_more_settings);
mMoreSettings.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
fireMoreSettings();
+ fireInteraction();
}
});
+
+ mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions);
+
+ mAlarmWarning = findViewById(R.id.zen_alarm_warning);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (DEBUG) Log.d(mTag, "onAttachedToWindow");
- final ViewGroup p = (ViewGroup) getParent();
- updateShowing(p != null && p.getVisibility() == VISIBLE);
+ mAttachedZen = getSelectedZen(-1);
+ refreshExitConditionText();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
- updateShowing(false);
+ mAttachedZen = -1;
+ setExpanded(false);
}
- @Override
- public void setVisibility(int visibility) {
- super.setVisibility(visibility);
- final boolean vis = visibility == VISIBLE;
- updateShowing(isAttachedToWindow() && vis);
- }
-
- private void updateShowing(boolean showing) {
- if (showing == mShowing) return;
- mShowing = showing;
- if (DEBUG) Log.d(mTag, "mShowing=" + mShowing);
- if (mController != null) {
- setRequestingConditions(mShowing);
- }
+ private void setExpanded(boolean expanded) {
+ if (expanded == mExpanded) return;
+ mExpanded = expanded;
+ updateWidgets();
+ setRequestingConditions(mExpanded);
+ fireExpanded();
}
/** Start or stop requesting relevant zen mode exit conditions */
@@ -149,24 +197,40 @@
timeCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]);
}
if (DEBUG) Log.d(mTag, "Initial bucket index: " + mBucketIndex);
- bind(timeCondition, mConditions.getChildAt(0));
- handleUpdateConditions(new Condition[0]);
+ handleUpdateConditions(new Condition[0]); // ensures forever exists
+ bind(timeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
+ checkForDefault();
} else {
- mConditions.removeAllViews();
+ mZenConditions.removeAllViews();
}
}
- public void init(ZenModeController controller, char logTag, boolean moreSettings) {
+ public void init(ZenModeController controller, char logTag) {
mController = controller;
mLogTag = logTag;
updateTag();
- mExitConditionId = mController.getExitConditionId();
+ setExitConditionId(mController.getExitConditionId());
+ refreshExitConditionText();
+ mAttachedZen = getSelectedZen(-1);
+ handleUpdateZen(mController.getZen());
if (DEBUG) Log.d(mTag, "init mExitConditionId=" + mExitConditionId);
- mMoreSettings.setVisibility(moreSettings ? VISIBLE : GONE);
- mConditions.removeAllViews();
+ mZenConditions.removeAllViews();
mController.addCallback(mZenCallback);
- if (mShowing) {
- setRequestingConditions(true);
+ }
+
+ private void setExitConditionId(Uri exitConditionId) {
+ if (Objects.equals(mExitConditionId, exitConditionId)) return;
+ mExitConditionId = exitConditionId;
+ refreshExitConditionText();
+ }
+
+ private void refreshExitConditionText() {
+ if (mExitConditionId == null) {
+ mExitConditionText = mContext.getString(R.string.zen_mode_forever);
+ } else if (ZenModeConfig.isValidCountdownConditionId(mExitConditionId)) {
+ mExitConditionText = parseExistingTimeCondition(mExitConditionId).summary;
+ } else {
+ mExitConditionText = "(until condition ends)"; // TODO persist current description
}
}
@@ -174,6 +238,59 @@
mCallback = callback;
}
+ public void showSilentHint() {
+ if (DEBUG) Log.d(mTag, "showSilentHint");
+ if (mZenButtons == null || mZenButtons.getChildCount() == 0) return;
+ final View noneButton = mZenButtons.getChildAt(0);
+ if (noneButton.getScaleX() != 1) return; // already running
+ noneButton.animate().cancel();
+ noneButton.animate().scaleX(SILENT_HINT_PULSE_SCALE).scaleY(SILENT_HINT_PULSE_SCALE)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ noneButton.animate().scaleX(1).scaleY(1).setListener(null);
+ }
+ });
+ }
+
+ private void handleUpdateZen(int zen) {
+ if (mAttachedZen != -1 && mAttachedZen != zen) {
+ setExpanded(zen != Global.ZEN_MODE_OFF);
+ mAttachedZen = zen;
+ }
+ mZenButtons.setSelectedValue(zen);
+ updateWidgets();
+ }
+
+ private int getSelectedZen(int defValue) {
+ final Object zen = mZenButtons.getSelectedValue();
+ return zen != null ? (Integer) zen : defValue;
+ }
+
+ private void updateWidgets() {
+ final int zen = getSelectedZen(Global.ZEN_MODE_OFF);
+ final boolean zenOff = zen == Global.ZEN_MODE_OFF;
+ final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+ mZenSubhead.setVisibility(!zenOff ? VISIBLE : GONE);
+ mZenSubheadExpanded.setVisibility(mExpanded ? VISIBLE : GONE);
+ mZenSubheadCollapsed.setVisibility(!mExpanded ? VISIBLE : GONE);
+ mMoreSettings.setVisibility(zenImportant && mExpanded ? VISIBLE : GONE);
+ mZenConditions.setVisibility(!zenOff && mExpanded ? VISIBLE : GONE);
+ mAlarmWarning.setVisibility(zenNone && mExpanded && mController != null
+ && mController.hasNextAlarm() ? VISIBLE : GONE);
+
+ if (zenNone) {
+ mZenSubheadExpanded.setText(R.string.zen_no_interruptions);
+ mZenSubheadCollapsed.setText(mExitConditionText);
+ } else if (zenImportant) {
+ mZenSubheadExpanded.setText(R.string.zen_important_interruptions);
+ mZenSubheadCollapsed.setText(mExitConditionText);
+ }
+ }
+
private Condition parseExistingTimeCondition(Uri conditionId) {
final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
if (time == 0) return null;
@@ -200,23 +317,23 @@
private void handleUpdateConditions(Condition[] conditions) {
final int newCount = conditions == null ? 0 : conditions.length;
- for (int i = mConditions.getChildCount() - 1; i > newCount; i--) {
- mConditions.removeViewAt(i);
+ if (DEBUG) Log.d(mTag, "handleUpdateConditions newCount=" + newCount);
+ for (int i = mZenConditions.getChildCount(); i >= newCount + FIRST_CONDITION_INDEX; i--) {
+ mZenConditions.removeViewAt(i);
}
+ bind(null, mZenConditions.getChildAt(FOREVER_CONDITION_INDEX));
for (int i = 0; i < newCount; i++) {
- bind(conditions[i], mConditions.getChildAt(i + 1));
+ bind(conditions[i], mZenConditions.getChildAt(FIRST_CONDITION_INDEX + i));
}
- bind(null, mConditions.getChildAt(newCount + 1));
- checkForDefault();
}
private ConditionTag getConditionTagAt(int index) {
- return (ConditionTag) mConditions.getChildAt(index).getTag();
+ return (ConditionTag) mZenConditions.getChildAt(index).getTag();
}
private void checkForDefault() {
// are we left without anything selected? if so, set a default
- for (int i = 0; i < mConditions.getChildCount(); i++) {
+ for (int i = 0; i < mZenConditions.getChildCount(); i++) {
if (getConditionTagAt(i).rb.isChecked()) {
return;
}
@@ -224,19 +341,19 @@
if (DEBUG) Log.d(mTag, "Selecting a default");
final int favoriteIndex = mFavorites.getMinuteIndex();
if (favoriteIndex == -1) {
- getConditionTagAt(mConditions.getChildCount() - 1).rb.setChecked(true);
+ getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true);
} else {
final Condition c = newTimeCondition(MINUTE_BUCKETS[favoriteIndex]);
mBucketIndex = favoriteIndex;
- bind(c, mConditions.getChildAt(0));
- getConditionTagAt(0).rb.setChecked(true);
+ bind(c, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
+ getConditionTagAt(TIME_CONDITION_INDEX).rb.setChecked(true);
}
}
private void handleExitConditionChanged(Uri exitCondition) {
- mExitConditionId = exitCondition;
+ setExitConditionId(exitCondition);
if (DEBUG) Log.d(mTag, "handleExitConditionChanged " + mExitConditionId);
- final int N = mConditions.getChildCount();
+ final int N = mZenConditions.getChildCount();
for (int i = 0; i < N; i++) {
final ConditionTag tag = getConditionTagAt(i);
tag.rb.setChecked(Objects.equals(tag.conditionId, exitCondition));
@@ -249,7 +366,7 @@
if (convertView == null) {
row = mInflater.inflate(R.layout.zen_mode_condition, this, false);
if (DEBUG) Log.d(mTag, "Adding new condition view for: " + condition);
- mConditions.addView(row);
+ mZenConditions.addView(row);
} else {
row = convertView;
}
@@ -264,9 +381,9 @@
tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (mShowing && isChecked) {
+ if (mExpanded && isChecked) {
if (DEBUG) Log.d(mTag, "onCheckedChanged " + tag.conditionId);
- final int N = mConditions.getChildCount();
+ final int N = mZenConditions.getChildCount();
for (int i = 0; i < N; i++) {
ConditionTag childTag = getConditionTagAt(i);
if (childTag == tag) continue;
@@ -284,7 +401,7 @@
title.setText(condition.summary);
}
title.setEnabled(enabled);
- title.setAlpha(enabled ? 1 : .5f);
+ title.setAlpha(enabled ? 1 : .4f);
final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
button1.setOnClickListener(new OnClickListener() {
@Override
@@ -365,7 +482,7 @@
if (mController != null) {
mController.setExitConditionId(conditionId);
}
- mExitConditionId = conditionId;
+ setExitConditionId(conditionId);
if (conditionId == null) {
mFavorites.setMinuteIndex(-1);
} else if (ZenModeConfig.isValidCountdownConditionId(conditionId) && mBucketIndex != -1) {
@@ -385,8 +502,18 @@
}
}
+ private void fireExpanded() {
+ if (mCallback != null) {
+ mCallback.onExpanded(mExpanded);
+ }
+ }
+
private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
@Override
+ public void onZenChanged(int zen) {
+ mHandler.obtainMessage(H.UPDATE_ZEN, zen, 0).sendToTarget();
+ }
+ @Override
public void onConditionsChanged(Condition[] conditions) {
mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget();
}
@@ -400,6 +527,7 @@
private final class H extends Handler {
private static final int UPDATE_CONDITIONS = 1;
private static final int EXIT_CONDITION_CHANGED = 2;
+ private static final int UPDATE_ZEN = 3;
private H() {
super(Looper.getMainLooper());
@@ -409,8 +537,11 @@
public void handleMessage(Message msg) {
if (msg.what == UPDATE_CONDITIONS) {
handleUpdateConditions((Condition[])msg.obj);
+ checkForDefault();
} else if (msg.what == EXIT_CONDITION_CHANGED) {
handleExitConditionChanged((Uri)msg.obj);
+ } else if (msg.what == UPDATE_ZEN) {
+ handleUpdateZen(msg.arg1);
}
}
}
@@ -418,6 +549,7 @@
public interface Callback {
void onMoreSettings();
void onInteraction();
+ void onExpanded(boolean expanded);
}
// used as the view tag on condition rows
@@ -466,4 +598,15 @@
return Math.max(-1, Math.min(MINUTE_BUCKETS.length - 1, index));
}
}
+
+ private final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() {
+ @Override
+ public void onSelected(Object value) {
+ if (value != null && mZenButtons.isShown()) {
+ if (DEBUG) Log.d(mTag, "mZenButtonsCallback selected=" + value);
+ mController.setZen((Integer) value);
+ mController.setExitConditionId(null);
+ }
+ }
+ };
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 3c996b6..b31a3d6 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -2925,7 +2925,8 @@
private void writeApkToBackup(PackageInfo pkg, BackupDataOutput output) {
// Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
- final String appSourceDir = pkg.applicationInfo.sourceDir;
+ // TODO: handle backing up split APKs
+ final String appSourceDir = pkg.applicationInfo.getBaseCodePath();
final String apkDir = new File(appSourceDir).getParent();
FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
apkDir, appSourceDir, output);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6474047..1da8123 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7258,6 +7258,7 @@
rti.stackId = tr.stack != null ? tr.stack.mStackId : -1;
rti.userId = tr.userId;
rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription);
+ rti.firstActiveTime = tr.firstActiveTime;
rti.lastActiveTime = tr.lastActiveTime;
return rti;
}
@@ -9370,8 +9371,11 @@
mWindowManager.setAppFullscreen(token, false);
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
return true;
+ } else {
+ r.task.stack.mReturningActivityOptions = options;
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+ return false;
}
- return false;
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -9434,6 +9438,17 @@
}
}
+ @Override
+ public boolean isTopOfTask(IBinder token) {
+ synchronized (this) {
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ throw new IllegalArgumentException();
+ }
+ return r.task.getTopActivity() == r;
+ }
+ }
+
public final void enterSafeMode() {
synchronized(this) {
// It only makes sense to do this before the system is ready
@@ -16534,11 +16549,9 @@
}
cleanUpApplicationRecordLocked(app, false, true, -1);
mRemovedProcesses.remove(i);
-
+
if (app.persistent) {
- if (app.persistent) {
- addAppLocked(app.info, false, null /* ABI override */);
- }
+ addAppLocked(app.info, false, null /* ABI override */);
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 545423b..ca518f3 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -209,7 +209,7 @@
// Activity.onTranslucentConversionComplete(false). If a timeout occurs prior to the last
// background activity being drawn then the same call will be made with a true value.
ActivityRecord mTranslucentActivityWaiting = null;
- ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent =
+ private ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent =
new ArrayList<ActivityRecord>();
// Options passed from the caller of the convertToTranslucent to the activity that will
// appear below it.
@@ -1203,7 +1203,17 @@
// else to do here.
if (DEBUG_VISBILITY) Slog.v(TAG, "Skipping: already visible at " + r);
r.stopFreezingScreenLocked(false);
-
+ try {
+ if (mReturningActivityOptions != null) {
+ if (activityNdx > 0) {
+ ActivityRecord under = activities.get(activityNdx - 1);
+ under.app.thread.scheduleOnNewActivityOptions(under.appToken,
+ mReturningActivityOptions);
+ }
+ mReturningActivityOptions = null;
+ }
+ } catch(RemoteException e) {
+ }
} else if (onlyThisProcess == null) {
// This activity is not currently visible, but is running.
// Tell it to become visible.
@@ -1291,6 +1301,12 @@
}
}
}
+
+ if (mTranslucentActivityWaiting != null &&
+ mUndrawnActivitiesBelowTopTranslucent.isEmpty()) {
+ // Nothing is getting drawn or everything was already visible, don't wait for timeout.
+ notifyActivityDrawnLocked(null);
+ }
}
void convertToTranslucent(ActivityRecord r, ActivityOptions options) {
diff --git a/services/core/java/com/android/server/am/BaseErrorDialog.java b/services/core/java/com/android/server/am/BaseErrorDialog.java
old mode 100644
new mode 100755
index 6ede8f8..35bdee0
--- a/services/core/java/com/android/server/am/BaseErrorDialog.java
+++ b/services/core/java/com/android/server/am/BaseErrorDialog.java
@@ -27,6 +27,11 @@
import android.widget.Button;
class BaseErrorDialog extends AlertDialog {
+ private static final int ENABLE_BUTTONS = 0;
+ private static final int DISABLE_BUTTONS = 1;
+
+ private boolean mConsuming = true;
+
public BaseErrorDialog(Context context) {
super(context, com.android.internal.R.style.Theme_Dialog_AppError);
@@ -41,8 +46,8 @@
public void onStart() {
super.onStart();
- setEnabled(false);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(0), 1000);
+ mHandler.sendEmptyMessage(DISABLE_BUTTONS);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(ENABLE_BUTTONS), 1000);
}
public boolean dispatchKeyEvent(KeyEvent event) {
@@ -71,12 +76,12 @@
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
- if (msg.what == 0) {
+ if (msg.what == ENABLE_BUTTONS) {
mConsuming = false;
setEnabled(true);
+ } else if (msg.what == DISABLE_BUTTONS) {
+ setEnabled(false);
}
}
};
-
- private boolean mConsuming = true;
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index d221f96..c9890cd3cd 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -57,6 +57,7 @@
private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
private static final String ATTR_USERID = "user_id";
private static final String ATTR_TASKTYPE = "task_type";
+ private static final String ATTR_FIRSTACTIVETIME = "first_active_time";
private static final String ATTR_LASTACTIVETIME = "last_active_time";
private static final String ATTR_LASTDESCRIPTION = "last_description";
private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
@@ -75,6 +76,7 @@
Intent affinityIntent; // Intent of affinity-moved activity that started this task.
ComponentName origActivity; // The non-alias activity component of the intent.
ComponentName realActivity; // The actual activity component that started the task.
+ long firstActiveTime; // First time this task was active.
long lastActiveTime; // Last time this task was active, including sleep.
boolean rootWasReset; // True if the intent at the root of the task had
// the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
@@ -147,8 +149,8 @@
TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent,
String _affinity, ComponentName _realActivity, ComponentName _origActivity,
boolean _rootWasReset, boolean _askedCompatMode, int _taskType, int _userId,
- String _lastDescription, ArrayList<ActivityRecord> activities, long _lastActiveTime,
- long lastTimeMoved, boolean neverRelinquishIdentity,
+ String _lastDescription, ArrayList<ActivityRecord> activities, long _firstActiveTime,
+ long _lastActiveTime, long lastTimeMoved, boolean neverRelinquishIdentity,
ActivityManager.TaskDescription _lastTaskDescription) {
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX + TaskPersister.IMAGE_EXTENSION;
@@ -166,6 +168,7 @@
taskType = _taskType;
mTaskToReturnTo = HOME_ACTIVITY_TYPE;
userId = _userId;
+ firstActiveTime = _firstActiveTime;
lastActiveTime = _lastActiveTime;
lastDescription = _lastDescription;
mActivities = activities;
@@ -176,6 +179,9 @@
void touchActiveTime() {
lastActiveTime = android.os.SystemClock.elapsedRealtime();
+ if (firstActiveTime == 0) {
+ firstActiveTime = lastActiveTime;
+ }
}
long getInactiveDuration() {
@@ -668,6 +674,7 @@
out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
out.attribute(null, ATTR_USERID, String.valueOf(userId));
out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
+ out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime));
out.attribute(null, ATTR_LASTACTIVETIME, String.valueOf(lastActiveTime));
out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
@@ -719,6 +726,7 @@
int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
int userId = 0;
String lastDescription = null;
+ long firstActiveTime = -1;
long lastActiveTime = -1;
long lastTimeOnTop = 0;
boolean neverRelinquishIdentity = true;
@@ -747,6 +755,8 @@
userId = Integer.valueOf(attrValue);
} else if (ATTR_TASKTYPE.equals(attrName)) {
taskType = Integer.valueOf(attrValue);
+ } else if (ATTR_FIRSTACTIVETIME.equals(attrName)) {
+ firstActiveTime = Long.valueOf(attrValue);
} else if (ATTR_LASTACTIVETIME.equals(attrName)) {
lastActiveTime = Long.valueOf(attrValue);
} else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
@@ -795,8 +805,8 @@
final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
affinityIntent, affinity, realActivity, origActivity, rootHasReset,
- askedCompatMode, taskType, userId, lastDescription, activities, lastActiveTime,
- lastTimeOnTop, neverRelinquishIdentity, taskDescription);
+ askedCompatMode, taskType, userId, lastDescription, activities, firstActiveTime,
+ lastActiveTime, lastTimeOnTop, neverRelinquishIdentity, taskDescription);
for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
activities.get(activityNdx).task = task;
@@ -853,6 +863,7 @@
pw.print(" lastThumbnailFile="); pw.print(mLastThumbnailFile);
pw.print(" lastDescription="); pw.println(lastDescription);
pw.print(prefix); pw.print("hasBeenVisible="); pw.print(hasBeenVisible);
+ pw.print(" firstActiveTime="); pw.print(lastActiveTime);
pw.print(" lastActiveTime="); pw.print(lastActiveTime);
pw.print(" (inactive for ");
pw.print((getInactiveDuration()/1000)); pw.println("s)");
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index c860673..a5a502a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -57,7 +57,7 @@
*
* @param deviceType requested device type to allocate logical address
* @param logicalAddress allocated logical address. If it is
- * {@link Constants.ADDR_UNREGISTERED}, it means that
+ * {@link Constants#ADDR_UNREGISTERED}, it means that
* it failed to allocate logical address for the given device type
*/
void onAllocated(int deviceType, int logicalAddress);
@@ -158,7 +158,7 @@
*
* @param deviceType type of device to used to determine logical address
* @param preferredAddress a logical address preferred to be allocated.
- * If sets {@link Constants.ADDR_UNREGISTERED}, scans
+ * If sets {@link Constants#ADDR_UNREGISTERED}, scans
* the smallest logical address matched with the given device type.
* Otherwise, scan address will start from {@code preferredAddress}
* @param callback callback interface to report allocated logical address to caller
@@ -559,10 +559,10 @@
/**
* Called by native when a hotplug event issues.
*/
- private void handleHotplug(boolean connected) {
- // TODO: once add port number to cec HAL interface, pass port number
- // to the service.
- mService.onHotplug(0, connected);
+ @ServiceThreadOnly
+ private void handleHotplug(int port, boolean connected) {
+ assertRunOnServiceThread();
+ mService.onHotplug(port, connected);
}
private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index e87db50..c5169b9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -156,6 +156,12 @@
return handleSetStreamPath(message);
case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
return handleGiveDevicePowerStatus(message);
+ case Constants.MESSAGE_VENDOR_COMMAND:
+ return handleVendorCommand(message);
+ case Constants.MESSAGE_VENDOR_COMMAND_WITH_ID:
+ return handleVendorCommandWithId(message);
+ case Constants.MESSAGE_SET_OSD_NAME:
+ return handleSetOsdName(message);
default:
return false;
}
@@ -244,10 +250,6 @@
return true;
}
- protected boolean handleVendorSpecificCommand(HdmiCecMessage message) {
- return false;
- }
-
protected boolean handleRoutingChange(HdmiCecMessage message) {
return false;
}
@@ -337,6 +339,34 @@
return true;
}
+ protected boolean handleVendorCommand(HdmiCecMessage message) {
+ mService.invokeVendorCommandListeners(mDeviceType, message.getSource(),
+ message.getParams(), false);
+ return true;
+ }
+
+ protected boolean handleVendorCommandWithId(HdmiCecMessage message) {
+ byte[] params = message.getParams();
+ int vendorId = HdmiUtils.threeBytesToInt(params);
+ if (vendorId == mService.getVendorId()) {
+ mService.invokeVendorCommandListeners(mDeviceType, message.getSource(), params, true);
+ } else if (message.getDestination() != Constants.ADDR_BROADCAST &&
+ message.getSource() != Constants.ADDR_UNREGISTERED) {
+ Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>");
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
+ message.getSource(), Constants.MESSAGE_VENDOR_COMMAND_WITH_ID,
+ Constants.ABORT_UNRECOGNIZED_MODE));
+ } else {
+ Slog.v(TAG, "Wrong broadcast vendor command. Ignoring");
+ }
+ return true;
+ }
+
+ protected boolean handleSetOsdName(HdmiCecMessage message) {
+ // The default behavior of <Set Osd Name> is doing nothing.
+ return true;
+ }
+
@ServiceThreadOnly
final void handleAddressAllocated(int logicalAddress) {
assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 223deab..02b5344 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -16,12 +16,13 @@
package com.android.server.hdmi;
+import android.content.Intent;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.IHdmiControlCallback;
import android.media.AudioSystem;
-import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseArray;
@@ -29,6 +30,7 @@
import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
+import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -257,7 +259,7 @@
int address = message.getSource();
int path = HdmiUtils.twoBytesToInt(message.getParams());
if (getDeviceInfo(address) == null) {
- handleNewDeviceAtTheTailOfActivePath(address, path);
+ handleNewDeviceAtTheTailOfActivePath(path);
} else {
ActiveSourceHandler.create(this, null).process(address, path);
}
@@ -338,13 +340,13 @@
int path = HdmiUtils.twoBytesToInt(message.getParams());
int address = message.getSource();
if (!isInDeviceList(path, address)) {
- handleNewDeviceAtTheTailOfActivePath(address, path);
+ handleNewDeviceAtTheTailOfActivePath(path);
}
addAndStartAction(new NewDeviceAction(this, address, path));
return true;
}
- private void handleNewDeviceAtTheTailOfActivePath(int address, int path) {
+ private void handleNewDeviceAtTheTailOfActivePath(int path) {
// Seq #22
if (isTailOfActivePath(path, getActivePath())) {
removeAction(RoutingControlAction.class);
@@ -404,33 +406,6 @@
@Override
@ServiceThreadOnly
- protected boolean handleVendorSpecificCommand(HdmiCecMessage message) {
- assertRunOnServiceThread();
- List<VendorSpecificAction> actions = Collections.emptyList();
- // TODO: Call mService.getActions(VendorSpecificAction.class) to get all the actions.
-
- // We assume that there can be multiple vendor-specific command actions running
- // at the same time. Pass the message to each action to see if one of them needs it.
- for (VendorSpecificAction action : actions) {
- if (action.processCommand(message)) {
- return true;
- }
- }
- // Handle the message here if it is not already consumed by one of the running actions.
- // Respond with a appropriate vendor-specific command or <Feature Abort>, or create another
- // vendor-specific action:
- //
- // mService.addAndStartAction(new VendorSpecificAction(mService, mAddress));
- //
- // For now, simply reply with <Feature Abort> and mark it consumed by returning true.
- mService.sendCecCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(
- message.getDestination(), message.getSource(), message.getOpcode(),
- Constants.ABORT_REFUSED));
- return true;
- }
-
- @Override
- @ServiceThreadOnly
protected boolean handleReportAudioStatus(HdmiCecMessage message) {
assertRunOnServiceThread();
@@ -465,6 +440,35 @@
return handleTextViewOn(message);
}
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleSetOsdName(HdmiCecMessage message) {
+ int source = message.getSource();
+ HdmiCecDeviceInfo deviceInfo = getDeviceInfo(source);
+ // If the device is not in device list, ignore it.
+ if (deviceInfo == null) {
+ Slog.e(TAG, "No source device info for <Set Osd Name>." + message);
+ return true;
+ }
+ String osdName = null;
+ try {
+ osdName = new String(message.getParams(), "US-ASCII");
+ } catch (UnsupportedEncodingException e) {
+ Slog.e(TAG, "Invalid <Set Osd Name> request:" + message, e);
+ return true;
+ }
+
+ if (deviceInfo.getDisplayName().equals(osdName)) {
+ Slog.i(TAG, "Ignore incoming <Set Osd Name> having same osd name:" + message);
+ return true;
+ }
+
+ addCecDevice(new HdmiCecDeviceInfo(deviceInfo.getLogicalAddress(),
+ deviceInfo.getPhysicalAddress(), deviceInfo.getDeviceType(),
+ deviceInfo.getVendorId(), osdName));
+ return true;
+ }
+
@ServiceThreadOnly
private void launchDeviceDiscovery() {
assertRunOnServiceThread();
@@ -702,8 +706,6 @@
assertRunOnServiceThread();
// In case where <Terminate Arc> is started by <Request ARC Termination>
// need to clean up RequestArcInitiationAction.
- // TODO: check conditions of power status by calling is_connected api
- // to be added soon.
removeAction(RequestArcTerminationAction.class);
SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
message.getSource(), false);
@@ -756,7 +758,7 @@
* that has the same logical address as new one has.
*/
@ServiceThreadOnly
- HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
+ private HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
assertRunOnServiceThread();
HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
if (oldDeviceInfo != null) {
@@ -777,7 +779,7 @@
* @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
*/
@ServiceThreadOnly
- HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
+ private HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
assertRunOnServiceThread();
HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
if (deviceInfo != null) {
@@ -905,7 +907,8 @@
}
/**
- * Called when a device is newly added or a new device is detected.
+ * Called when a device is newly added or a new device is detected or
+ * existing device is updated.
*
* @param info device info of a new device.
*/
@@ -1025,6 +1028,7 @@
if (!hotplugActions.isEmpty()) {
// Note that hotplug action is single action running on a machine.
// "pollAllDevicesNow" cleans up timer and start poll action immediately.
+ // It covers seq #40, #43.
hotplugActions.get(0).pollAllDevicesNow();
}
}
@@ -1080,4 +1084,11 @@
boolean isPowerStandbyOrTransient() {
return mService.isPowerStandbyOrTransient();
}
+
+ void displayOsd(int messageId) {
+ Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
+ intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
+ mService.getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
+ HdmiControlService.PERMISSION);
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index c0ff81d..970568a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -172,6 +172,10 @@
return "Active Source";
case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
return "Give Device Power Status";
+ case Constants.MESSAGE_VENDOR_COMMAND:
+ return "Vendor Command";
+ case Constants.MESSAGE_VENDOR_COMMAND_WITH_ID:
+ return "Vendor Command With ID";
default:
return String.format("Opcode: %02X", opcode);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index fe35b24..57f89b1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -424,6 +424,37 @@
return buildCommand(src, dest, Constants.MESSAGE_STANDBY);
}
+ /**
+ * Build <Vendor Command> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param params vendor-specific parameters
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildVendorCommand(int src, int dest, byte[] params) {
+ return buildCommand(src, dest, Constants.MESSAGE_VENDOR_COMMAND, params);
+ }
+
+ /**
+ * Build <Vendor Command With ID> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param vendorId vendor ID
+ * @param operands vendor-specific parameters
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildVendorCommandWithId(int src, int dest, int vendorId,
+ byte[] operands) {
+ byte[] params = new byte[operands.length + 3]; // parameter plus len(vendorId)
+ params[0] = (byte) ((vendorId >> 16) & 0xFF);
+ params[1] = (byte) ((vendorId >> 8) & 0xFF);
+ params[2] = (byte) (vendorId & 0xFF);
+ System.arraycopy(operands, 0, params, 3, operands.length);
+ return buildCommand(src, dest, Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, params);
+ }
+
/***** Please ADD new buildXXX() methods above. ******/
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index f927cb6..c32f312 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -32,6 +32,7 @@
import android.hardware.hdmi.IHdmiHotplugEventListener;
import android.hardware.hdmi.IHdmiInputChangeListener;
import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
+import android.hardware.hdmi.IHdmiVendorCommandListener;
import android.media.AudioManager;
import android.os.Build;
import android.os.Handler;
@@ -61,8 +62,7 @@
public final class HdmiControlService extends SystemService {
private static final String TAG = "HdmiControlService";
- // TODO: Rename the permission to HDMI_CONTROL.
- private static final String PERMISSION = "android.permission.HDMI_CEC";
+ static final String PERMISSION = "android.permission.HDMI_CEC";
/**
* Interface to report send result.
@@ -140,6 +140,11 @@
private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
new ArrayList<>();
+ // List of records for vendor command listener to handle the the caller killed in action.
+ @GuardedBy("mLock")
+ private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords =
+ new ArrayList<>();
+
@GuardedBy("mLock")
private IHdmiInputChangeListener mInputChangeListener;
@@ -199,8 +204,12 @@
mCecController = HdmiCecController.create(this);
if (mCecController != null) {
+ // TODO: Remove this as soon as OEM's HAL implementation is corrected.
+ mCecController.setOption(HdmiTvClient.OPTION_CEC_ENABLE,
+ HdmiTvClient.ENABLED);
+
mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL,
- HdmiTvClient.DISABLED);
+ HdmiTvClient.ENABLED);
initializeLocalDevices(mLocalDevices);
} else {
Slog.i(TAG, "Device does not support HDMI-CEC.");
@@ -608,6 +617,23 @@
}
}
+ class VendorCommandListenerRecord implements IBinder.DeathRecipient {
+ private final IHdmiVendorCommandListener mListener;
+ private final int mDeviceType;
+
+ public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
+ mListener = listener;
+ mDeviceType = deviceType;
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ mVendorCommandListenerRecords.remove(this);
+ }
+ }
+ }
+
private void enforceAccessPermission() {
getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
}
@@ -917,6 +943,42 @@
}
HdmiControlService.this.setProhibitMode(enabled);
}
+
+ @Override
+ public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
+ final int deviceType) {
+ enforceAccessPermission();
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiControlService.this.addVendorCommandListener(listener, deviceType);
+ }
+ });
+ }
+
+ @Override
+ public void sendVendorCommand(final int deviceType, final int targetAddress,
+ final byte[] params, final boolean hasVendorId) {
+ enforceAccessPermission();
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
+ if (device == null) {
+ Slog.w(TAG, "Local device not available");
+ return;
+ }
+ if (hasVendorId) {
+ sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId(
+ device.getDeviceInfo().getLogicalAddress(), targetAddress,
+ getVendorId(), params));
+ } else {
+ sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand(
+ device.getDeviceInfo().getLogicalAddress(), targetAddress, params));
+ }
+ }
+ });
+ }
}
@ServiceThreadOnly
@@ -1197,6 +1259,35 @@
mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL, HdmiTvClient.DISABLED);
}
+ private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
+ VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
+ try {
+ listener.asBinder().linkToDeath(record, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Listener already died");
+ return;
+ }
+ synchronized (mLock) {
+ mVendorCommandListenerRecords.add(record);
+ }
+ }
+
+ void invokeVendorCommandListeners(int deviceType, int srcAddress, byte[] params,
+ boolean hasVendorId) {
+ synchronized (mLock) {
+ for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
+ if (record.mDeviceType != deviceType) {
+ continue;
+ }
+ try {
+ record.mListener.onReceived(srcAddress, params, hasVendorId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify vendor command reception", e);
+ }
+ }
+ }
+ }
+
boolean isProhibitMode() {
synchronized (mLock) {
return mProhibitMode;
diff --git a/services/core/java/com/android/server/hdmi/VendorSpecificAction.java b/services/core/java/com/android/server/hdmi/VendorSpecificAction.java
deleted file mode 100644
index ff21a57..0000000
--- a/services/core/java/com/android/server/hdmi/VendorSpecificAction.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.android.server.hdmi;
-
-/**
- * Handles vendor-specific commands that require a sequence of command exchange,
- * or need to manage some states to complete the processing.
- */
-public class VendorSpecificAction extends FeatureAction {
-
- // Sample state this action can be in.
- private static final int STATE_1 = 1;
- private static final int STATE_2 = 2;
-
- VendorSpecificAction(HdmiCecLocalDevice source) {
- super(source);
- // Modify the constructor if additional arguments are necessary.
- }
-
- @Override
- boolean start() {
- // Do initialization step and update the state accordingly here.
- mState = STATE_1;
- addTimer(STATE_1, TIMEOUT_MS);
- return true;
- }
-
- @Override
- boolean processCommand(HdmiCecMessage cmd) {
- // Returns true if the command was consumed. Otherwise return false for other
- // actions in progress can be given its turn to process it.
- return false;
- }
-
- @Override
- void handleTimerEvent(int state) {
- // Ignore the timer event if the current state and the state this event should be
- // handled in are different. Could be an outdated event which should have been cleared by
- // calling {@code mActionTimer.clearTimerMessage()}.
- if (mState != state) {
- return;
- }
-
- switch (state) {
- case STATE_1:
- mState = STATE_2;
- addTimer(STATE_2, TIMEOUT_MS);
- break;
- case STATE_2:
- finish();
- break;
- }
- }
-}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index f8ac2e4..08c8271 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -264,7 +264,7 @@
mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
} else if (c.state == Condition.STATE_TRUE) {
Slog.d(TAG, "Enter zen: automatic condition true: " + c);
- mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_ON);
+ mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c8496e4..3a6cec2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -890,7 +890,7 @@
// Grab our optional AudioService
mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
-
+ mZenModeHelper.setAudioManager(mAudioManager);
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
// This observer will force an update when observe is called, causing us to
// bind to listener services.
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 7bac3dc..92bec14 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -74,6 +74,8 @@
private int mZenMode;
private ZenModeConfig mConfig;
+ private AudioManager mAudioManager;
+ private int mPreviousRingerMode = -1;
// temporary, until we update apps to provide metadata
private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
@@ -129,13 +131,17 @@
mCallbacks.add(callback);
}
+ public void setAudioManager(AudioManager audioManager) {
+ mAudioManager = audioManager;
+ }
+
public boolean shouldIntercept(NotificationRecord record) {
if (mZenMode != Global.ZEN_MODE_OFF) {
if (isSystem(record)) {
return false;
}
if (isAlarm(record)) {
- return false;
+ return mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
}
// audience has veto power over all following rules
if (!audienceMatches(record)) {
@@ -185,6 +191,34 @@
mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
zen ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
exceptionPackages);
+
+ // alarm restrictions
+ final boolean muteAlarms = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
+ mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM,
+ muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+ mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM,
+ muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+
+ // force ringer mode into compliance
+ if (mAudioManager != null) {
+ int ringerMode = mAudioManager.getRingerMode();
+ if (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
+ if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
+ mPreviousRingerMode = ringerMode;
+ Slog.d(TAG, "Silencing ringer");
+ mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
+ }
+ } else {
+ if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
+ Slog.d(TAG, "Unsilencing ringer");
+ mAudioManager.setRingerMode(mPreviousRingerMode != -1 ? mPreviousRingerMode
+ : AudioManager.RINGER_MODE_NORMAL);
+ mPreviousRingerMode = -1;
+ }
+ }
+ }
dispatchOnZenModeChanged();
}
@@ -201,6 +235,7 @@
pw.println(Global.zenModeToString(mZenMode));
pw.print(prefix); pw.print("mConfig="); pw.println(mConfig);
pw.print(prefix); pw.print("mDefaultConfig="); pw.println(mDefaultConfig);
+ pw.print(prefix); pw.print("mPreviousRingerMode="); pw.println(mPreviousRingerMode);
}
public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException {
@@ -308,16 +343,6 @@
return new Date(time) + " (" + time + ")";
}
- public static boolean isWeekend(long time, int offsetDays) {
- final Calendar c = Calendar.getInstance();
- c.setTimeInMillis(time);
- if (offsetDays != 0) {
- c.add(Calendar.DATE, offsetDays);
- }
- final int day = c.get(Calendar.DAY_OF_WEEK);
- return day == Calendar.SATURDAY || day == Calendar.SUNDAY;
- }
-
private class SettingsObserver extends ContentObserver {
private final Uri ZEN_MODE = Global.getUriFor(Global.ZEN_MODE);
@@ -344,31 +369,45 @@
}
private class ZenBroadcastReceiver extends BroadcastReceiver {
+ private final Calendar mCalendar = Calendar.getInstance();
+
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_ENTER_ZEN.equals(intent.getAction())) {
- setZenMode(intent, 1, Global.ZEN_MODE_ON);
+ setZenMode(intent, Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
} else if (ACTION_EXIT_ZEN.equals(intent.getAction())) {
- setZenMode(intent, 0, Global.ZEN_MODE_OFF);
+ setZenMode(intent, Global.ZEN_MODE_OFF);
}
}
- private void setZenMode(Intent intent, int wkendOffsetDays, int zenModeValue) {
+ private void setZenMode(Intent intent, int zenModeValue) {
final long schTime = intent.getLongExtra(EXTRA_TIME, 0);
final long now = System.currentTimeMillis();
Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s",
intent.getAction(), ts(schTime), ts(now), now - schTime));
- final boolean skip = ZenModeConfig.SLEEP_MODE_WEEKNIGHTS.equals(mConfig.sleepMode) &&
- isWeekend(schTime, wkendOffsetDays);
-
- if (skip) {
- Slog.d(TAG, "Skipping zen mode update for the weekend");
+ final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode);
+ if (days != null) {
+ final int day = getDayOfWeek(schTime);
+ for (int i = 0; i < days.length; i++) {
+ if (days[i] == day) {
+ Slog.d(TAG, "Enter downtime, day=" + day + " days=" + Arrays.asList(days));
+ ZenModeHelper.this.setZenMode(zenModeValue);
+ break;
+ } else {
+ Slog.d(TAG, "Skip downtime, day=" + day + " days=" + Arrays.asList(days));
+ }
+ }
} else {
- ZenModeHelper.this.setZenMode(zenModeValue);
+ Slog.d(TAG, "Skip downtime, no days configured");
}
updateAlarms();
}
+
+ private int getDayOfWeek(long time) {
+ mCalendar.setTimeInMillis(time);
+ return mCalendar.get(Calendar.DAY_OF_WEEK);
+ }
}
public static class Callback {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index c011cf9..8067bd9 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -344,11 +344,11 @@
final ApkLite info;
try {
- info = PackageParser.parseApkLite(new File(app.sourceDir),
+ info = PackageParser.parseApkLite(new File(app.getBaseCodePath()),
PackageParser.PARSE_GET_SIGNATURES);
} catch (PackageParserException e) {
throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
- "Failed to parse existing base " + app.sourceDir + ": " + e);
+ "Failed to parse existing base " + app.getBaseCodePath() + ": " + e);
}
assertPackageConsistent("Existing base", info.packageName, info.versionCode,
@@ -379,7 +379,7 @@
*/
private void spliceExistingFilesIntoStage() throws InstallFailedException {
final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
- final File existingDir = new File(app.sourceDir).getParentFile();
+ final File existingDir = new File(app.getBaseCodePath());
try {
linkTreeIgnoringExisting(existingDir, sessionDir);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c80f316..016d612 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -30,8 +30,10 @@
import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
import static android.system.OsConstants.O_CREAT;
+import static android.system.OsConstants.EEXIST;
import static android.system.OsConstants.O_EXCL;
import static android.system.OsConstants.O_RDWR;
+import static android.system.OsConstants.O_WRONLY;
import static android.system.OsConstants.S_IRGRP;
import static android.system.OsConstants.S_IROTH;
import static android.system.OsConstants.S_IRWXU;
@@ -103,6 +105,7 @@
import android.content.pm.PackageInstallerParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser.ActivityIntentInfo;
+import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser;
import android.content.pm.PackageStats;
@@ -146,6 +149,7 @@
import android.security.SystemKeyStore;
import android.system.ErrnoException;
import android.system.Os;
+import android.system.OsConstants;
import android.system.StructStat;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -190,6 +194,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
@@ -375,6 +380,8 @@
/** Directory where third-party apps are staged before install */
final File mAppStagingDir;
+ private final Random mTempFileRandom = new Random();
+
// ----------------------------------------------------------------
// Lock for state used when installing and doing other long running
@@ -4085,7 +4092,7 @@
pkg.applicationInfo.resourceDirs = new String[overlayArray.length];
int i = 0;
for (PackageParser.Package p : overlayArray) {
- pkg.applicationInfo.resourceDirs[i++] = p.applicationInfo.sourceDir;
+ pkg.applicationInfo.resourceDirs[i++] = p.baseCodePath;
}
return true;
}
@@ -4103,7 +4110,8 @@
}
for (File file : files) {
- if (!isApkFile(file)) {
+ final boolean isPackage = isApkFile(file) || file.isDirectory();
+ if (!isPackage) {
// Ignore entries which are not apk's
continue;
}
@@ -4184,8 +4192,7 @@
private PackageParser.Package scanPackageLI(File scanFile,
int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) {
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
- String scanPath = scanFile.getPath();
- if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanPath);
+ if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
parseFlags |= mDefParseFlags;
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
@@ -4198,7 +4205,7 @@
final PackageParser.Package pkg;
try {
- pkg = pp.parseMonolithicPackage(scanFile, parseFlags);
+ pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
mLastScanError = e.error;
return null;
@@ -4357,27 +4364,29 @@
}
}
- final String baseCodePath = pkg.baseCodePath;
- final String[] splitCodePaths = pkg.splitCodePaths;
-
// TODO: extend to support forward-locked splits
- String baseResPath = null;
+ String resourcePath = null;
+ String baseResourcePath = null;
if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) {
if (ps != null && ps.resourcePathString != null) {
- baseResPath = ps.resourcePathString;
+ resourcePath = ps.resourcePathString;
+ baseResourcePath = ps.resourcePathString;
} else {
// Should not happen at all. Just log an error.
Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
}
} else {
- baseResPath = pkg.baseCodePath;
+ resourcePath = pkg.codePath;
+ baseResourcePath = pkg.baseCodePath;
}
// Set application objects path explicitly.
- pkg.applicationInfo.sourceDir = baseCodePath;
- pkg.applicationInfo.publicSourceDir = baseResPath;
- pkg.applicationInfo.splitSourceDirs = splitCodePaths;
- pkg.applicationInfo.splitPublicSourceDirs = splitCodePaths;
+ pkg.applicationInfo.setCodePath(pkg.codePath);
+ pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
+ pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
+ pkg.applicationInfo.setResourcePath(resourcePath);
+ pkg.applicationInfo.setBaseResourcePath(baseResourcePath);
+ pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
@@ -4906,8 +4915,8 @@
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) {
final File scanFile = new File(pkg.codePath);
- if (pkg.applicationInfo.sourceDir == null ||
- pkg.applicationInfo.publicSourceDir == null) {
+ if (pkg.applicationInfo.getCodePath() == null ||
+ pkg.applicationInfo.getResourcePath() == null) {
// Bail out. The resource and code paths haven't been set.
Slog.w(TAG, " Code and resource paths haven't been set correctly");
mLastScanError = PackageManager.INSTALL_FAILED_INVALID_APK;
@@ -4978,8 +4987,8 @@
}
// Initialize package source and resource directories
- File destCodeFile = new File(pkg.applicationInfo.sourceDir);
- File destResourceFile = new File(pkg.applicationInfo.publicSourceDir);
+ File destCodeFile = new File(pkg.applicationInfo.getCodePath());
+ File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
SharedUserSetting suid = null;
PackageSetting pkgSetting = null;
@@ -6137,19 +6146,22 @@
PackageSetting pkgSetting) {
// "bundled" here means system-installed with no overriding update
final boolean bundledApk = isSystemApp(pkg) && !isUpdatedSystemApp(pkg);
- final String apkName = getApkName(pkg.applicationInfo.sourceDir);
- final File libDir;
+ final File codeFile = new File(pkg.applicationInfo.getCodePath());
+ final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
+ final String nativeLibraryPath;
if (bundledApk) {
// If "/system/lib64/apkname" exists, assume that is the per-package
// native library directory to use; otherwise use "/system/lib/apkname".
- String apkRoot = calculateApkRoot(pkg.applicationInfo.sourceDir);
+ String apkRoot = calculateApkRoot(pkg.applicationInfo.getCodePath());
File lib64 = new File(apkRoot, LIB64_DIR_NAME);
File packLib64 = new File(lib64, apkName);
- libDir = (packLib64.exists()) ? lib64 : new File(apkRoot, LIB_DIR_NAME);
+ File libDir = (packLib64.exists()) ? lib64 : new File(apkRoot, LIB_DIR_NAME);
+ nativeLibraryPath = (new File(libDir, apkName)).getAbsolutePath();
} else {
- libDir = mAppLibInstallDir;
+ // We're installing an upgrade; use directory found during scan
+ // TODO: consider deriving this based on instructionSet
+ nativeLibraryPath = pkg.applicationInfo.nativeLibraryDir;
}
- final String nativeLibraryPath = (new File(libDir, apkName)).getPath();
pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath;
// pkgSetting might be null during rescan following uninstall of updates
// to a bundled app, so accommodate that possibility. The settings in
@@ -6161,8 +6173,8 @@
// Deduces the required ABI of an upgraded system app.
private void setInternalAppAbi(PackageParser.Package pkg, PackageSetting pkgSetting) {
- final String apkRoot = calculateApkRoot(pkg.applicationInfo.sourceDir);
- final String apkName = getApkName(pkg.applicationInfo.sourceDir);
+ final String apkRoot = calculateApkRoot(pkg.applicationInfo.getCodePath());
+ final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
// This is of the form "/system/lib64/<packagename>", "/vendor/lib64/<packagename>"
// or similar.
@@ -8973,7 +8985,7 @@
return new AsecInstallArgs(codeFile, cid, instructionSet, installOnSd(flags),
installForwardLocked(flags));
} else {
- return new FileInstallArgs(codeFile, pkgName, instructionSet);
+ return new FileInstallArgs(codeFile, instructionSet);
}
}
@@ -9013,7 +9025,12 @@
abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
abstract int doPreInstall(int status);
- abstract boolean doRename(int status, String pkgName, String oldCodePath);
+
+ /**
+ * Rename package into final resting place. All paths on the given
+ * scanned package should be updated to reflect the rename.
+ */
+ abstract boolean doRename(int status, PackageParser.Package pkg, String oldCodePath);
abstract int doPostInstall(int status, int uid);
/** @see PackageSettingBase#codePathString */
@@ -9062,13 +9079,15 @@
* and renaming logic.
*/
class FileInstallArgs extends InstallArgs {
- // TODO: teach about handling cluster directories
+ private File codeFile;
+ private File resourceFile;
+ private File nativeLibraryFile;
- File installDir;
- String codeFileName;
- String resourceFileName;
- String libraryPath;
- boolean created = false;
+ // Example topology:
+ // /data/app/com.example/base.apk
+ // /data/app/com.example/split_foo.apk
+ // /data/app/com.example/native/arm/libfoo.so
+ // /data/app/com.example/dalvik/arm/base.apk@classes.dex
/** New install */
FileInstallArgs(InstallParams params) {
@@ -9076,27 +9095,23 @@
params.flags, params.installerPackageName, params.getManifestDigest(),
params.getUser(), params.packageInstructionSetOverride,
params.packageAbiOverride);
+ if (isFwdLocked()) {
+ throw new IllegalArgumentException("Forward locking only supported in ASEC");
+ }
}
/** Existing install */
- FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
+ FileInstallArgs(String codePath, String resourcePath, String nativeLibraryPath,
String instructionSet) {
super(null, false, null, null, 0, null, null, null, instructionSet, null);
- File codeFile = new File(fullCodePath);
- installDir = codeFile.getParentFile();
- codeFileName = fullCodePath;
- resourceFileName = fullResourcePath;
- libraryPath = nativeLibraryPath;
+ this.codeFile = (codePath != null) ? new File(codePath) : null;
+ this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
+ this.nativeLibraryFile = (nativeLibraryPath != null) ? new File(nativeLibraryPath) : null;
}
/** New install from existing */
- FileInstallArgs(File originFile, String pkgName, String instructionSet) {
+ FileInstallArgs(File originFile, String instructionSet) {
super(originFile, true, null, null, 0, null, null, null, instructionSet, null);
- installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
- String apkName = getNextCodePath(null, pkgName, ".apk");
- codeFileName = new File(installDir, apkName + ".apk").getPath();
- resourceFileName = getResourcePathFromCodePath();
- libraryPath = new File(mAppLibInstallDir, pkgName).getPath();
}
boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
@@ -9120,70 +9135,38 @@
lowThreshold);
}
- void createCopyFile() {
- installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
- codeFileName = createTempPackageFile(installDir).getPath();
- resourceFileName = getResourcePathFromCodePath();
- libraryPath = getLibraryPathFromCodePath();
- created = true;
- }
-
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
- if (temp) {
- // Generate temp file name
- createCopyFile();
- }
- // Get a ParcelFileDescriptor to write to the output file
- final File codeFile = new File(codeFileName);
- if (!created) {
- try {
- codeFile.createNewFile();
- // Set permissions
- if (!setPermissions()) {
- // Failed setting permissions.
- return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- }
- } catch (IOException e) {
- Slog.w(TAG, "Failed to create file " + codeFile);
- return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- }
+ try {
+ final File tempDir = createTempPackageDir(mAppInstallDir);
+ codeFile = tempDir;
+ resourceFile = tempDir;
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to create copy file: " + e);
+ return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
- // TODO: extend to support copying into clusters
final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
@Override
public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
+ if (!FileUtils.isValidExtFilename(name)) {
+ throw new IllegalArgumentException("Invalid filename: " + name);
+ }
try {
- return ParcelFileDescriptor.open(codeFile,
- ParcelFileDescriptor.MODE_READ_WRITE);
- } catch (FileNotFoundException e) {
- throw new RemoteException(e.getMessage());
+ final File file = new File(codeFile, name);
+ final FileDescriptor fd = Os.open(file.getAbsolutePath(),
+ O_RDWR | O_CREAT, 0644);
+ Os.chmod(file.getAbsolutePath(), 0644);
+ return new ParcelFileDescriptor(fd);
+ } catch (ErrnoException e) {
+ throw new RemoteException("Failed to open: " + e.getMessage());
}
}
};
- // Copy the resource now
int ret = imcs.copyPackage(originFile.getAbsolutePath(), target);
-
- if (isFwdLocked()) {
- final File destResourceFile = new File(getResourcePath());
-
- // Copy the public files
- try {
- PackageHelper.extractPublicFiles(codeFileName, destResourceFile);
- } catch (IOException e) {
- Slog.e(TAG, "Couldn't create a new zip file for the public parts of a"
- + " forward-locked app.");
- destResourceFile.delete();
- return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- }
- }
-
- final File nativeLibraryFile = new File(getNativeLibraryPath());
- Slog.i(TAG, "Copying native libraries to " + nativeLibraryFile.getPath());
- if (nativeLibraryFile.exists()) {
- NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);
- nativeLibraryFile.delete();
+ if (ret != PackageManager.INSTALL_SUCCEEDED) {
+ Slog.e(TAG, "Failed to copy package");
+ return ret;
}
String[] abiList = (abiOverride != null) ?
@@ -9197,11 +9180,24 @@
abiList = Build.SUPPORTED_32_BIT_ABIS;
}
- int copyRet = copyNativeLibrariesForInternalApp(handle, nativeLibraryFile, abiList);
- if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
- return copyRet;
+ // TODO: refactor to avoid double findSupportedAbi()
+ final int abiIndex = NativeLibraryHelper.findSupportedAbi(handle, abiList);
+ if (abiIndex < 0 && abiIndex != PackageManager.NO_NATIVE_LIBRARIES) {
+ return abiIndex;
+ } else if (abiIndex >= 0) {
+ final File baseLibFile = new File(codeFile, LIB_DIR_NAME);
+ baseLibFile.mkdir();
+ Os.chmod(baseLibFile.getAbsolutePath(), 0755);
+
+ final String abi = Build.SUPPORTED_ABIS[abiIndex];
+ final String instructionSet = VMRuntime.getInstructionSet(abi);
+ nativeLibraryFile = new File(baseLibFile, instructionSet);
+ nativeLibraryFile.mkdir();
+ Os.chmod(nativeLibraryFile.getAbsolutePath(), 0755);
+
+ copyNativeLibrariesForInternalApp(handle, nativeLibraryFile, abiList);
}
- } catch (IOException e) {
+ } catch (IOException | ErrnoException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
} finally {
@@ -9218,51 +9214,44 @@
return status;
}
- boolean doRename(int status, final String pkgName, String oldCodePath) {
+ boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp();
return false;
} else {
- final File oldCodeFile = new File(getCodePath());
- final File oldResourceFile = new File(getResourcePath());
- final File oldLibraryFile = new File(getNativeLibraryPath());
+ final File beforeCodeFile = codeFile;
+ final File afterCodeFile = new File(mAppInstallDir,
+ getNextCodePath(oldCodePath, pkg.packageName, null));
- // Rename APK file based on packageName
- final String apkName = getNextCodePath(oldCodePath, pkgName, ".apk");
- final File newCodeFile = new File(installDir, apkName + ".apk");
- if (!oldCodeFile.renameTo(newCodeFile)) {
+ Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
+ if (!beforeCodeFile.renameTo(afterCodeFile)) {
return false;
}
- codeFileName = newCodeFile.getPath();
-
- // Rename public resource file if it's forward-locked.
- final File newResFile = new File(getResourcePathFromCodePath());
- if (isFwdLocked() && !oldResourceFile.renameTo(newResFile)) {
- return false;
- }
- resourceFileName = newResFile.getPath();
-
- // Rename library path
- final File newLibraryFile = new File(getLibraryPathFromCodePath());
- if (newLibraryFile.exists()) {
- NativeLibraryHelper.removeNativeBinariesFromDirLI(newLibraryFile);
- newLibraryFile.delete();
- }
- if (!oldLibraryFile.renameTo(newLibraryFile)) {
- Slog.e(TAG, "Cannot rename native library directory "
- + oldLibraryFile.getPath() + " to " + newLibraryFile.getPath());
- return false;
- }
- libraryPath = newLibraryFile.getPath();
-
- // Attempt to set permissions
- if (!setPermissions()) {
+ if (!SELinux.restoreconRecursive(afterCodeFile)) {
return false;
}
- if (!SELinux.restorecon(newCodeFile)) {
- return false;
- }
+ // Reflect the rename internally
+ codeFile = afterCodeFile;
+ resourceFile = afterCodeFile;
+ nativeLibraryFile = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
+ nativeLibraryFile);
+
+ // Reflect the rename in scanned details
+ pkg.codePath = afterCodeFile.getAbsolutePath();
+ pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
+ pkg.baseCodePath);
+ pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
+ pkg.splitCodePaths);
+
+ // Reflect the rename in app info
+ pkg.applicationInfo.setCodePath(pkg.codePath);
+ pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
+ pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
+ pkg.applicationInfo.setResourcePath(pkg.codePath);
+ pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
+ pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
+ pkg.applicationInfo.nativeLibraryDir = getNativeLibraryPath();
return true;
}
@@ -9275,120 +9264,71 @@
return status;
}
- private String getResourcePathFromCodePath() {
- final String codePath = getCodePath();
- if (isFwdLocked()) {
- final StringBuilder sb = new StringBuilder();
-
- sb.append(mAppInstallDir.getPath());
- sb.append('/');
- sb.append(getApkName(codePath));
- sb.append(".zip");
-
- /*
- * If our APK is a temporary file, mark the resource as a
- * temporary file as well so it can be cleaned up after
- * catastrophic failure.
- */
- if (codePath.endsWith(".tmp")) {
- sb.append(".tmp");
- }
-
- return sb.toString();
- } else {
- return codePath;
- }
- }
-
- private String getLibraryPathFromCodePath() {
- return new File(mAppLibInstallDir, getApkName(getCodePath())).getPath();
- }
-
@Override
String getCodePath() {
- return codeFileName;
+ return (codeFile != null) ? codeFile.getAbsolutePath() : null;
}
@Override
String getResourcePath() {
- return resourceFileName;
+ return (resourceFile != null) ? resourceFile.getAbsolutePath() : null;
}
@Override
String getNativeLibraryPath() {
- if (libraryPath == null) {
- libraryPath = getLibraryPathFromCodePath();
- }
- return libraryPath;
+ return (nativeLibraryFile != null) ? nativeLibraryFile.getAbsolutePath() : null;
}
private boolean cleanUp() {
- boolean ret = true;
- String sourceDir = getCodePath();
- String publicSourceDir = getResourcePath();
- if (sourceDir != null) {
- File sourceFile = new File(sourceDir);
- if (!sourceFile.exists()) {
- Slog.w(TAG, "Package source " + sourceDir + " does not exist.");
- ret = false;
- }
- // Delete application's code and resources
- sourceFile.delete();
- }
- if (publicSourceDir != null && !publicSourceDir.equals(sourceDir)) {
- final File publicSourceFile = new File(publicSourceDir);
- if (!publicSourceFile.exists()) {
- Slog.w(TAG, "Package public source " + publicSourceFile + " does not exist.");
- }
- if (publicSourceFile.exists()) {
- publicSourceFile.delete();
- }
+ if (codeFile == null || !codeFile.exists()) {
+ return false;
}
- if (libraryPath != null) {
- File nativeLibraryFile = new File(libraryPath);
- NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);
- if (!nativeLibraryFile.delete()) {
- Slog.w(TAG, "Couldn't delete native library directory " + libraryPath);
- }
+ if (codeFile.isDirectory()) {
+ FileUtils.deleteContents(codeFile);
+ }
+ codeFile.delete();
+
+ if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
+ resourceFile.delete();
}
- return ret;
+ if (nativeLibraryFile != null && !FileUtils.contains(codeFile, nativeLibraryFile)) {
+ FileUtils.deleteContents(nativeLibraryFile);
+ nativeLibraryFile.delete();
+ }
+
+ return true;
}
void cleanUpResourcesLI() {
- String sourceDir = getCodePath();
- if (cleanUp()) {
+ // Try enumerating all code paths before deleting
+ List<String> allCodePaths = Collections.EMPTY_LIST;
+ if (codeFile != null && codeFile.exists()) {
+ try {
+ final PackageLite pkg = PackageParser.parsePackageLite(codeFile, 0);
+ allCodePaths = pkg.getAllCodePaths();
+ } catch (PackageParserException e) {
+ // Ignored; we tried our best
+ }
+ }
+
+ cleanUp();
+
+ if (!allCodePaths.isEmpty()) {
if (instructionSet == null) {
throw new IllegalStateException("instructionSet == null");
}
- int retCode = mInstaller.rmdex(sourceDir, instructionSet);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't remove dex file for package: "
- + " at location "
- + sourceDir + ", retcode=" + retCode);
- // we don't consider this to be a failure of the core package deletion
- }
- }
- }
- private boolean setPermissions() {
- // TODO Do this in a more elegant way later on. for now just a hack
- if (!isFwdLocked()) {
- final int filePermissions =
- FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP
- |FileUtils.S_IROTH;
- int retCode = FileUtils.setPermissions(getCodePath(), filePermissions, -1, -1);
- if (retCode != 0) {
- Slog.e(TAG, "Couldn't set new package file permissions for " +
- getCodePath()
- + ". The return code was: " + retCode);
- // TODO Define new internal error
- return false;
+ for (String codePath : allCodePaths) {
+ int retCode = mInstaller.rmdex(codePath, instructionSet);
+ if (retCode < 0) {
+ Slog.w(TAG, "Couldn't remove dex file for package: "
+ + " at location " + codePath + ", retcode=" + retCode);
+ // we don't consider this to be a failure of the core package deletion
+ }
}
- return true;
}
- return true;
}
boolean doPostDeleteLI(boolean delete) {
@@ -9538,9 +9478,8 @@
return status;
}
- boolean doRename(int status, final String pkgName,
- String oldCodePath) {
- String newCacheId = getNextCodePath(oldCodePath, pkgName, "/" + RES_FILE_NAME);
+ boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {
+ String newCacheId = getNextCodePath(oldCodePath, pkg.packageName, "/" + RES_FILE_NAME);
String newCachePath = null;
if (PackageHelper.isContainerMounted(cid)) {
// Unmount the container
@@ -9580,6 +9519,20 @@
" at new path: " + newCachePath);
cid = newCacheId;
setCachePath(newCachePath);
+
+ // TODO: extend to support split APKs
+ pkg.codePath = getCodePath();
+ pkg.baseCodePath = getCodePath();
+ pkg.splitCodePaths = null;
+
+ pkg.applicationInfo.setCodePath(getCodePath());
+ pkg.applicationInfo.setBaseCodePath(getCodePath());
+ pkg.applicationInfo.setSplitCodePaths(null);
+ pkg.applicationInfo.setResourcePath(getResourcePath());
+ pkg.applicationInfo.setBaseResourcePath(getResourcePath());
+ pkg.applicationInfo.setSplitResourcePaths(null);
+ pkg.applicationInfo.nativeLibraryDir = getNativeLibraryPath();
+
return true;
}
@@ -9716,7 +9669,7 @@
if (oldCodePath != null) {
String subStr = oldCodePath;
// Drop the suffix right away
- if (subStr.endsWith(suffix)) {
+ if (suffix != null && subStr.endsWith(suffix)) {
subStr = subStr.substring(0, subStr.length() - suffix.length());
}
// If oldCodePath already contains prefix find out the
@@ -9747,7 +9700,7 @@
// Utility method used to ignore ADD/REMOVE events
// by directory observer.
private static boolean ignoreCodePath(String fullPathStr) {
- String apkName = getApkName(fullPathStr);
+ String apkName = deriveCodePathName(fullPathStr);
int idx = apkName.lastIndexOf(INSTALL_PACKAGE_SUFFIX);
if (idx != -1 && ((idx+1) < apkName.length())) {
// Make sure the package ends with a numeral
@@ -9763,33 +9716,21 @@
// Utility method that returns the relative package path with respect
// to the installation directory. Like say for /data/data/com.test-1.apk
// string com.test-1 is returned.
- static String getApkName(String codePath) {
+ static String deriveCodePathName(String codePath) {
if (codePath == null) {
return null;
}
- int sidx = codePath.lastIndexOf("/");
- int eidx = codePath.lastIndexOf(".");
- if (eidx == -1) {
- eidx = codePath.length();
- } else if (eidx == 0) {
- Slog.w(TAG, " Invalid code path, "+ codePath + " Not a valid apk name");
+ final File codeFile = new File(codePath);
+ final String name = codeFile.getName();
+ if (codeFile.isDirectory()) {
+ return name;
+ } else if (name.endsWith(".apk") || name.endsWith(".tmp")) {
+ final int lastDot = name.lastIndexOf('.');
+ return name.substring(0, lastDot);
+ } else {
+ Slog.w(TAG, "Odd, " + codePath + " doesn't look like an APK");
return null;
}
- return codePath.substring(sidx+1, eidx);
- }
-
- private static String[] deriveSplitResPaths(String[] splitCodePaths) {
- String[] splitResPaths = null;
- if (!ArrayUtils.isEmpty(splitCodePaths)) {
- splitResPaths = new String[splitCodePaths.length];
- for (int i = 0; i < splitCodePaths.length; i++) {
- final String splitCodePath = splitCodePaths[i];
- final String resName = getApkName(splitCodePath) + ".zip";
- splitResPaths[i] = new File(new File(splitCodePath).getParentFile(),
- resName).getAbsolutePath();
- }
- }
- return splitResPaths;
}
class PackageInstalledInfo {
@@ -10058,8 +9999,8 @@
// which means we are replacing another update that is already
// installed. We need to make sure to delete the older one's .apk.
res.removedInfo.args = createInstallArgsForExisting(0,
- deletedPackage.applicationInfo.sourceDir,
- deletedPackage.applicationInfo.publicSourceDir,
+ deletedPackage.applicationInfo.getCodePath(),
+ deletedPackage.applicationInfo.getResourcePath(),
deletedPackage.applicationInfo.nativeLibraryDir,
getAppInstructionSet(deletedPackage.applicationInfo));
} else {
@@ -10223,7 +10164,7 @@
final PackageParser.Package pkg;
try {
- pkg = pp.parseMonolithicPackage(tmpPackageFile, parseFlags);
+ pkg = pp.parsePackage(tmpPackageFile, parseFlags);
} catch (PackageParserException e) {
res.returnCode = e.error;
return;
@@ -10341,22 +10282,11 @@
return;
}
- if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {
+ if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return;
}
- // Set application objects path explicitly after the rename
- // TODO: derive split paths from original scan after rename
- pkg.codePath = args.getCodePath();
- pkg.baseCodePath = args.getCodePath();
- pkg.splitCodePaths = null;
- pkg.applicationInfo.sourceDir = args.getCodePath();
- pkg.applicationInfo.publicSourceDir = args.getResourcePath();
- pkg.applicationInfo.splitSourceDirs = null;
- pkg.applicationInfo.splitPublicSourceDirs = null;
- pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
-
if (replace) {
replacePackageLI(pkg, parseFlags, scanMode, args.user,
installerPackageName, res, args.abiOverride);
@@ -10436,36 +10366,57 @@
private static final void deleteTempPackageFilesInDirectory(File directory,
FilenameFilter filter) {
- final String[] tmpFilesList = directory.list(filter);
- if (tmpFilesList == null) {
- return;
- }
- for (int i = 0; i < tmpFilesList.length; i++) {
- final File tmpFile = new File(directory, tmpFilesList[i]);
- tmpFile.delete();
+ final File[] files = directory.listFiles(filter);
+ if (!ArrayUtils.isEmpty(files)) {
+ for (File file : files) {
+ if (file.isDirectory()) {
+ FileUtils.deleteContents(file);
+ file.delete();
+ } else if (file.isFile()) {
+ file.delete();
+ }
+ }
}
}
- private File createTempPackageFile(File installDir) {
- File tmpPackageFile;
- try {
- tmpPackageFile = File.createTempFile("vmdl", ".tmp", installDir);
- } catch (IOException e) {
- Slog.e(TAG, "Couldn't create temp file for downloaded package file.");
- return null;
- }
- try {
- FileUtils.setPermissions(
- tmpPackageFile.getCanonicalPath(), FileUtils.S_IRUSR|FileUtils.S_IWUSR,
- -1, -1);
- if (!SELinux.restorecon(tmpPackageFile)) {
- return null;
+ private File createTempPackageDir(File installDir) throws IOException {
+ int n = 0;
+ while (n++ < 32) {
+ final File file = new File(installDir, "vmdl" + mTempFileRandom.nextInt() + ".tmp");
+ try {
+ Os.mkdir(file.getAbsolutePath(), 0755);
+ Os.chmod(file.getAbsolutePath(), 0755);
+ if (!SELinux.restorecon(file)) {
+ throw new IOException("Failed to restorecon");
+ }
+ return file;
+ } catch (ErrnoException e) {
+ if (e.errno == EEXIST) continue;
+ throw e.rethrowAsIOException();
}
- } catch (IOException e) {
- Slog.e(TAG, "Trouble getting the canoncical path for a temp file.");
- return null;
}
- return tmpPackageFile;
+ throw new IOException("Failed to create temp directory");
+ }
+
+ private File createTempPackageFile(File installDir) throws IOException {
+ int n = 0;
+ while (n++ < 32) {
+ final File file = new File(installDir, "vmdl" + mTempFileRandom.nextInt() + ".tmp");
+ try {
+ final FileDescriptor fd = Os.open(file.getAbsolutePath(),
+ O_RDWR | O_CREAT | O_EXCL, 0644);
+ IoUtils.closeQuietly(fd);
+ Os.chmod(file.getAbsolutePath(), 0644);
+ if (!SELinux.restorecon(file)) {
+ throw new IOException("Failed to restorecon");
+ }
+ return file;
+ } catch (ErrnoException e) {
+ if (e.errno == EEXIST) continue;
+ throw e.rethrowAsIOException();
+ }
+ }
+ throw new IOException("Failed to create temp file");
}
@Override
@@ -11254,7 +11205,7 @@
libDirPath = ps.nativeLibraryPathString;
}
if (p != null && (isExternal(p) || isForwardLocked(p))) {
- String secureContainerId = cidFromCodePath(p.applicationInfo.sourceDir);
+ String secureContainerId = cidFromCodePath(p.applicationInfo.getBaseCodePath());
if (secureContainerId != null) {
asecPath = PackageHelper.getSdFilesystem(secureContainerId);
}
@@ -11268,7 +11219,7 @@
return false;
}
if (isForwardLocked(p)) {
- publicSrcDir = applicationInfo.publicSourceDir;
+ publicSrcDir = applicationInfo.getBaseResourcePath();
}
}
// TODO: extend to measure size of split APKs
@@ -12862,7 +12813,7 @@
Message msg = mHandler.obtainMessage(INIT_COPY);
final String instructionSet = getAppInstructionSet(pkg.applicationInfo);
InstallArgs srcArgs = createInstallArgsForExisting(currFlags,
- pkg.applicationInfo.sourceDir, pkg.applicationInfo.publicSourceDir,
+ pkg.applicationInfo.getCodePath(), pkg.applicationInfo.getResourcePath(),
pkg.applicationInfo.nativeLibraryDir, instructionSet);
MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
instructionSet, pkg.applicationInfo.uid, user);
@@ -12889,10 +12840,11 @@
Slog.w(TAG, " Package " + mp.packageName
+ " doesn't exist. Aborting move");
returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
- } else if (!mp.srcArgs.getCodePath().equals(pkg.applicationInfo.sourceDir)) {
+ } else if (!mp.srcArgs.getCodePath().equals(
+ pkg.applicationInfo.getCodePath())) {
Slog.w(TAG, "Package " + mp.packageName + " code path changed from "
+ mp.srcArgs.getCodePath() + " to "
- + pkg.applicationInfo.sourceDir
+ + pkg.applicationInfo.getCodePath()
+ " Aborting move and returning error");
returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
} else {
@@ -12916,10 +12868,10 @@
+ " doesn't exist. Aborting move");
returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
} else if (!mp.srcArgs.getCodePath().equals(
- pkg.applicationInfo.sourceDir)) {
+ pkg.applicationInfo.getCodePath())) {
Slog.w(TAG, "Package " + mp.packageName
+ " code path changed from " + mp.srcArgs.getCodePath()
- + " to " + pkg.applicationInfo.sourceDir
+ + " to " + pkg.applicationInfo.getCodePath()
+ " Aborting move and returning error");
returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
} else {
@@ -12974,14 +12926,19 @@
}
if (returnCode == PackageManager.MOVE_SUCCEEDED) {
- pkg.applicationInfo.sourceDir = newCodePath;
- pkg.applicationInfo.publicSourceDir = newResPath;
+ pkg.applicationInfo.setCodePath(newCodePath);
+ pkg.applicationInfo.setBaseCodePath(newCodePath);
+ pkg.applicationInfo.setSplitCodePaths(null);
+ pkg.applicationInfo.setResourcePath(newResPath);
+ pkg.applicationInfo.setBaseResourcePath(newResPath);
+ pkg.applicationInfo.setSplitResourcePaths(null);
pkg.applicationInfo.nativeLibraryDir = newNativePath;
+
PackageSetting ps = (PackageSetting) pkg.mExtras;
- ps.codePath = new File(pkg.applicationInfo.sourceDir);
+ ps.codePath = new File(pkg.applicationInfo.getCodePath());
ps.codePathString = ps.codePath.getPath();
ps.resourcePath = new File(
- pkg.applicationInfo.publicSourceDir);
+ pkg.applicationInfo.getResourcePath());
ps.resourcePathString = ps.resourcePath.getPath();
ps.nativeLibraryPathString = newNativePath;
// Set the application info flag
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3bb487b..ff075e3 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -683,8 +683,8 @@
p.pkg = pkg;
// pkg.mSetEnabled = p.getEnabled(userId);
// pkg.mSetStopped = p.getStopped(userId);
- final String codePath = pkg.applicationInfo.sourceDir;
- final String resourcePath = pkg.applicationInfo.publicSourceDir;
+ final String codePath = pkg.applicationInfo.getCodePath();
+ final String resourcePath = pkg.applicationInfo.getResourcePath();
// Update code path if needed
if (!codePath.equalsIgnoreCase(p.codePathString)) {
Slog.w(PackageManagerService.TAG, "Code path for pkg : " + p.pkg.packageName +
@@ -1958,6 +1958,13 @@
void writeSigningKeySetsLPr(XmlSerializer serializer,
PackageKeySetData data) throws IOException {
if (data.getSigningKeySets() != null) {
+ // Keep track of the original signing-keyset.
+ // Must be recorded first, since it will be read first and wipe the
+ // current signing-keysets for the package when set.
+ long properSigningKeySet = data.getProperSigningKeySet();
+ serializer.startTag(null, "proper-signing-keyset");
+ serializer.attribute(null, "identifier", Long.toString(properSigningKeySet));
+ serializer.endTag(null, "proper-signing-keyset");
for (long id : data.getSigningKeySets()) {
serializer.startTag(null, "signing-keyset");
serializer.attribute(null, "identifier", Long.toString(id));
@@ -2917,6 +2924,9 @@
} else if (tagName.equals("perms")) {
readGrantedPermissionsLPw(parser, packageSetting.grantedPermissions);
packageSetting.permissionsFixed = true;
+ } else if (tagName.equals("proper-signing-keyset")) {
+ long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
+ packageSetting.keySetData.setProperSigningKeySet(id);
} else if (tagName.equals("signing-keyset")) {
long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
packageSetting.keySetData.addSigningKeySet(id);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 02dd1bf..bf767e2 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -485,6 +485,27 @@
}
@Override
+ public void onChannelRetuned(Uri channelUri) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
+ }
+ if (sessionState.mSession == null || sessionState.mClient == null) {
+ return;
+ }
+ try {
+ // TODO: Consider adding this channel change in the watch log. When we do
+ // that, how we can protect the watch log from malicious tv inputs should
+ // be addressed. e.g. add a field which represents where the channel change
+ // originated from.
+ sessionState.mClient.onChannelRetuned(channelUri, sessionState.mSeq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onChannelRetuned");
+ }
+ }
+ }
+
+ @Override
public void onSessionEvent(String eventType, Bundle eventArgs) {
synchronized (mLock) {
if (DEBUG) {
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
index d275e4d..71a7aa6 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -162,8 +162,10 @@
void propagateHotplugEvent(const hotplug_event_t& event) {
// Note that this method should be called in service thread.
JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jint port = event.port;
+ jboolean connected = (jboolean) event.connected;
env->CallVoidMethod(mController->getCallbacksObj(),
- gHdmiCecControllerClassInfo.handleHotplug, event.connected);
+ gHdmiCecControllerClassInfo.handleHotplug, port, connected);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
@@ -314,7 +316,7 @@
GET_METHOD_ID(gHdmiCecControllerClassInfo.handleIncomingCecCommand, clazz,
"handleIncomingCecCommand", "(II[B)V");
GET_METHOD_ID(gHdmiCecControllerClassInfo.handleHotplug, clazz,
- "handleHotplug", "(Z)V");
+ "handleHotplug", "(IZ)V");
return reinterpret_cast<jlong>(controller);
}
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 64d418a..a9d5c72 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -68,6 +68,166 @@
////////////////////////////////////////////////////////////////////////////////
+class BufferProducerThread : public Thread {
+public:
+ BufferProducerThread(tv_input_device_t* device, int deviceId, const tv_stream_t* stream);
+
+ virtual status_t readyToRun();
+
+ void setSurface(const sp<Surface>& surface);
+ void onCaptured(uint32_t seq, bool succeeded);
+ void shutdown();
+
+private:
+ Mutex mLock;
+ Condition mCondition;
+ sp<Surface> mSurface;
+ tv_input_device_t* mDevice;
+ int mDeviceId;
+ tv_stream_t mStream;
+ sp<ANativeWindowBuffer_t> mBuffer;
+ enum {
+ CAPTURING,
+ CAPTURED,
+ RELEASED,
+ } mBufferState;
+ uint32_t mSeq;
+ bool mShutdown;
+
+ virtual bool threadLoop();
+
+ void setSurfaceLocked(const sp<Surface>& surface);
+};
+
+BufferProducerThread::BufferProducerThread(
+ tv_input_device_t* device, int deviceId, const tv_stream_t* stream)
+ : Thread(false),
+ mDevice(device),
+ mDeviceId(deviceId),
+ mBuffer(NULL),
+ mBufferState(RELEASED),
+ mSeq(0u),
+ mShutdown(false) {
+ memcpy(&mStream, stream, sizeof(mStream));
+}
+
+status_t BufferProducerThread::readyToRun() {
+ sp<ANativeWindow> anw(mSurface);
+ status_t err = native_window_set_usage(anw.get(), mStream.buffer_producer.usage);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = native_window_set_buffers_dimensions(
+ anw.get(), mStream.buffer_producer.width, mStream.buffer_producer.height);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = native_window_set_buffers_format(anw.get(), mStream.buffer_producer.format);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ return NO_ERROR;
+}
+
+void BufferProducerThread::setSurface(const sp<Surface>& surface) {
+ Mutex::Autolock autoLock(&mLock);
+ setSurfaceLocked(surface);
+}
+
+void BufferProducerThread::setSurfaceLocked(const sp<Surface>& surface) {
+ if (surface == mSurface) {
+ return;
+ }
+
+ if (mBufferState == CAPTURING) {
+ mDevice->cancel_capture(mDevice, mDeviceId, mStream.stream_id, mSeq);
+ }
+ while (mBufferState == CAPTURING) {
+ status_t err = mCondition.waitRelative(mLock, s2ns(1));
+ if (err != NO_ERROR) {
+ ALOGE("error %d while wating for buffer state to change.", err);
+ break;
+ }
+ }
+ mBuffer.clear();
+ mBufferState = RELEASED;
+
+ mSurface = surface;
+ mCondition.broadcast();
+}
+
+void BufferProducerThread::onCaptured(uint32_t seq, bool succeeded) {
+ Mutex::Autolock autoLock(&mLock);
+ if (seq != mSeq) {
+ ALOGW("Incorrect sequence value: expected %u actual %u", mSeq, seq);
+ }
+ if (mBufferState != CAPTURING) {
+ ALOGW("mBufferState != CAPTURING : instead %d", mBufferState);
+ }
+ if (succeeded) {
+ mBufferState = CAPTURED;
+ } else {
+ mBuffer.clear();
+ mBufferState = RELEASED;
+ }
+ mCondition.broadcast();
+}
+
+void BufferProducerThread::shutdown() {
+ Mutex::Autolock autoLock(&mLock);
+ mShutdown = true;
+ setSurfaceLocked(NULL);
+ requestExitAndWait();
+}
+
+bool BufferProducerThread::threadLoop() {
+ Mutex::Autolock autoLock(&mLock);
+
+ status_t err = NO_ERROR;
+ if (mSurface == NULL) {
+ err = mCondition.waitRelative(mLock, s2ns(1));
+ // It's OK to time out here.
+ if (err != NO_ERROR && err != TIMED_OUT) {
+ ALOGE("error %d while wating for non-null surface to be set", err);
+ return false;
+ }
+ return true;
+ }
+ sp<ANativeWindow> anw(mSurface);
+ while (mBufferState == CAPTURING) {
+ err = mCondition.waitRelative(mLock, s2ns(1));
+ if (err != NO_ERROR) {
+ ALOGE("error %d while wating for buffer state to change.", err);
+ return false;
+ }
+ }
+ if (mBufferState == CAPTURED && anw != NULL) {
+ err = anw->queueBuffer(anw.get(), mBuffer.get(), -1);
+ if (err != NO_ERROR) {
+ ALOGE("error %d while queueing buffer to surface", err);
+ return false;
+ }
+ mBuffer.clear();
+ mBufferState = RELEASED;
+ }
+ if (mBuffer == NULL && !mShutdown && anw != NULL) {
+ ANativeWindowBuffer_t* buffer = NULL;
+ err = native_window_dequeue_buffer_and_wait(anw.get(), &buffer);
+ if (err != NO_ERROR) {
+ ALOGE("error %d while dequeueing buffer to surface", err);
+ return false;
+ }
+ mBuffer = buffer;
+ mBufferState = CAPTURING;
+ mDevice->request_capture(mDevice, mDeviceId, mStream.stream_id,
+ buffer->handle, ++mSeq);
+ }
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
class JTvInputHal {
public:
~JTvInputHal();
@@ -79,23 +239,31 @@
const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);
private:
+ // Connection between a surface and a stream.
class Connection {
public:
Connection() {}
sp<Surface> mSurface;
+ tv_stream_type_t mStreamType;
+
+ // Only valid when mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE
sp<NativeHandle> mSourceHandle;
+ // Only valid when mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER
+ sp<BufferProducerThread> mThread;
};
JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev);
static void notify(
- tv_input_device_t* dev,tv_input_event_t* event, void* data);
+ tv_input_device_t* dev, tv_input_event_t* event, void* data);
void onDeviceAvailable(const tv_input_device_info_t& info);
void onDeviceUnavailable(int deviceId);
void onStreamConfigurationsChanged(int deviceId);
+ void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded);
+ Mutex mLock;
jweak mThiz;
tv_input_device_t* mDevice;
tv_input_callback_ops_t mCallback;
@@ -153,11 +321,16 @@
// Nothing to do
return NO_ERROR;
}
- if (Surface::isValid(connection.mSurface)) {
+ // Clear the surface in the connection.
+ if (connection.mSurface != NULL) {
+ if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
+ if (Surface::isValid(connection.mSurface)) {
+ connection.mSurface->setSidebandStream(NULL);
+ }
+ }
connection.mSurface.clear();
}
- connection.mSurface = surface;
- if (connection.mSourceHandle == NULL) {
+ if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
// Need to configure stream
int numConfigs = 0;
const tv_stream_config_t* configs = NULL;
@@ -177,22 +350,32 @@
ALOGE("Cannot find a config with given stream ID: %d", streamId);
return BAD_VALUE;
}
- // TODO: handle buffer producer profile.
- if (configs[configIndex].type !=
- TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
- ALOGE("Profiles other than independent video source is not yet "
- "supported : type = %d", configs[configIndex].type);
- return INVALID_OPERATION;
- }
tv_stream_t stream;
stream.stream_id = configs[configIndex].stream_id;
+ if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
+ stream.buffer_producer.width = configs[configIndex].max_video_width;
+ stream.buffer_producer.height = configs[configIndex].max_video_height;
+ }
if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
ALOGE("Couldn't add stream");
return UNKNOWN_ERROR;
}
- connection.mSourceHandle = NativeHandle::create(
- stream.sideband_stream_source_handle, false);
+ if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
+ connection.mSourceHandle = NativeHandle::create(
+ stream.sideband_stream_source_handle, false);
+ } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
+ if (connection.mThread != NULL) {
+ connection.mThread->shutdown();
+ }
+ connection.mThread = new BufferProducerThread(mDevice, deviceId, &stream);
+ connection.mThread->run();
+ }
+ }
+ connection.mSurface = surface;
+ if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
connection.mSurface->setSidebandStream(connection.mSourceHandle);
+ } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
+ connection.mThread->setSurface(surface);
}
return NO_ERROR;
}
@@ -220,6 +403,12 @@
ALOGE("Couldn't remove stream");
return BAD_VALUE;
}
+
+ // Clear everything
+ if (connection.mThread != NULL) {
+ connection.mThread->shutdown();
+ connection.mThread.clear();
+ }
connection.mSourceHandle.clear();
}
return NO_ERROR;
@@ -249,14 +438,29 @@
case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
thiz->onStreamConfigurationsChanged(event->device_info.device_id);
} break;
+ case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: {
+ thiz->onCaptured(event->capture_result.device_id,
+ event->capture_result.stream_id,
+ event->capture_result.seq,
+ true /* succeeded */);
+ } break;
+ case TV_INPUT_EVENT_CAPTURE_FAILED: {
+ thiz->onCaptured(event->capture_result.device_id,
+ event->capture_result.stream_id,
+ event->capture_result.seq,
+ false /* succeeded */);
+ } break;
default:
ALOGE("Unrecognizable event");
}
}
void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
+ {
+ Mutex::Autolock autoLock(&mLock);
+ mConnections.add(info.device_id, KeyedVector<int, Connection>());
+ }
JNIEnv* env = AndroidRuntime::getJNIEnv();
- mConnections.add(info.device_id, KeyedVector<int, Connection>());
jobject builder = env->NewObject(
gTvInputHardwareInfoBuilderClassInfo.clazz,
@@ -290,13 +494,16 @@
}
void JTvInputHal::onDeviceUnavailable(int deviceId) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
- for (size_t i = 0; i < connections.size(); ++i) {
- removeStream(deviceId, connections.keyAt(i));
+ {
+ Mutex::Autolock autoLock(&mLock);
+ KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
+ for (size_t i = 0; i < connections.size(); ++i) {
+ removeStream(deviceId, connections.keyAt(i));
+ }
+ connections.clear();
+ mConnections.removeItem(deviceId);
}
- connections.clear();
- mConnections.removeItem(deviceId);
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(
mThiz,
gTvInputHalClassInfo.deviceUnavailable,
@@ -304,18 +511,36 @@
}
void JTvInputHal::onStreamConfigurationsChanged(int deviceId) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
- for (size_t i = 0; i < connections.size(); ++i) {
- removeStream(deviceId, connections.keyAt(i));
+ {
+ Mutex::Autolock autoLock(&mLock);
+ KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
+ for (size_t i = 0; i < connections.size(); ++i) {
+ removeStream(deviceId, connections.keyAt(i));
+ }
+ connections.clear();
}
- connections.clear();
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(
mThiz,
gTvInputHalClassInfo.streamConfigsChanged,
deviceId);
}
+void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) {
+ sp<BufferProducerThread> thread;
+ {
+ Mutex::Autolock autoLock(&mLock);
+ KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
+ Connection& connection = connections.editValueFor(streamId);
+ if (connection.mThread == NULL) {
+ ALOGE("capture thread not existing.");
+ return;
+ }
+ thread = connection.mThread;
+ }
+ thread->onCaptured(seq, succeeded);
+}
+
////////////////////////////////////////////////////////////////////////////////
static jlong nativeOpen(JNIEnv* env, jobject thiz) {
diff --git a/telecomm/java/android/telecomm/CallInfo.aidl b/telecomm/java/android/telecomm/CallInfo.aidl
deleted file mode 100644
index bc5ef96..0000000
--- a/telecomm/java/android/telecomm/CallInfo.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telecomm;
-
-parcelable CallInfo;
diff --git a/telecomm/java/android/telecomm/CallInfo.java b/telecomm/java/android/telecomm/CallInfo.java
deleted file mode 100644
index 3810d7c..0000000
--- a/telecomm/java/android/telecomm/CallInfo.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telecomm;
-
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * A parcelable holder class of Call information data. This class is intended for transferring call
- * information from Telecomm to call services and thus is read-only.
- * TODO(santoscordon): Need final public-facing comments in this file.
- */
-public final class CallInfo implements Parcelable {
-
- /**
- * Unique identifier for the call.
- */
- private final String mId;
-
- /**
- * The state of the call.
- */
- private final CallState mState;
-
- /**
- * Endpoint to which the call is connected.
- * This could be the dialed value for outgoing calls or the caller id of incoming calls.
- */
- private final Uri mHandle;
-
- /**
- * Gateway information for the call.
- */
- private final GatewayInfo mGatewayInfo;
-
- /**
- * Account information for the call.
- */
- private final PhoneAccount mAccount;
-
- /**
- * Additional information that can be persisted.
- */
- private final Bundle mExtras;
-
- /** The descriptor for the call service currently routing this call. */
- private final CallServiceDescriptor mCurrentCallServiceDescriptor;
-
- public CallInfo(String id, CallState state, Uri handle) {
- this(id, state, handle, null, null, Bundle.EMPTY, null);
- }
-
- /**
- * Persists handle of the other party of this call.
- *
- * @param id The unique ID of the call.
- * @param state The state of the call.
- * @param handle The handle to the other party in this call.
- * @param gatewayInfo Gateway information pertaining to this call.
- * @param account Account information pertaining to this call.
- * @param extras Additional information that can be persisted.
- * @param currentCallServiceDescriptor The descriptor for the call service currently routing
- * this call.
- *
- * @hide
- */
- public CallInfo(
- String id,
- CallState state,
- Uri handle,
- GatewayInfo gatewayInfo,
- PhoneAccount account,
- Bundle extras,
- CallServiceDescriptor currentCallServiceDescriptor) {
- mId = id;
- mState = state;
- mHandle = handle;
- mGatewayInfo = gatewayInfo;
- mAccount = account;
- mExtras = extras;
- mCurrentCallServiceDescriptor = currentCallServiceDescriptor;
- }
-
- public String getId() {
- return mId;
- }
-
- public CallState getState() {
- return mState;
- }
-
- public Uri getHandle() {
- return mHandle;
- }
-
- /**
- * @return The actual handle this call is associated with. This is used by call services to
- * correctly indicate in their UI what handle the user is actually calling, and by other
- * telecomm components that require the user-dialed handle to function.
- */
- public Uri getOriginalHandle() {
- if (mGatewayInfo != null) {
- return mGatewayInfo.getOriginalHandle();
- }
- return getHandle();
- }
-
- public GatewayInfo getGatewayInfo() {
- return mGatewayInfo;
- }
-
- public PhoneAccount getAccount() {
- return mAccount;
- }
-
- public Bundle getExtras() {
- return mExtras;
- }
-
- public CallServiceDescriptor getCurrentCallServiceDescriptor() {
- return mCurrentCallServiceDescriptor;
- }
-
- /**
- * Responsible for creating CallInfo objects for deserialized Parcels.
- */
- public static final Parcelable.Creator<CallInfo> CREATOR = new Parcelable.Creator<CallInfo> () {
- @Override
- public CallInfo createFromParcel(Parcel source) {
- String id = source.readString();
- CallState state = CallState.valueOf(source.readString());
- Uri handle = Uri.CREATOR.createFromParcel(source);
-
- GatewayInfo gatewayInfo = readProviderInfoIfExists(source, GatewayInfo.CREATOR);
- PhoneAccount account = readProviderInfoIfExists(source, PhoneAccount.CREATOR);
-
- ClassLoader classLoader = CallInfo.class.getClassLoader();
- Bundle extras = source.readParcelable(classLoader);
- CallServiceDescriptor descriptor = source.readParcelable(classLoader);
- return new CallInfo(id, state, handle, gatewayInfo, account, extras, descriptor);
- }
-
- @Override
- public CallInfo[] newArray(int size) {
- return new CallInfo[size];
- }
- };
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /**
- * Writes CallInfo object into a serializeable Parcel.
- */
- @Override
- public void writeToParcel(Parcel destination, int flags) {
- destination.writeString(mId);
- destination.writeString(mState.name());
- mHandle.writeToParcel(destination, 0);
-
- writeProviderInfoIfExists(destination, mGatewayInfo);
- writeProviderInfoIfExists(destination, mAccount);
-
- destination.writeParcelable(mExtras, 0);
- destination.writeParcelable(mCurrentCallServiceDescriptor, 0);
- }
-
- /**
- * Helper function to write provider information (either GatewayInfo or Account) to
- * parcel. Will write a false byte if the information does not exist.
- */
- private void writeProviderInfoIfExists(Parcel destination, Parcelable provider) {
- if (provider != null) {
- destination.writeByte((byte) 1);
- provider.writeToParcel(destination, 0);
- } else {
- destination.writeByte((byte) 0);
- }
- }
-
- /**
- * Helper function to read provider information (either GatewayInfo or Account) from
- * parcel.
- */
- private static <T> T readProviderInfoIfExists(Parcel source,
- Parcelable.Creator<T> creator) {
- if (source.readByte() != 0) {
- return creator.createFromParcel(source);
- }
- return null;
- }
-}
diff --git a/telecomm/java/android/telecomm/CallService.java b/telecomm/java/android/telecomm/CallService.java
deleted file mode 100644
index e77fb23..0000000
--- a/telecomm/java/android/telecomm/CallService.java
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telecomm;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-
-import com.android.internal.os.SomeArgs;
-import com.android.internal.telecomm.ICallService;
-import com.android.internal.telecomm.ICallServiceAdapter;
-
-/**
- * Base implementation of CallService which can be used to provide calls for the system
- * in-call UI. CallService is a one-way service from the framework's CallsManager to any app
- * that would like to provide calls managed by the default system in-call user interface.
- * TODO(santoscordon): Needs more about AndroidManifest.xml service registrations before
- * we can unhide this API.
- *
- * Most public methods of this function are backed by a one-way AIDL interface which precludes
- * synchronous responses. As a result, most responses are handled by (or have TODOs to handle)
- * response objects instead of return values.
- * TODO(santoscordon): Improve paragraph above once the final design is in place.
- */
-public abstract class CallService extends Service {
-
- private static final int MSG_SET_CALL_SERVICE_ADAPTER = 1;
- private static final int MSG_CALL = 2;
- private static final int MSG_ABORT = 3;
- private static final int MSG_SET_INCOMING_CALL_ID = 4;
- private static final int MSG_ANSWER = 5;
- private static final int MSG_REJECT = 6;
- private static final int MSG_DISCONNECT = 7;
- private static final int MSG_HOLD = 8;
- private static final int MSG_UNHOLD = 9;
- private static final int MSG_ON_AUDIO_STATE_CHANGED = 10;
- private static final int MSG_PLAY_DTMF_TONE = 11;
- private static final int MSG_STOP_DTMF_TONE = 12;
- private static final int MSG_CONFERENCE = 13;
- private static final int MSG_SPLIT_FROM_CONFERENCE = 14;
- private static final int MSG_ON_POST_DIAL_CONTINUE = 15;
- private static final int MSG_ON_PHONE_ACCOUNT_CLICKED = 16;
-
- /**
- * Default Handler used to consolidate binder method calls onto a single thread.
- */
- private final class CallServiceMessageHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SET_CALL_SERVICE_ADAPTER:
- mAdapter.addAdapter((ICallServiceAdapter) msg.obj);
- onAdapterAttached(mAdapter);
- break;
- case MSG_CALL:
- call((CallInfo) msg.obj);
- break;
- case MSG_ABORT:
- abort((String) msg.obj);
- break;
- case MSG_SET_INCOMING_CALL_ID: {
- SomeArgs args = (SomeArgs) msg.obj;
- try {
- String callId = (String) args.arg1;
- Bundle extras = (Bundle) args.arg2;
- setIncomingCallId(callId, extras);
- } finally {
- args.recycle();
- }
- break;
- }
- case MSG_ANSWER:
- answer((String) msg.obj);
- break;
- case MSG_REJECT:
- reject((String) msg.obj);
- break;
- case MSG_DISCONNECT:
- disconnect((String) msg.obj);
- break;
- case MSG_HOLD:
- hold((String) msg.obj);
- break;
- case MSG_UNHOLD:
- unhold((String) msg.obj);
- break;
- case MSG_ON_AUDIO_STATE_CHANGED: {
- SomeArgs args = (SomeArgs) msg.obj;
- try {
- String callId = (String) args.arg1;
- CallAudioState audioState = (CallAudioState) args.arg2;
- onAudioStateChanged(callId, audioState);
- } finally {
- args.recycle();
- }
- break;
- }
- case MSG_PLAY_DTMF_TONE:
- playDtmfTone((String) msg.obj, (char) msg.arg1);
- break;
- case MSG_STOP_DTMF_TONE:
- stopDtmfTone((String) msg.obj);
- break;
- case MSG_CONFERENCE: {
- SomeArgs args = (SomeArgs) msg.obj;
- try {
- String conferenceCallId = (String) args.arg1;
- String callId = (String) args.arg2;
- conference(conferenceCallId, callId);
- } finally {
- args.recycle();
- }
- break;
- }
- case MSG_ON_POST_DIAL_CONTINUE: {
- SomeArgs args = (SomeArgs) msg.obj;
- try {
- String callId = (String) args.arg1;
- boolean proceed = (args.argi1 == 1);
- onPostDialContinue(callId, proceed);
- } finally {
- args.recycle();
- }
- break;
- }
- case MSG_SPLIT_FROM_CONFERENCE:
- splitFromConference((String) msg.obj);
- break;
- case MSG_ON_PHONE_ACCOUNT_CLICKED:
- onPhoneAccountClicked((String) msg.obj);
- break;
- default:
- break;
- }
- }
- }
-
- /**
- * Default ICallService implementation provided to CallsManager via {@link #onBind}.
- */
- private final class CallServiceBinder extends ICallService.Stub {
- @Override
- public void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) {
- mMessageHandler.obtainMessage(MSG_SET_CALL_SERVICE_ADAPTER, callServiceAdapter)
- .sendToTarget();
- }
-
- @Override
- public void call(CallInfo callInfo) {
- mMessageHandler.obtainMessage(MSG_CALL, callInfo).sendToTarget();
- }
-
- @Override
- public void abort(String callId) {
- mMessageHandler.obtainMessage(MSG_ABORT, callId).sendToTarget();
- }
-
- @Override
- public void setIncomingCallId(String callId, Bundle extras) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = callId;
- args.arg2 = extras;
- mMessageHandler.obtainMessage(MSG_SET_INCOMING_CALL_ID, args).sendToTarget();
- }
-
- @Override
- public void answer(String callId) {
- mMessageHandler.obtainMessage(MSG_ANSWER, callId).sendToTarget();
- }
-
- @Override
- public void reject(String callId) {
- mMessageHandler.obtainMessage(MSG_REJECT, callId).sendToTarget();
- }
-
- @Override
- public void disconnect(String callId) {
- mMessageHandler.obtainMessage(MSG_DISCONNECT, callId).sendToTarget();
- }
-
- @Override
- public void hold(String callId) {
- mMessageHandler.obtainMessage(MSG_HOLD, callId).sendToTarget();
- }
-
- @Override
- public void unhold(String callId) {
- mMessageHandler.obtainMessage(MSG_UNHOLD, callId).sendToTarget();
- }
-
- @Override
- public void playDtmfTone(String callId, char digit) {
- mMessageHandler.obtainMessage(MSG_PLAY_DTMF_TONE, digit, 0, callId).sendToTarget();
- }
-
- @Override
- public void stopDtmfTone(String callId) {
- mMessageHandler.obtainMessage(MSG_STOP_DTMF_TONE, callId).sendToTarget();
- }
-
- @Override
- public void onAudioStateChanged(String callId, CallAudioState audioState) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = callId;
- args.arg2 = audioState;
- mMessageHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, args).sendToTarget();
- }
-
- @Override
- public void conference(String conferenceCallId, String callId) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = conferenceCallId;
- args.arg2 = callId;
- mMessageHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget();
- }
-
- @Override
- public void splitFromConference(String callId) {
- mMessageHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget();
- }
-
- @Override
- public void onPostDialContinue(String callId, boolean proceed) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = callId;
- args.argi1 = proceed ? 1 : 0;
- mMessageHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget();
- }
-
- @Override
- public void onPhoneAccountClicked(String callId) {
- mMessageHandler.obtainMessage(MSG_ON_PHONE_ACCOUNT_CLICKED, callId).sendToTarget();
- }
-
- }
-
- /**
- * Message handler for consolidating binder callbacks onto a single thread.
- * See {@link CallServiceMessageHandler}.
- */
- private final CallServiceMessageHandler mMessageHandler = new CallServiceMessageHandler();
-
- /**
- * Default binder implementation of {@link ICallService} interface.
- */
- private final CallServiceBinder mBinder = new CallServiceBinder();
-
- private CallServiceAdapter mAdapter = new CallServiceAdapter();
-
- /** {@inheritDoc} */
- @Override
- public final IBinder onBind(Intent intent) {
- return getBinder();
- }
-
- /**
- * Returns binder object which can be used across IPC methods.
- */
- public final IBinder getBinder() {
- return mBinder;
- }
-
- /** @hide */
- protected final CallServiceAdapter getAdapter() {
- return mAdapter;
- }
-
- /** @hide */
- protected abstract void onAdapterAttached(CallServiceAdapter adapter);
-
- /** @hide */
- protected abstract void call(CallInfo callInfo);
-
- /** @hide */
- protected abstract void abort(String callId);
-
- /** @hide */
- protected abstract void setIncomingCallId(String callId, Bundle extras);
-
- /** @hide */
- protected abstract void answer(String callId);
-
- /** @hide */
- protected abstract void reject(String callId);
-
- /** @hide */
- protected abstract void disconnect(String callId);
-
- /** @hide */
- protected abstract void hold(String callId);
-
- /** @hide */
- protected abstract void unhold(String callId);
-
- /** @hide */
- protected abstract void playDtmfTone(String callId, char digit);
-
- /** @hide */
- protected abstract void stopDtmfTone(String callId);
-
- /** @hide */
- protected abstract void onAudioStateChanged(String activeCallId, CallAudioState audioState);
-
- /** @hide */
- protected abstract void conference(String conferenceCallId, String callId);
-
- /** @hide */
- protected abstract void splitFromConference(String callId);
-
- /** @hide */
- protected abstract void onPostDialContinue(String callId, boolean proceed);
-
- /** @hide */
- protected abstract void onFeaturesChanged(String callId, int features);
-
- /** @hide */
- protected abstract void onPhoneAccountClicked(String callId);
-}
diff --git a/telecomm/java/android/telecomm/CallServiceDescriptor.java b/telecomm/java/android/telecomm/CallServiceDescriptor.java
index dec3791..5ae07d3 100644
--- a/telecomm/java/android/telecomm/CallServiceDescriptor.java
+++ b/telecomm/java/android/telecomm/CallServiceDescriptor.java
@@ -26,7 +26,7 @@
import java.util.UUID;
/**
- * An immutable object containing information about a given {@link CallService}. Instances are
+ * An immutable object containing information about a given {@link ConnectionService}. Instances are
* created using the enclosed {@link Builder}.
*/
public final class CallServiceDescriptor implements Parcelable {
@@ -40,19 +40,19 @@
/**
* Indicates that the device must be connected to a Wi-Fi network in order for the backing
- * {@link CallService} to be used.
+ * {@link ConnectionService} to be used.
*/
public static final int FLAG_WIFI = 0x01;
/**
* Indicates that the device must be connected to a cellular PSTN network in order for the
- * backing {@link CallService} to be used.
+ * backing {@link ConnectionService} to be used.
*/
public static final int FLAG_PSTN = 0x02;
/**
* Indicates that the device must be connected to a cellular data network in order for the
- * backing {@link CallService} to be used.
+ * backing {@link ConnectionService} to be used.
*/
public static final int FLAG_MOBILE = 0x04;
@@ -65,45 +65,46 @@
/**
* A unique ID used to identify a given instance.
*/
- private final String mCallServiceId;
+ private final String mConnectionServiceId;
/**
- * The {@link ComponentName} of the {@link CallService} implementation which this is describing.
+ * The {@link ComponentName} of the {@link ConnectionService} implementation which this is
+ * describing.
*/
private final ComponentName mComponentName;
/**
- * The type of connection that the {@link CallService} requires; will be one of the FLAG_*
+ * The type of connection that the {@link ConnectionService} requires; will be one of the FLAG_*
* constants defined in this class.
*/
private final int mNetworkType;
private CallServiceDescriptor(
- String callServiceId,
+ String connectionServiceId,
ComponentName componentName,
int networkType) {
- mCallServiceId = callServiceId;
+ mConnectionServiceId = connectionServiceId;
mComponentName = componentName;
mNetworkType = networkType;
}
/**
- * @return The ID used to identify this {@link CallService}.
+ * @return The ID used to identify this {@link ConnectionService}.
*/
- public String getCallServiceId() {
- return mCallServiceId;
+ public String getConnectionServiceId() {
+ return mConnectionServiceId;
}
/**
- * @return The {@link ComponentName} of the {@link CallService}.
+ * @return The {@link ComponentName} of the {@link ConnectionService}.
*/
public ComponentName getServiceComponent() {
return mComponentName;
}
/**
- * @return The network type required by the {@link CallService} to place a call.
+ * @return The network type required by the {@link ConnectionService} to place a call.
*/
public int getNetworkType() {
return mNetworkType;
@@ -119,7 +120,7 @@
return false;
}
CallServiceDescriptor descriptor = (CallServiceDescriptor) obj;
- return mCallServiceId.equals(descriptor.mCallServiceId) &&
+ return mConnectionServiceId.equals(descriptor.mConnectionServiceId) &&
mComponentName.equals(descriptor.mComponentName) &&
mNetworkType == descriptor.mNetworkType;
}
@@ -147,10 +148,10 @@
/** The {@link Context} to use to verify {@link ComponentName} ownership. */
private Context mContext;
- /** The {@link ComponentName} pointing to the backing {@link CallService}. */
+ /** The {@link ComponentName} pointing to the backing {@link ConnectionService}. */
private ComponentName mComponentName;
- /** The required network type that the {@link CallService} needs. */
+ /** The required network type that the {@link ConnectionService} needs. */
private int mNetworkType = FLAG_INVALID;
private Builder(Context context) {
@@ -158,21 +159,21 @@
}
/**
- * Set which {@link CallService} this {@link CallServiceDescriptor} is describing.
+ * Set which {@link ConnectionService} this {@link CallServiceDescriptor} is describing.
*
- * @param callServiceClass The {@link CallService} class
+ * @param serviceClass The {@link ConnectionService} class
* @return This {@link Builder} for method chaining.
*/
- public Builder setCallService(Class<? extends CallService> callServiceClass) {
- mComponentName = new ComponentName(mContext, callServiceClass);
+ public Builder setConnectionService(Class<? extends ConnectionService> serviceClass) {
+ mComponentName = new ComponentName(mContext, serviceClass);
return this;
}
/**
- * Which network type the backing {@link CallService} requires. This must be one of the
- * {@link CallServiceDescriptor}.TYPE_* fields.
+ * Which network type the backing {@link ConnectionService} requires. This must be one of
+ * the {@link CallServiceDescriptor}.TYPE_* fields.
*
- * @param networkType Which network type the backing {@link CallService} requires.
+ * @param networkType Which network type the backing {@link ConnectionService} requires.
* @return This {@link Builder} for method chaining.
*/
public Builder setNetworkType(int networkType) {
@@ -207,7 +208,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mCallServiceId);
+ dest.writeString(mConnectionServiceId);
dest.writeParcelable(mComponentName, 0);
dest.writeInt(mNetworkType);
}
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index 32dcf6c..c55e5ab 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -35,7 +35,6 @@
public interface Listener {
void onStateChanged(Connection c, int state);
void onFeaturesChanged(Connection c, int features);
- void onAudioStateChanged(Connection c, CallAudioState state);
void onHandleChanged(Connection c, Uri newHandle);
void onSignalChanged(Connection c, Bundle details);
void onDisconnected(Connection c, int cause, String message);
@@ -56,10 +55,6 @@
@Override
public void onFeaturesChanged(Connection c, int features) {}
- /** {@inheritDoc} */
- @Override
- public void onAudioStateChanged(Connection c, CallAudioState state) {}
-
@Override
public void onHandleChanged(Connection c, Uri newHandle) {}
@@ -141,7 +136,9 @@
* @return The features of the call. These are items for which the InCall UI may wish to
* display a visual indicator.
*/
- public final int getFeatures() { return mFeatures; }
+ public final int getFeatures() {
+ return mFeatures;
+ }
/**
* @return The audio state of the call, describing how its audio is currently
@@ -153,6 +150,21 @@
}
/**
+ * Returns whether this connection is requesting that the system play a ringback tone
+ * on its behalf.
+ */
+ public final boolean isRequestingRingback() {
+ return mRequestingRingback;
+ }
+
+ /**
+ * Returns whether this connection is a conference connection (has child connections).
+ */
+ public final boolean isConferenceConnection() {
+ return !mChildConnections.isEmpty();
+ }
+
+ /**
* Assign a listener to be notified of state changes.
*
* @param l A listener.
@@ -179,151 +191,14 @@
}
/**
- * Play a DTMF tone in this Connection.
- *
- * @param c A DTMF character.
- *
- * @hide
- */
- public final void playDtmfTone(char c) {
- Log.d(this, "playDtmfTone %c", c);
- onPlayDtmfTone(c);
- }
-
- /**
- * Stop any DTMF tones which may be playing in this Connection.
- *
- * @hide
- */
- public final void stopDtmfTone() {
- Log.d(this, "stopDtmfTone");
- onStopDtmfTone();
- }
-
- /**
- * Disconnect this Connection. If and when the Connection can comply with
- * this request, it will transition to the {@link State#DISCONNECTED}
- * state and notify its listeners.
- *
- * @hide
- */
- public final void disconnect() {
- Log.d(this, "disconnect");
- onDisconnect();
- }
-
- /**
- * Separates this Connection from a parent connection.
- *
- * @hide
- */
- public final void separate() {
- Log.d(this, "separate");
- onSeparate();
- }
-
- /**
- * Abort this Connection. The Connection will immediately transition to
- * the {@link State#DISCONNECTED} state, and send no notifications of this
- * or any other future events.
- *
- * @hide
- */
- public final void abort() {
- Log.d(this, "abort");
- onAbort();
- }
-
- /**
- * Place this Connection on hold. If and when the Connection can comply with
- * this request, it will transition to the {@link State#HOLDING}
- * state and notify its listeners.
- *
- * @hide
- */
- public final void hold() {
- Log.d(this, "hold");
- onHold();
- }
-
- /**
- * Un-hold this Connection. If and when the Connection can comply with
- * this request, it will transition to the {@link State#ACTIVE}
- * state and notify its listeners.
- *
- * @hide
- */
- public final void unhold() {
- Log.d(this, "unhold");
- onUnhold();
- }
-
- /**
- * Accept a {@link State#RINGING} Connection. If and when the Connection
- * can comply with this request, it will transition to the {@link State#ACTIVE}
- * state and notify its listeners.
- *
- * @hide
- */
- public final void answer() {
- Log.d(this, "answer");
- if (mState == State.RINGING) {
- onAnswer();
- }
- }
-
- /**
- * Reject a {@link State#RINGING} Connection. If and when the Connection
- * can comply with this request, it will transition to the {@link State#ACTIVE}
- * state and notify its listeners.
- *
- * @hide
- */
- public final void reject() {
- Log.d(this, "reject");
- if (mState == State.RINGING) {
- onReject();
- }
- }
-
- /**
- * TODO(santoscordon): Needs updated documentation.
- *
- * @hide
- */
- public final void conference() {
- Log.d(this, "conference");
- onConference();
- }
-
- /**
- * Set the features applicable to the connection. These are items for which the InCall UI may
- * wish to display a visual indicator.
- * Features are defined in {@link android.telecomm.CallFeatures} and are passed in as a
- * bit-mask.
- *
- * @param features The features active.
- */
- public final void setFeatures(int features) {
- Log.d(this, "setFeatures %d", features);
- this.mFeatures = features;
- for (Listener l : mListeners) {
- l.onFeaturesChanged(this, mFeatures);
- }
- }
-
- /**
* Inform this Connection that the state of its audio output has been changed externally.
*
* @param state The new audio state.
* @hide
*/
- public final void setAudioState(CallAudioState state) {
+ final void setAudioState(CallAudioState state) {
Log.d(this, "setAudioState %s", state);
mCallAudioState = state;
- for (Listener l : mListeners) {
- l.onAudioStateChanged(this, state);
- }
onSetAudioState(state);
}
@@ -352,21 +227,6 @@
}
/**
- * Returns whether this connection is requesting that the system play a ringback tone
- * on its behalf.
- */
- public final boolean isRequestingRingback() {
- return mRequestingRingback;
- }
-
- /**
- * Returns whether this connection is a conference connection (has child connections).
- */
- public final boolean isConferenceConnection() {
- return !mChildConnections.isEmpty();
- }
-
- /**
* TODO(santoscordon): Needs documentation.
*/
public final void setParentConnection(Connection parentConnection) {
@@ -415,6 +275,22 @@
}
/**
+ * Set the features applicable to the connection. These are items for which the InCall UI may
+ * wish to display a visual indicator.
+ * Features are defined in {@link android.telecomm.CallFeatures} and are passed in as a
+ * bit-mask.
+ *
+ * @param features The features active.
+ */
+ public final void setFeatures(int features) {
+ Log.d(this, "setFeatures %d", features);
+ mFeatures = features;
+ for (Listener l : mListeners) {
+ l.onFeaturesChanged(this, mFeatures);
+ }
+ }
+
+ /**
* Sets state to active (e.g., an ongoing call where two or more parties can actively
* communicate).
*/
@@ -604,11 +480,6 @@
/**
* TODO(santoscordon): Needs documentation.
*/
- protected void onConference() {}
-
- /**
- * TODO(santoscordon): Needs documentation.
- */
protected void onChildrenChanged(List<Connection> children) {}
/**
diff --git a/telecomm/java/android/telecomm/ConnectionRequest.java b/telecomm/java/android/telecomm/ConnectionRequest.java
index a9aa5dd..ed61622 100644
--- a/telecomm/java/android/telecomm/ConnectionRequest.java
+++ b/telecomm/java/android/telecomm/ConnectionRequest.java
@@ -80,9 +80,6 @@
mExtras == null ? "" : mExtras);
}
- /**
- * Responsible for creating CallInfo objects for deserialized Parcels.
- */
public static final Parcelable.Creator<ConnectionRequest> CREATOR =
new Parcelable.Creator<ConnectionRequest> () {
@Override
@@ -107,9 +104,6 @@
return 0;
}
- /**
- * Writes CallInfo object into a serializeable Parcel.
- */
@Override
public void writeToParcel(Parcel destination, int flags) {
destination.writeString(mCallId);
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index d6970e2..3b763957 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -16,16 +16,19 @@
package android.telecomm;
+import android.app.Service;
+import android.content.Intent;
import android.content.ComponentName;
import android.net.Uri;
import android.os.Bundle;
-
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.telephony.DisconnectCause;
+import android.os.Message;
-import com.android.internal.telecomm.ICallService;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telecomm.IConnectionService;
+import com.android.internal.telecomm.IConnectionServiceAdapter;
import com.android.internal.telecomm.RemoteServiceCallback;
import java.util.Collection;
@@ -34,23 +37,39 @@
import java.util.Map;
/**
- * A {@link android.app.Service} that provides telephone connections to
- * processes running on an Android device.
+ * A {@link android.app.Service} that provides telephone connections to processes running on an
+ * Android device.
*/
-public abstract class ConnectionService extends CallService {
+public abstract class ConnectionService extends Service {
// Flag controlling whether PII is emitted into the logs
private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
private static final Connection NULL_CONNECTION = new Connection() {};
- // Mappings from Connections to IDs as understood by the current CallService implementation
+ private static final int MSG_ADD_CALL_SERVICE_ADAPTER = 1;
+ private static final int MSG_CALL = 2;
+ private static final int MSG_ABORT = 3;
+ private static final int MSG_CREATE_INCOMING_CALL = 4;
+ private static final int MSG_ANSWER = 5;
+ private static final int MSG_REJECT = 6;
+ private static final int MSG_DISCONNECT = 7;
+ private static final int MSG_HOLD = 8;
+ private static final int MSG_UNHOLD = 9;
+ private static final int MSG_ON_AUDIO_STATE_CHANGED = 10;
+ private static final int MSG_PLAY_DTMF_TONE = 11;
+ private static final int MSG_STOP_DTMF_TONE = 12;
+ private static final int MSG_CONFERENCE = 13;
+ private static final int MSG_SPLIT_FROM_CONFERENCE = 14;
+ private static final int MSG_ON_POST_DIAL_CONTINUE = 15;
+ private static final int MSG_ON_PHONE_ACCOUNT_CLICKED = 16;
+
private final Map<String, Connection> mConnectionById = new HashMap<>();
private final Map<Connection, String> mIdByConnection = new HashMap<>();
private final RemoteConnectionManager mRemoteConnectionManager = new RemoteConnectionManager();
- private final Handler mHandler = new Handler(Looper.getMainLooper());
private SimpleResponse<Uri, List<PhoneAccount>> mAccountLookupResponse;
private Uri mAccountLookupHandle;
private boolean mAreAccountsInitialized = false;
+ private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter();
/**
* A callback for providing the resuilt of creating a connection.
@@ -81,6 +100,180 @@
void onCancel(ConnectionRequest request);
}
+ private final IBinder mBinder = new IConnectionService.Stub() {
+ @Override
+ public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
+ mHandler.obtainMessage(MSG_ADD_CALL_SERVICE_ADAPTER, adapter).sendToTarget();
+ }
+
+ @Override
+ public void call(ConnectionRequest request) {
+ mHandler.obtainMessage(MSG_CALL, request).sendToTarget();
+ }
+
+ @Override
+ public void abort(String callId) {
+ mHandler.obtainMessage(MSG_ABORT, callId).sendToTarget();
+ }
+
+ @Override
+ public void createIncomingCall(ConnectionRequest request) {
+ mHandler.obtainMessage(MSG_CREATE_INCOMING_CALL, request).sendToTarget();
+ }
+
+ @Override
+ public void answer(String callId) {
+ mHandler.obtainMessage(MSG_ANSWER, callId).sendToTarget();
+ }
+
+ @Override
+ public void reject(String callId) {
+ mHandler.obtainMessage(MSG_REJECT, callId).sendToTarget();
+ }
+
+ @Override
+ public void disconnect(String callId) {
+ mHandler.obtainMessage(MSG_DISCONNECT, callId).sendToTarget();
+ }
+
+ @Override
+ public void hold(String callId) {
+ mHandler.obtainMessage(MSG_HOLD, callId).sendToTarget();
+ }
+
+ @Override
+ public void unhold(String callId) {
+ mHandler.obtainMessage(MSG_UNHOLD, callId).sendToTarget();
+ }
+
+ @Override
+ public void onAudioStateChanged(String callId, CallAudioState audioState) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = audioState;
+ mHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, args).sendToTarget();
+ }
+
+ @Override
+ public void playDtmfTone(String callId, char digit) {
+ mHandler.obtainMessage(MSG_PLAY_DTMF_TONE, digit, 0, callId).sendToTarget();
+ }
+
+ @Override
+ public void stopDtmfTone(String callId) {
+ mHandler.obtainMessage(MSG_STOP_DTMF_TONE, callId).sendToTarget();
+ }
+
+ @Override
+ public void conference(String conferenceCallId, String callId) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = conferenceCallId;
+ args.arg2 = callId;
+ mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget();
+ }
+
+ @Override
+ public void splitFromConference(String callId) {
+ mHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget();
+ }
+
+ @Override
+ public void onPostDialContinue(String callId, boolean proceed) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.argi1 = proceed ? 1 : 0;
+ mHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget();
+ }
+
+ @Override
+ public void onPhoneAccountClicked(String callId) {
+ mHandler.obtainMessage(MSG_ON_PHONE_ACCOUNT_CLICKED, callId).sendToTarget();
+ }
+ };
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_ADD_CALL_SERVICE_ADAPTER:
+ mAdapter.addAdapter((IConnectionServiceAdapter) msg.obj);
+ onAdapterAttached();
+ break;
+ case MSG_CALL:
+ call((ConnectionRequest) msg.obj);
+ break;
+ case MSG_ABORT:
+ abort((String) msg.obj);
+ break;
+ case MSG_CREATE_INCOMING_CALL:
+ createIncomingCall((ConnectionRequest) msg.obj);
+ break;
+ case MSG_ANSWER:
+ answer((String) msg.obj);
+ break;
+ case MSG_REJECT:
+ reject((String) msg.obj);
+ break;
+ case MSG_DISCONNECT:
+ disconnect((String) msg.obj);
+ break;
+ case MSG_HOLD:
+ hold((String) msg.obj);
+ break;
+ case MSG_UNHOLD:
+ unhold((String) msg.obj);
+ break;
+ case MSG_ON_AUDIO_STATE_CHANGED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String callId = (String) args.arg1;
+ CallAudioState audioState = (CallAudioState) args.arg2;
+ onAudioStateChanged(callId, audioState);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_PLAY_DTMF_TONE:
+ playDtmfTone((String) msg.obj, (char) msg.arg1);
+ break;
+ case MSG_STOP_DTMF_TONE:
+ stopDtmfTone((String) msg.obj);
+ break;
+ case MSG_CONFERENCE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String conferenceCallId = (String) args.arg1;
+ String callId = (String) args.arg2;
+ conference(conferenceCallId, callId);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SPLIT_FROM_CONFERENCE:
+ splitFromConference((String) msg.obj);
+ break;
+ case MSG_ON_POST_DIAL_CONTINUE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String callId = (String) args.arg1;
+ boolean proceed = (args.argi1 == 1);
+ onPostDialContinue(callId, proceed);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_ON_PHONE_ACCOUNT_CLICKED:
+ onPhoneAccountClicked((String) msg.obj);
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
private final Connection.Listener mConnectionListener = new Connection.Listener() {
@Override
public void onStateChanged(Connection c, int state) {
@@ -88,22 +281,22 @@
Log.d(this, "Adapter set state %s %s", id, Connection.stateToString(state));
switch (state) {
case Connection.State.ACTIVE:
- getAdapter().setActive(id);
+ mAdapter.setActive(id);
break;
case Connection.State.DIALING:
- getAdapter().setDialing(id);
+ mAdapter.setDialing(id);
break;
case Connection.State.DISCONNECTED:
// Handled in onDisconnected()
break;
case Connection.State.HOLDING:
- getAdapter().setOnHold(id);
+ mAdapter.setOnHold(id);
break;
case Connection.State.NEW:
// Nothing to tell Telecomm
break;
case Connection.State.RINGING:
- getAdapter().setRinging(id);
+ mAdapter.setRinging(id);
break;
}
}
@@ -113,14 +306,14 @@
public void onFeaturesChanged(Connection c, int features) {
String id = mIdByConnection.get(c);
Log.d(this, "Adapter set features %d", features);
- getAdapter().setFeatures(id, features);
+ mAdapter.setFeatures(id, features);
}
@Override
public void onDisconnected(Connection c, int cause, String message) {
String id = mIdByConnection.get(c);
Log.d(this, "Adapter set disconnected %d %s", cause, message);
- getAdapter().setDisconnected(id, cause, message);
+ mAdapter.setDisconnected(id, cause, message);
}
@Override
@@ -129,11 +322,6 @@
}
@Override
- public void onAudioStateChanged(Connection c, CallAudioState state) {
- // TODO: Unsupported yet
- }
-
- @Override
public void onSignalChanged(Connection c, Bundle details) {
// TODO: Unsupported yet
}
@@ -144,23 +332,23 @@
}
@Override
- public final void onPostDialWait(Connection c, String remaining) {
+ public void onPostDialWait(Connection c, String remaining) {
String id = mIdByConnection.get(c);
Log.d(this, "Adapter onPostDialWait %s, %s", c, remaining);
- getAdapter().onPostDialWait(id, remaining);
+ mAdapter.onPostDialWait(id, remaining);
}
@Override
public void onRequestingRingback(Connection c, boolean ringback) {
String id = mIdByConnection.get(c);
Log.d(this, "Adapter onRingback %b", ringback);
- getAdapter().setRequestingRingback(id, ringback);
+ mAdapter.setRequestingRingback(id, ringback);
}
@Override
public void onConferenceCapableChanged(Connection c, boolean isConferenceCapable) {
String id = mIdByConnection.get(c);
- getAdapter().setCanConference(id, isConferenceCapable);
+ mAdapter.setCanConference(id, isConferenceCapable);
}
/** ${inheritDoc} */
@@ -168,156 +356,120 @@
public void onParentConnectionChanged(Connection c, Connection parent) {
String id = mIdByConnection.get(c);
String parentId = parent == null ? null : mIdByConnection.get(parent);
- getAdapter().setIsConferenced(id, parentId);
+ mAdapter.setIsConferenced(id, parentId);
}
@Override
public void onSetCallVideoProvider(Connection c, CallVideoProvider callVideoProvider) {
String id = mIdByConnection.get(c);
- getAdapter().setCallVideoProvider(id, callVideoProvider);
+ mAdapter.setCallVideoProvider(id, callVideoProvider);
}
};
- /** @hide */
+ /** {@inheritDoc} */
@Override
- protected final void call(final CallInfo callInfo) {
- Log.d(this, "call %s", callInfo);
+ public final IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ private void call(final ConnectionRequest originalRequest) {
+ Log.d(this, "call %s", originalRequest);
onCreateConnections(
- new ConnectionRequest(
- callInfo.getId(),
- callInfo.getHandle(),
- callInfo.getExtras()),
+ originalRequest,
new OutgoingCallResponse<Connection>() {
@Override
public void onSuccess(ConnectionRequest request, Connection connection) {
- Log.d(this, "adapter handleSuccessfulOutgoingCall %s",
- callInfo.getId());
- getAdapter().handleSuccessfulOutgoingCall(callInfo.getId());
- addConnection(callInfo.getId(), connection);
+ Log.d(this, "adapter handleSuccessfulOutgoingCall %s", request.getCallId());
+ mAdapter.handleSuccessfulOutgoingCall(request);
+ addConnection(request.getCallId(), connection);
}
@Override
public void onFailure(ConnectionRequest request, int code, String msg) {
- getAdapter().handleFailedOutgoingCall(request, code, msg);
+ mAdapter.handleFailedOutgoingCall(request, code, msg);
}
@Override
public void onCancel(ConnectionRequest request) {
- getAdapter().cancelOutgoingCall(callInfo.getId());
+ mAdapter.cancelOutgoingCall(request);
}
}
);
}
- /** @hide */
- @Override
- protected final void abort(String callId) {
+ private void abort(String callId) {
Log.d(this, "abort %s", callId);
- findConnectionForAction(callId, "abort").abort();
+ findConnectionForAction(callId, "abort").onAbort();
}
- /** @hide */
- @Override
- protected final void setIncomingCallId(final String callId, Bundle extras) {
- Log.d(this, "setIncomingCallId %s %s", callId, extras);
+ private void createIncomingCall(ConnectionRequest originalRequest) {
+ Log.d(this, "createIncomingCall %s", originalRequest);
onCreateIncomingConnection(
- new ConnectionRequest(
- callId,
- null, // TODO: Can we obtain this from "extras"?
- extras),
+ originalRequest,
new Response<ConnectionRequest, Connection>() {
@Override
public void onResult(ConnectionRequest request, Connection... result) {
if (result != null && result.length != 1) {
- Log.d(this, "adapter handleFailedOutgoingCall %s", callId);
- getAdapter().handleFailedOutgoingCall(
- request,
- DisconnectCause.ERROR_UNSPECIFIED,
- "Created " + result.length + " Connections, expected 1");
for (Connection c : result) {
- c.abort();
+ c.onAbort();
}
} else {
- addConnection(callId, result[0]);
- Log.d(this, "adapter notifyIncomingCall %s", callId);
- // TODO: Uri.EMPTY is because CallInfo crashes when Parceled with a
- // null URI ... need to fix that at its cause!
- getAdapter().notifyIncomingCall(new CallInfo(
- callId,
- connectionStateToCallState(result[0].getState()),
- request.getHandle() /* result[0].getHandle() == null
- ? Uri.EMPTY : result[0].getHandle() */));
+ addConnection(request.getCallId(), result[0]);
+ Log.d(this, "adapter notifyIncomingCall %s", request);
+ mAdapter.notifyIncomingCall(request);
}
}
@Override
public void onError(ConnectionRequest request, int code, String msg) {
- Log.d(this, "adapter failed setIncomingCallId %s %d %s",
+ Log.d(this, "adapter failed createIncomingCall %s %d %s",
request, code, msg);
}
}
);
}
- /** @hide */
- @Override
- protected final void answer(String callId) {
+ private void answer(String callId) {
Log.d(this, "answer %s", callId);
- findConnectionForAction(callId, "answer").answer();
+ findConnectionForAction(callId, "answer").onAnswer();
}
- /** @hide */
- @Override
- protected final void reject(String callId) {
+ private void reject(String callId) {
Log.d(this, "reject %s", callId);
- findConnectionForAction(callId, "reject").reject();
+ findConnectionForAction(callId, "reject").onReject();
}
- /** @hide */
- @Override
- protected final void disconnect(String callId) {
+ private void disconnect(String callId) {
Log.d(this, "disconnect %s", callId);
- findConnectionForAction(callId, "disconnect").disconnect();
+ findConnectionForAction(callId, "disconnect").onDisconnect();
}
- /** @hide */
- @Override
- protected final void hold(String callId) {
+ private void hold(String callId) {
Log.d(this, "hold %s", callId);
- findConnectionForAction(callId, "hold").hold();
+ findConnectionForAction(callId, "hold").onHold();
}
- /** @hide */
- @Override
- protected final void unhold(String callId) {
+ private void unhold(String callId) {
Log.d(this, "unhold %s", callId);
- findConnectionForAction(callId, "unhold").unhold();
+ findConnectionForAction(callId, "unhold").onUnhold();
}
- /** @hide */
- @Override
- protected final void playDtmfTone(String callId, char digit) {
- Log.d(this, "playDtmfTone %s %c", callId, digit);
- findConnectionForAction(callId, "playDtmfTone").playDtmfTone(digit);
- }
-
- /** @hide */
- @Override
- protected final void stopDtmfTone(String callId) {
- Log.d(this, "stopDtmfTone %s", callId);
- findConnectionForAction(callId, "stopDtmfTone").stopDtmfTone();
- }
-
- /** @hide */
- @Override
- protected final void onAudioStateChanged(String callId, CallAudioState audioState) {
+ private void onAudioStateChanged(String callId, CallAudioState audioState) {
Log.d(this, "onAudioStateChanged %s %s", callId, audioState);
findConnectionForAction(callId, "onAudioStateChanged").setAudioState(audioState);
}
- /** @hide */
- @Override
- protected final void conference(final String conferenceCallId, String callId) {
+ private void playDtmfTone(String callId, char digit) {
+ Log.d(this, "playDtmfTone %s %c", callId, digit);
+ findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
+ }
+
+ private void stopDtmfTone(String callId) {
+ Log.d(this, "stopDtmfTone %s", callId);
+ findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone();
+ }
+
+ private void conference(final String conferenceCallId, String callId) {
Log.d(this, "conference %s, %s", conferenceCallId, callId);
Connection connection = findConnectionForAction(callId, "conference");
@@ -336,7 +488,7 @@
Connection conferenceConnection = result[0];
if (!mIdByConnection.containsKey(conferenceConnection)) {
Log.v(this, "sending new conference call %s", conferenceCallId);
- getAdapter().addConferenceCall(conferenceCallId);
+ mAdapter.addConferenceCall(conferenceCallId);
addConnection(conferenceCallId, conferenceConnection);
}
}
@@ -350,9 +502,7 @@
});
}
- /** @hide */
- @Override
- protected final void splitFromConference(String callId) {
+ private void splitFromConference(String callId) {
Log.d(this, "splitFromConference(%s)", callId);
Connection connection = findConnectionForAction(callId, "splitFromConference");
@@ -364,55 +514,36 @@
// TODO(santoscordon): Find existing conference call and invoke split(connection).
}
- /** @hide */
- @Override
- protected final void onPostDialContinue(String callId, boolean proceed) {
+ private void onPostDialContinue(String callId, boolean proceed) {
Log.d(this, "onPostDialContinue(%s)", callId);
-
- Connection connection = findConnectionForAction(callId, "onPostDialContinue");
- if (connection == NULL_CONNECTION) {
- Log.w(this, "Connection missing in post-dial request %s.", callId);
- return;
- }
- connection.onPostDialContinue(proceed);
+ findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed);
}
- /** @hide */
- @Override
- protected final void onFeaturesChanged(String callId, int features) {
- Log.d(this, "onFeaturesChanged %s %d", callId, features);
- findConnectionForAction(callId, "onFeaturesChanged").setFeatures(features);
- }
-
- /** @hide */
- @Override
- protected void onPhoneAccountClicked(String callId) {
+ private void onPhoneAccountClicked(String callId) {
Log.d(this, "onPhoneAccountClicked %s", callId);
findConnectionForAction(callId, "onPhoneAccountClicked").onPhoneAccountClicked();
}
- /** @hide */
- @Override
- protected final void onAdapterAttached(CallServiceAdapter adapter) {
+ private void onAdapterAttached() {
if (mAreAccountsInitialized) {
// No need to query again if we already did it.
return;
}
- getAdapter().queryRemoteConnectionServices(new RemoteServiceCallback.Stub() {
+ mAdapter.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() {
@Override
public void onResult(
final List<ComponentName> componentNames,
- final List<IBinder> callServices) {
+ final List<IBinder> services) {
mHandler.post(new Runnable() {
@Override public void run() {
- for (int i = 0; i < componentNames.size() && i < callServices.size(); i++) {
+ for (int i = 0; i < componentNames.size() && i < services.size(); i++) {
mRemoteConnectionManager.addConnectionService(
componentNames.get(i),
- ICallService.Stub.asInterface(callServices.get(i)));
+ IConnectionService.Stub.asInterface(services.get(i)));
}
mAreAccountsInitialized = true;
- Log.d(this, "remote call services found: " + callServices);
+ Log.d(this, "remote call services found: " + services);
maybeRespondToAccountLookup();
}
});
@@ -540,26 +671,6 @@
return builder.toString();
}
- private CallState connectionStateToCallState(int connectionState) {
- switch (connectionState) {
- case Connection.State.NEW:
- return CallState.NEW;
- case Connection.State.RINGING:
- return CallState.RINGING;
- case Connection.State.DIALING:
- return CallState.DIALING;
- case Connection.State.ACTIVE:
- return CallState.ACTIVE;
- case Connection.State.HOLDING:
- return CallState.ON_HOLD;
- case Connection.State.DISCONNECTED:
- return CallState.DISCONNECTED;
- default:
- Log.wtf(this, "Unknown Connection.State %d", connectionState);
- return CallState.NEW;
- }
- }
-
private void addConnection(String callId, Connection connection) {
mConnectionById.put(callId, connection);
mIdByConnection.put(connection, callId);
diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
similarity index 67%
rename from telecomm/java/android/telecomm/CallServiceAdapter.java
rename to telecomm/java/android/telecomm/ConnectionServiceAdapter.java
index dcab566..1d21888 100644
--- a/telecomm/java/android/telecomm/CallServiceAdapter.java
+++ b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
@@ -21,8 +21,8 @@
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
-import com.android.internal.telecomm.ICallService;
-import com.android.internal.telecomm.ICallServiceAdapter;
+import com.android.internal.telecomm.IConnectionService;
+import com.android.internal.telecomm.IConnectionServiceAdapter;
import com.android.internal.telecomm.ICallVideoProvider;
import com.android.internal.telecomm.RemoteServiceCallback;
@@ -32,24 +32,17 @@
import java.util.Set;
/**
- * Provides methods for ICallService implementations to interact with the system phone app.
- * TODO(santoscordon): Rename this to CallServiceAdapterDemultiplexer (or something).
+ * Provides methods for IConnectionService implementations to interact with the system phone app.
*
* @hide
*/
-public final class CallServiceAdapter implements DeathRecipient {
- private final Set<ICallServiceAdapter> mAdapters = new HashSet<>();
+final class ConnectionServiceAdapter implements DeathRecipient {
+ private final Set<IConnectionServiceAdapter> mAdapters = new HashSet<>();
- /**
- * @hide
- */
- public CallServiceAdapter() {
+ ConnectionServiceAdapter() {
}
- /**
- * @hide
- */
- public void addAdapter(ICallServiceAdapter adapter) {
+ void addAdapter(IConnectionServiceAdapter adapter) {
if (mAdapters.add(adapter)) {
try {
adapter.asBinder().linkToDeath(this, 0);
@@ -59,10 +52,7 @@
}
}
- /**
- * @hide
- */
- public void removeAdapter(ICallServiceAdapter adapter) {
+ void removeAdapter(IConnectionServiceAdapter adapter) {
if (mAdapters.remove(adapter)) {
adapter.asBinder().unlinkToDeath(this, 0);
}
@@ -71,31 +61,25 @@
/** ${inheritDoc} */
@Override
public void binderDied() {
- ICallServiceAdapter adapterToRemove = null;
- for (ICallServiceAdapter adapter : mAdapters) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
if (!adapter.asBinder().isBinderAlive()) {
- adapterToRemove = adapter;
- break;
+ removeAdapter(adapter);
}
}
-
- if (adapterToRemove != null) {
- removeAdapter(adapterToRemove);
- }
}
/**
* Provides Telecomm with the details of an incoming call. An invocation of this method must
- * follow {@link CallService#setIncomingCallId} and use the call ID specified therein. Upon the
- * invocation of this method, Telecomm will bring up the incoming-call interface where the user
- * can elect to answer or reject a call.
+ * follow {@link ConnectionService#setIncomingCallId} and use the call ID specified therein.
+ * Upon the invocation of this method, Telecomm will bring up the incoming-call interface where
+ * the user can elect to answer or reject a call.
*
- * @param callInfo The details of the relevant call.
+ * @param request The connection request.
*/
- public void notifyIncomingCall(CallInfo callInfo) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ void notifyIncomingCall(ConnectionRequest request) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.notifyIncomingCall(callInfo);
+ adapter.notifyIncomingCall(request);
} catch (RemoteException e) {
}
}
@@ -103,15 +87,13 @@
/**
* Tells Telecomm that an attempt to place the specified outgoing call succeeded.
- * TODO(santoscordon): Consider adding a CallState parameter in case this outgoing call is
- * somehow no longer in the DIALING state.
*
- * @param callId The ID of the outgoing call.
+ * @param request The originating request for a connection.
*/
- public void handleSuccessfulOutgoingCall(String callId) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ void handleSuccessfulOutgoingCall(ConnectionRequest request) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.handleSuccessfulOutgoingCall(callId);
+ adapter.handleSuccessfulOutgoingCall(request);
} catch (RemoteException e) {
}
}
@@ -124,11 +106,11 @@
* @param errorCode The error code associated with the failed call attempt.
* @param errorMsg The error message associated with the failed call attempt.
*/
- public void handleFailedOutgoingCall(
+ void handleFailedOutgoingCall(
ConnectionRequest request,
int errorCode,
String errorMsg) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
adapter.handleFailedOutgoingCall(request, errorCode, errorMsg);
} catch (RemoteException e) {
@@ -139,12 +121,12 @@
/**
* Tells Telecomm to cancel the call.
*
- * @param callId The ID of the outgoing call.
+ * @param request The originating request for a connection.
*/
- public void cancelOutgoingCall(String callId) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ void cancelOutgoingCall(ConnectionRequest request) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.cancelOutgoingCall(callId);
+ adapter.cancelOutgoingCall(request);
} catch (RemoteException e) {
}
}
@@ -156,8 +138,8 @@
*
* @param callId The unique ID of the call whose state is changing to active.
*/
- public void setActive(String callId) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ void setActive(String callId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
adapter.setActive(callId);
} catch (RemoteException e) {
@@ -170,8 +152,8 @@
*
* @param callId The unique ID of the call whose state is changing to ringing.
*/
- public void setRinging(String callId) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ void setRinging(String callId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
adapter.setRinging(callId);
} catch (RemoteException e) {
@@ -184,8 +166,8 @@
*
* @param callId The unique ID of the call whose state is changing to dialing.
*/
- public void setDialing(String callId) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ void setDialing(String callId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
adapter.setDialing(callId);
} catch (RemoteException e) {
@@ -201,8 +183,8 @@
* {@link android.telephony.DisconnectCause}.
* @param disconnectMessage Optional call-service-provided message about the disconnect.
*/
- public void setDisconnected(String callId, int disconnectCause, String disconnectMessage) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ void setDisconnected(String callId, int disconnectCause, String disconnectMessage) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
adapter.setDisconnected(callId, disconnectCause, disconnectMessage);
} catch (RemoteException e) {
@@ -215,8 +197,8 @@
*
* @param callId - The unique ID of the call whose state is changing to be on hold.
*/
- public void setOnHold(String callId) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ void setOnHold(String callId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
adapter.setOnHold(callId);
} catch (RemoteException e) {
@@ -230,8 +212,8 @@
* @param callId The unique ID of the call whose ringback is being changed.
* @param ringback Whether Telecomm should start playing a ringback tone.
*/
- public void setRequestingRingback(String callId, boolean ringback) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ void setRequestingRingback(String callId, boolean ringback) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
adapter.setRequestingRingback(callId, ringback);
} catch (RemoteException e) {
@@ -244,10 +226,9 @@
*
* @param callId The unique ID of the call.
* @param canConference Specified whether or not the call can be conferenced.
- * @hide
*/
- public void setCanConference(String callId, boolean canConference) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ void setCanConference(String callId, boolean canConference) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
adapter.setCanConference(callId, canConference);
} catch (RemoteException ignored) {
@@ -262,10 +243,9 @@
* @param callId The unique ID of the call being conferenced.
* @param conferenceCallId The unique ID of the conference call. Null if call is not
* conferenced.
- * @hide
*/
- public void setIsConferenced(String callId, String conferenceCallId) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ void setIsConferenced(String callId, String conferenceCallId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
adapter.setIsConferenced(callId, conferenceCallId);
} catch (RemoteException ignored) {
@@ -278,10 +258,9 @@
* call.
*
* @param callId The unique ID of the call.
- * @hide
*/
- public void removeCall(String callId) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ void removeCall(String callId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
adapter.removeCall(callId);
} catch (RemoteException ignored) {
@@ -289,8 +268,8 @@
}
}
- public void onPostDialWait(String callId, String remaining) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ void onPostDialWait(String callId, String remaining) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
adapter.onPostDialWait(callId, remaining);
} catch (RemoteException ignored) {
@@ -303,10 +282,10 @@
*
* @param callId The unique ID of the conference call.
*/
- public void addConferenceCall(String callId) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ void addConferenceCall(String callId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.addConferenceCall(callId, null);
+ adapter.addConferenceCall(callId);
} catch (RemoteException ignored) {
}
}
@@ -314,9 +293,8 @@
/**
* Retrieves a list of remote connection services usable to place calls.
- * @hide
*/
- public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
+ void queryRemoteConnectionServices(RemoteServiceCallback callback) {
// Only supported when there is only one adapter.
if (mAdapters.size() == 1) {
try {
@@ -333,8 +311,8 @@
* @param callId The unique ID of the call to set with the given call video provider.
* @param callVideoProvider The call video provider instance to set on the call.
*/
- public void setCallVideoProvider(String callId, CallVideoProvider callVideoProvider) {
- for (ICallServiceAdapter adapter : mAdapters) {
+ void setCallVideoProvider(String callId, CallVideoProvider callVideoProvider) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
adapter.setCallVideoProvider(callId, callVideoProvider.getInterface());
} catch (RemoteException e) {
@@ -350,9 +328,9 @@
* @param callId The unique ID of the call to set features for.
* @param features The features.
*/
- public void setFeatures(String callId, int features) {
+ void setFeatures(String callId, int features) {
Log.v(this, "setFeatures: %d", features);
- for (ICallServiceAdapter adapter : mAdapters) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
try {
adapter.setFeatures(callId, features);
} catch (RemoteException ignored) {
diff --git a/telecomm/java/android/telecomm/RemoteConnection.java b/telecomm/java/android/telecomm/RemoteConnection.java
index f200bc0..18df37d 100644
--- a/telecomm/java/android/telecomm/RemoteConnection.java
+++ b/telecomm/java/android/telecomm/RemoteConnection.java
@@ -22,7 +22,7 @@
import android.os.RemoteException;
import android.telephony.DisconnectCause;
-import com.android.internal.telecomm.ICallService;
+import com.android.internal.telecomm.IConnectionService;
import java.util.HashSet;
import java.util.Set;
@@ -36,10 +36,11 @@
void onDisconnected(RemoteConnection connection, int cause, String message);
void onRequestingRingback(RemoteConnection connection, boolean ringback);
void onPostDialWait(RemoteConnection connection, String remainingDigits);
+ void onFeaturesChanged(RemoteConnection connection, int features);
void onDestroyed(RemoteConnection connection);
}
- private final ICallService mCallService;
+ private final IConnectionService mConnectionService;
private final String mConnectionId;
private final Set<Listener> mListeners = new HashSet<>();
@@ -48,12 +49,13 @@
private String mDisconnectMessage;
private boolean mRequestingRingback;
private boolean mConnected;
+ private int mFeatures;
/**
* @hide
*/
- RemoteConnection(ICallService callService, String connectionId) {
- mCallService = callService;
+ RemoteConnection(IConnectionService connectionService, String connectionId) {
+ mConnectionService = connectionService;
mConnectionId = connectionId;
mConnected = true;
@@ -79,10 +81,14 @@
return mDisconnectMessage;
}
+ public int getFeatures() {
+ return mFeatures;
+ }
+
public void abort() {
try {
if (mConnected) {
- mCallService.abort(mConnectionId);
+ mConnectionService.abort(mConnectionId);
}
} catch (RemoteException ignored) {
}
@@ -91,7 +97,7 @@
public void answer() {
try {
if (mConnected) {
- mCallService.answer(mConnectionId);
+ mConnectionService.answer(mConnectionId);
}
} catch (RemoteException ignored) {
}
@@ -100,7 +106,7 @@
public void reject() {
try {
if (mConnected) {
- mCallService.reject(mConnectionId);
+ mConnectionService.reject(mConnectionId);
}
} catch (RemoteException ignored) {
}
@@ -109,7 +115,7 @@
public void hold() {
try {
if (mConnected) {
- mCallService.hold(mConnectionId);
+ mConnectionService.hold(mConnectionId);
}
} catch (RemoteException ignored) {
}
@@ -118,7 +124,7 @@
public void unhold() {
try {
if (mConnected) {
- mCallService.unhold(mConnectionId);
+ mConnectionService.unhold(mConnectionId);
}
} catch (RemoteException ignored) {
}
@@ -127,7 +133,7 @@
public void disconnect() {
try {
if (mConnected) {
- mCallService.disconnect(mConnectionId);
+ mConnectionService.disconnect(mConnectionId);
}
} catch (RemoteException ignored) {
}
@@ -136,7 +142,7 @@
public void playDtmf(char digit) {
try {
if (mConnected) {
- mCallService.playDtmfTone(mConnectionId, digit);
+ mConnectionService.playDtmfTone(mConnectionId, digit);
}
} catch (RemoteException ignored) {
}
@@ -145,7 +151,7 @@
public void stopDtmf() {
try {
if (mConnected) {
- mCallService.stopDtmfTone(mConnectionId);
+ mConnectionService.stopDtmfTone(mConnectionId);
}
} catch (RemoteException ignored) {
}
@@ -154,7 +160,7 @@
public void postDialContinue(boolean proceed) {
try {
if (mConnected) {
- mCallService.onPostDialContinue(mConnectionId, proceed);
+ mConnectionService.onPostDialContinue(mConnectionId, proceed);
}
} catch (RemoteException ignored) {
}
@@ -163,7 +169,7 @@
public void setAudioState(CallAudioState state) {
try {
if (mConnected) {
- mCallService.onAudioStateChanged(mConnectionId, state);
+ mConnectionService.onAudioStateChanged(mConnectionId, state);
}
} catch (RemoteException ignored) {
}
@@ -236,4 +242,14 @@
l.onPostDialWait(this, remainingDigits);
}
}
+
+ /**
+ * @hide
+ */
+ void setFeatures(int features) {
+ mFeatures = features;
+ for (Listener l : mListeners) {
+ l.onFeaturesChanged(this, features);
+ }
+ }
}
diff --git a/telecomm/java/android/telecomm/RemoteConnectionManager.java b/telecomm/java/android/telecomm/RemoteConnectionManager.java
index eb3154b..9cffdcc 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionManager.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionManager.java
@@ -20,7 +20,7 @@
import android.net.Uri;
import android.os.RemoteException;
-import com.android.internal.telecomm.ICallService;
+import com.android.internal.telecomm.IConnectionService;
import java.util.HashMap;
import java.util.LinkedList;
@@ -33,11 +33,11 @@
public class RemoteConnectionManager {
private Map<ComponentName, RemoteConnectionService> mRemoteConnectionServices = new HashMap<>();
- void addConnectionService(ComponentName componentName, ICallService callService) {
+ void addConnectionService(ComponentName componentName, IConnectionService connectionService) {
if (!mRemoteConnectionServices.containsKey(componentName)) {
try {
RemoteConnectionService remoteConnectionService =
- new RemoteConnectionService(componentName, callService);
+ new RemoteConnectionService(componentName, connectionService);
mRemoteConnectionServices.put(componentName, remoteConnectionService);
} catch (RemoteException ignored) {
}
diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java
index 81bee98..73e7d77 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionService.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionService.java
@@ -24,8 +24,8 @@
import android.text.TextUtils;
-import com.android.internal.telecomm.ICallService;
-import com.android.internal.telecomm.ICallServiceAdapter;
+import com.android.internal.telecomm.IConnectionService;
+import com.android.internal.telecomm.IConnectionServiceAdapter;
import com.android.internal.telecomm.ICallVideoProvider;
import com.android.internal.telecomm.RemoteServiceCallback;
@@ -38,8 +38,8 @@
*
* @hide
*/
-public final class RemoteConnectionService implements DeathRecipient {
- private final ICallService mCallService;
+final class RemoteConnectionService implements DeathRecipient {
+ private final IConnectionService mConnectionService;
private final ComponentName mComponentName;
private String mConnectionId;
@@ -48,26 +48,26 @@
// Remote connection services only support a single connection.
private RemoteConnection mConnection;
- private final ICallServiceAdapter mAdapter = new ICallServiceAdapter.Stub() {
+ private final IConnectionServiceAdapter mAdapter = new IConnectionServiceAdapter.Stub() {
/** ${inheritDoc} */
- @Override
- public void notifyIncomingCall(CallInfo callInfo) {
+ @Override
+ public void notifyIncomingCall(ConnectionRequest request) {
Log.w(this, "notifyIncomingCall not implemented in Remote connection");
}
/** ${inheritDoc} */
- @Override
- public void handleSuccessfulOutgoingCall(String connectionId) {
- if (isPendingConnection(connectionId)) {
- mConnection = new RemoteConnection(mCallService, connectionId);
- mPendingOutgoingCallResponse.onSuccess(mPendingRequest, mConnection);
+ @Override
+ public void handleSuccessfulOutgoingCall(ConnectionRequest request) {
+ if (isPendingConnection(request.getCallId())) {
+ mConnection = new RemoteConnection(mConnectionService, request.getCallId());
+ mPendingOutgoingCallResponse.onSuccess(request, mConnection);
clearPendingInformation();
}
}
/** ${inheritDoc} */
- @Override
+ @Override
public void handleFailedOutgoingCall(
ConnectionRequest request, int errorCode, String errorMessage) {
if (isPendingConnection(request.getCallId())) {
@@ -78,17 +78,17 @@
}
/** ${inheritDoc} */
- @Override
- public void cancelOutgoingCall(String connectionId) {
- if (isPendingConnection(connectionId)) {
- mPendingOutgoingCallResponse.onCancel(mPendingRequest);
+ @Override
+ public void cancelOutgoingCall(ConnectionRequest request) {
+ if (isPendingConnection(request.getCallId())) {
+ mPendingOutgoingCallResponse.onCancel(request);
mConnectionId = null;
clearPendingInformation();
}
}
/** ${inheritDoc} */
- @Override
+ @Override
public void setActive(String connectionId) {
if (isCurrentConnection(connectionId)) {
mConnection.setState(Connection.State.ACTIVE);
@@ -96,7 +96,7 @@
}
/** ${inheritDoc} */
- @Override
+ @Override
public void setRinging(String connectionId) {
if (isCurrentConnection(connectionId)) {
mConnection.setState(Connection.State.RINGING);
@@ -110,7 +110,7 @@
}
/** ${inheritDoc} */
- @Override
+ @Override
public void setDialing(String connectionId) {
if (isCurrentConnection(connectionId)) {
mConnection.setState(Connection.State.DIALING);
@@ -118,7 +118,7 @@
}
/** ${inheritDoc} */
- @Override
+ @Override
public void setDisconnected(
String connectionId, int disconnectCause, String disconnectMessage) {
if (isCurrentConnection(connectionId)) {
@@ -127,7 +127,7 @@
}
/** ${inheritDoc} */
- @Override
+ @Override
public void setOnHold(String connectionId) {
if (isCurrentConnection(connectionId)) {
mConnection.setState(Connection.State.HOLDING);
@@ -135,7 +135,7 @@
}
/** ${inheritDoc} */
- @Override
+ @Override
public void setRequestingRingback(String connectionId, boolean isRequestingRingback) {
if (isCurrentConnection(connectionId)) {
mConnection.setRequestingRingback(isRequestingRingback);
@@ -143,25 +143,25 @@
}
/** ${inheritDoc} */
- @Override
+ @Override
public void setCanConference(String connectionId, boolean canConference) {
// not supported for remote connections.
}
/** ${inheritDoc} */
- @Override
+ @Override
public void setIsConferenced(String connectionId, String conferenceConnectionId) {
// not supported for remote connections.
}
/** ${inheritDoc} */
- @Override
- public void addConferenceCall(String connectionId, CallInfo callInfo) {
+ @Override
+ public void addConferenceCall(String connectionId) {
// not supported for remote connections.
}
/** ${inheritDoc} */
- @Override
+ @Override
public void removeCall(String connectionId) {
if (isCurrentConnection(connectionId)) {
destroyConnection();
@@ -169,7 +169,7 @@
}
/** ${inheritDoc} */
- @Override
+ @Override
public void onPostDialWait(String connectionId, String remainingDigits) {
if (isCurrentConnection(connectionId)) {
mConnection.setPostDialWait(remainingDigits);
@@ -189,23 +189,24 @@
/** ${inheritDoc} */
@Override
public void setFeatures(String connectionId, int features) {
- // not supported for remote connections.
+ if (isCurrentConnection(connectionId)) {
+ mConnection.setFeatures(features);
+ }
}
};
- RemoteConnectionService(ComponentName componentName, ICallService callService)
+ RemoteConnectionService(ComponentName componentName, IConnectionService connectionService)
throws RemoteException {
mComponentName = componentName;
- mCallService = callService;
+ mConnectionService = connectionService;
- // TODO(santoscordon): Rename from setCallServiceAdapter to addCallServiceAdapter.
- mCallService.setCallServiceAdapter(mAdapter);
- mCallService.asBinder().linkToDeath(this, 0);
+ mConnectionService.addConnectionServiceAdapter(mAdapter);
+ mConnectionService.asBinder().linkToDeath(this, 0);
}
@Override
public String toString() {
- return "[RemoteCS - " + mCallService.asBinder().toString() + "]";
+ return "[RemoteCS - " + mConnectionService.asBinder().toString() + "]";
}
/** ${inheritDoc} */
@@ -221,15 +222,16 @@
/**
* Places an outgoing call.
*/
- public final void createOutgoingConnection(
+ final void createOutgoingConnection(
ConnectionRequest request,
ConnectionService.OutgoingCallResponse<RemoteConnection> response) {
if (mConnectionId == null) {
String id = UUID.randomUUID().toString();
- CallInfo callInfo = new CallInfo(id, CallState.NEW, request.getHandle());
+ ConnectionRequest newRequest = new ConnectionRequest(request.getAccount(), id,
+ request.getHandle(), request.getExtras());
try {
- mCallService.call(callInfo);
+ mConnectionService.call(newRequest);
mConnectionId = id;
mPendingOutgoingCallResponse = response;
mPendingRequest = request;
@@ -244,7 +246,7 @@
// TODO(santoscordon): Handle incoming connections
// public final void handleIncomingConnection() {}
- public final List<PhoneAccount> lookupAccounts(Uri handle) {
+ final List<PhoneAccount> lookupAccounts(Uri handle) {
// TODO(santoscordon): Update this so that is actually calls into the RemoteConnection
// each time.
List<PhoneAccount> accounts = new LinkedList<>();
@@ -264,7 +266,7 @@
* the remote service is no longer being used.
*/
void release() {
- mCallService.asBinder().unlinkToDeath(this, 0);
+ mConnectionService.asBinder().unlinkToDeath(this, 0);
}
private boolean isPendingConnection(String id) {
diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java
index 80901cf..513a5ee 100644
--- a/telecomm/java/android/telecomm/TelecommConstants.java
+++ b/telecomm/java/android/telecomm/TelecommConstants.java
@@ -48,7 +48,7 @@
/**
* The service action used to bind to {@link ConnectionService} implementations.
*/
- public static final String ACTION_CALL_SERVICE = CallService.class.getName();
+ public static final String ACTION_CONNECTION_SERVICE = ConnectionService.class.getName();
/**
* Optional extra for {@link Intent#ACTION_CALL} containing a boolean that determines whether
@@ -94,6 +94,13 @@
"android.telecomm.extra.CALL_DISCONNECT_MESSAGE";
/**
+ * Optional extra for {@link TelephonyManager#ACTION_PHONE_STATE_CHANGED} containing the
+ * component name of the associated connection service.
+ */
+ public static final String EXTRA_CONNECTION_SERVICE =
+ "android.telecomm.extra.CONNECTION_SERVICE";
+
+ /**
* The dual tone multi-frequency signaling character sent to indicate the dialing system should
* pause for a predefined period.
*/
diff --git a/telecomm/java/com/android/internal/telecomm/ICallService.aidl b/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
similarity index 73%
rename from telecomm/java/com/android/internal/telecomm/ICallService.aidl
rename to telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
index ac4e66c..c8ce4c6 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * 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.
@@ -18,25 +18,25 @@
import android.os.Bundle;
import android.telecomm.CallAudioState;
-import android.telecomm.CallInfo;
+import android.telecomm.ConnectionRequest;
-import com.android.internal.telecomm.ICallServiceAdapter;
+import com.android.internal.telecomm.IConnectionServiceAdapter;
/**
- * Internal remote interface for call services.
+ * Internal remote interface for connection services.
*
- * @see android.telecomm.CallService
+ * @see android.telecomm.ConnectionService
*
* @hide
*/
-oneway interface ICallService {
- void setCallServiceAdapter(in ICallServiceAdapter callServiceAdapter);
+oneway interface IConnectionService {
+ void addConnectionServiceAdapter(in IConnectionServiceAdapter adapter);
- void call(in CallInfo callInfo);
+ void call(in ConnectionRequest request);
void abort(String callId);
- void setIncomingCallId(String callId, in Bundle extras);
+ void createIncomingCall(in ConnectionRequest request);
void answer(String callId);
diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
similarity index 78%
rename from telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
rename to telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
index eee64da..9acc920 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * 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.
@@ -16,27 +16,26 @@
package com.android.internal.telecomm;
-import android.telecomm.CallInfo;
import android.telecomm.ConnectionRequest;
import com.android.internal.telecomm.ICallVideoProvider;
import com.android.internal.telecomm.RemoteServiceCallback;
/**
- * Internal remote callback interface for call services.
+ * Internal remote callback interface for connection services.
*
- * @see android.telecomm.CallServiceAdapter
+ * @see android.telecomm.ConnectionServiceAdapter
*
* {@hide}
*/
-oneway interface ICallServiceAdapter {
- void notifyIncomingCall(in CallInfo callInfo);
+oneway interface IConnectionServiceAdapter {
+ void notifyIncomingCall(in ConnectionRequest request);
- void handleSuccessfulOutgoingCall(String callId);
+ void handleSuccessfulOutgoingCall(in ConnectionRequest request);
void handleFailedOutgoingCall(in ConnectionRequest request, int errorCode, String errorMessage);
- void cancelOutgoingCall(String callId);
+ void cancelOutgoingCall(in ConnectionRequest request);
void setActive(String callId);
@@ -54,7 +53,7 @@
void setIsConferenced(String callId, String conferenceCallId);
- void addConferenceCall(String callId, in CallInfo callInfo);
+ void addConferenceCall(String callId);
void removeCall(String callId);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index bd621e8..b29cc12 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2812,6 +2812,40 @@
}
/**
+ * Values used to return status for hasCarrierPrivileges call.
+ */
+ public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1;
+ public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0;
+ public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1;
+ public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2;
+
+ /**
+ * Has the calling application been granted carrier privileges by the carrier.
+ *
+ * If any of the packages in the calling UID has carrier privileges, the
+ * call will return true. This access is granted by the owner of the UICC
+ * card and does not depend on the registered carrier.
+ *
+ * TODO: Add a link to documentation.
+ *
+ * @return CARRIER_PRIVILEGE_STATUS_HAS_ACCESS if the app has carrier privileges.
+ * CARRIER_PRIVILEGE_STATUS_NO_ACCESS if the app does not have carrier privileges.
+ * CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED if the carrier rules are not loaded.
+ * CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES if there was an error loading carrier
+ * rules (or if there are no rules).
+ */
+ public int hasCarrierPrivileges() {
+ try {
+ return getITelephony().hasCarrierPrivileges();
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "hasCarrierPrivileges RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "hasCarrierPrivileges NPE", ex);
+ }
+ return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+ }
+
+ /**
* Expose the rest of ITelephony to @SystemApi
*/
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index ee04c06..5b6db4d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -633,5 +633,17 @@
*/
void setImsRegistrationState(boolean registered);
+ /**
+ * Has the calling application been granted special privileges by the carrier.
+ *
+ * If any of the packages in the calling UID has carrier privileges, the
+ * call will return true. This access is granted by the owner of the UICC
+ * card and does not depend on the registered carrier.
+ *
+ * TODO: Add a link to documentation.
+ *
+ * @return carrier privelege status defined in TelephonyManager.
+ */
+ int hasCarrierPrivileges();
}