Merge "Screenshots - add edit action and remove delete."
diff --git a/api/current.txt b/api/current.txt
index bb4c383..3d8c5a6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21751,7 +21751,13 @@
field public static final int CHANNEL_OUT_STEREO = 12; // 0xc
field public static final int CHANNEL_OUT_SURROUND = 1052; // 0x41c
field public static final android.os.Parcelable.Creator<android.media.AudioFormat> CREATOR;
+ field public static final int ENCODING_AAC_ELD = 15; // 0xf
+ field public static final int ENCODING_AAC_HE_V1 = 11; // 0xb
+ field public static final int ENCODING_AAC_HE_V2 = 12; // 0xc
+ field public static final int ENCODING_AAC_LC = 10; // 0xa
+ field public static final int ENCODING_AAC_XHE = 16; // 0x10
field public static final int ENCODING_AC3 = 5; // 0x5
+ field public static final int ENCODING_AC4 = 17; // 0x11
field public static final int ENCODING_DEFAULT = 1; // 0x1
field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
field public static final int ENCODING_DTS = 7; // 0x7
@@ -21759,6 +21765,7 @@
field public static final int ENCODING_E_AC3 = 6; // 0x6
field public static final int ENCODING_IEC61937 = 13; // 0xd
field public static final int ENCODING_INVALID = 0; // 0x0
+ field public static final int ENCODING_MP3 = 9; // 0x9
field public static final int ENCODING_PCM_16BIT = 2; // 0x2
field public static final int ENCODING_PCM_8BIT = 3; // 0x3
field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
@@ -21801,6 +21808,7 @@
method public boolean isBluetoothScoOn();
method public boolean isMicrophoneMute();
method public boolean isMusicActive();
+ method public boolean isOffloadedPlaybackSupported(android.media.AudioFormat);
method public boolean isSpeakerphoneOn();
method public boolean isStreamMute(int);
method public boolean isVolumeFixed();
@@ -22101,6 +22109,7 @@
method public int reloadStaticData();
method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+ method public void removeStreamEventCallback();
method public int setAuxEffectSendLevel(float);
method public int setBufferSizeInFrames(int);
method public int setLoopPoints(int, int, int);
@@ -22114,6 +22123,7 @@
method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
method protected deprecated void setState(int);
method public deprecated int setStereoVolume(float, float);
+ method public void setStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback);
method public int setVolume(float);
method public void stop() throws java.lang.IllegalStateException;
method public int write(byte[], int, int);
@@ -22149,6 +22159,7 @@
method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
+ method public android.media.AudioTrack.Builder setOffloadedPlayback(boolean);
method public android.media.AudioTrack.Builder setPerformanceMode(int);
method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException;
@@ -22164,6 +22175,12 @@
method public default void onRoutingChanged(android.media.AudioRouting);
}
+ public static abstract class AudioTrack.StreamEventCallback {
+ method public void onStreamDataRequest(android.media.AudioTrack);
+ method public void onStreamPresentationEnd(android.media.AudioTrack);
+ method public void onTearDown(android.media.AudioTrack);
+ }
+
public class CamcorderProfile {
method public static android.media.CamcorderProfile get(int);
method public static android.media.CamcorderProfile get(int, int);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b469de5..c5a58f2 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -458,7 +459,8 @@
*
* @see Context#startActivity(Intent, Bundle)
*/
- public Activity startActivitySync(Intent intent, @Nullable Bundle options) {
+ @NonNull
+ public Activity startActivitySync(@NonNull Intent intent, @Nullable Bundle options) {
validateNotAppThread();
synchronized (mSync) {
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index f409e5b..72afbb8 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -73,6 +73,7 @@
public static final int KM_TAG_USER_AUTH_TYPE = KM_ENUM | 504;
public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505;
public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
+ public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507;
public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 51cefb9..c79f5bd 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -35,6 +35,7 @@
#define ENCODING_DOLBY_TRUEHD 14
#define ENCODING_AAC_ELD 15
#define ENCODING_AAC_XHE 16
+#define ENCODING_AC4 17
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -77,6 +78,8 @@
return AUDIO_FORMAT_AAC_ELD;
case ENCODING_AAC_XHE:
return AUDIO_FORMAT_AAC; // FIXME temporary value, needs addition of xHE-AAC
+ case ENCODING_AC4:
+ return AUDIO_FORMAT_AC4;
case ENCODING_DEFAULT:
return AUDIO_FORMAT_DEFAULT;
default:
@@ -125,6 +128,8 @@
// FIXME needs addition of AUDIO_FORMAT_AAC_XHE
//case AUDIO_FORMAT_AAC_XHE:
// return ENCODING_AAC_XHE;
+ case AUDIO_FORMAT_AC4:
+ return ENCODING_AC4;
case AUDIO_FORMAT_DEFAULT:
return ENCODING_DEFAULT;
default:
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 46fe89a..b07d042 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -238,25 +238,13 @@
public static final int ENCODING_DTS = 7;
/** Audio data format: DTS HD compressed */
public static final int ENCODING_DTS_HD = 8;
- /** Audio data format: MP3 compressed
- * @hide
- * TODO unhide and add to @Encoding (intentional white space
- * */
+ /** Audio data format: MP3 compressed */
public static final int ENCODING_MP3 = 9;
- /** Audio data format: AAC LC compressed
- * @hide
- * TODO unhide and add to @Encoding (intentional white space
- * */
+ /** Audio data format: AAC LC compressed */
public static final int ENCODING_AAC_LC = 10;
- /** Audio data format: AAC HE V1 compressed
- * @hide
- * TODO unhide and add to @Encoding (intentional white space
- * */
+ /** Audio data format: AAC HE V1 compressed */
public static final int ENCODING_AAC_HE_V1 = 11;
- /** Audio data format: AAC HE V2 compressed
- * @hide
- * TODO unhide and add to @Encoding (intentional white space
- * */
+ /** Audio data format: AAC HE V2 compressed */
public static final int ENCODING_AAC_HE_V2 = 12;
/** Audio data format: compressed audio wrapped in PCM for HDMI
@@ -271,16 +259,12 @@
/** Audio data format: DOLBY TRUEHD compressed
**/
public static final int ENCODING_DOLBY_TRUEHD = 14;
- /** Audio data format: AAC ELD compressed
- * @hide
- * TODO unhide and add to @Encoding (intentional white space
- * */
+ /** Audio data format: AAC ELD compressed */
public static final int ENCODING_AAC_ELD = 15;
- /** Audio data format: AAC xHE compressed
- * @hide
- * TODO unhide and add to @Encoding (intentional white space
- * */
+ /** Audio data format: AAC xHE compressed */
public static final int ENCODING_AAC_XHE = 16;
+ /** Audio data format: AC-4 sync frame transport format */
+ public static final int ENCODING_AC4 = 17;
/** @hide */
public static String toLogFriendlyEncoding(int enc) {
@@ -317,6 +301,8 @@
return "ENCODING_AAC_ELD";
case ENCODING_AAC_XHE:
return "ENCODING_AAC_XHE";
+ case ENCODING_AC4:
+ return "ENCODING_AC4";
default :
return "invalid encoding " + enc;
}
@@ -535,6 +521,7 @@
case ENCODING_IEC61937:
case ENCODING_AAC_ELD:
case ENCODING_AAC_XHE:
+ case ENCODING_AC4:
return true;
default:
return false;
@@ -553,13 +540,13 @@
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_IEC61937:
- //TODO not true yet (intended white space
case ENCODING_MP3:
case ENCODING_AAC_LC:
case ENCODING_AAC_HE_V1:
case ENCODING_AAC_HE_V2:
case ENCODING_AAC_ELD:
case ENCODING_AAC_XHE:
+ case ENCODING_AC4:
return true;
default:
return false;
@@ -586,6 +573,7 @@
case ENCODING_IEC61937: // wrapped in PCM but compressed
case ENCODING_AAC_ELD:
case ENCODING_AAC_XHE:
+ case ENCODING_AC4:
return false;
case ENCODING_INVALID:
default:
@@ -613,6 +601,7 @@
case ENCODING_AAC_HE_V2:
case ENCODING_AAC_ELD:
case ENCODING_AAC_XHE:
+ case ENCODING_AC4:
return false;
case ENCODING_INVALID:
default:
@@ -849,6 +838,7 @@
case ENCODING_AAC_HE_V2:
case ENCODING_AAC_ELD:
case ENCODING_AAC_XHE:
+ case ENCODING_AC4:
mEncoding = encoding;
break;
case ENCODING_INVALID:
@@ -1056,7 +1046,13 @@
ENCODING_E_AC3,
ENCODING_DTS,
ENCODING_DTS_HD,
- ENCODING_IEC61937 }
+ ENCODING_IEC61937,
+ ENCODING_AAC_HE_V1,
+ ENCODING_AAC_HE_V2,
+ ENCODING_AAC_LC,
+ ENCODING_AAC_ELD,
+ ENCODING_AAC_XHE,
+ ENCODING_AC4 }
)
@Retention(RetentionPolicy.SOURCE)
public @interface Encoding {}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 4d5343c..2ac4063 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1331,15 +1331,13 @@
//====================================================================
// Offload query
/**
- * @hide
- * TODO unhide (intentional white space to attract attention:
* Returns whether offloaded playback of an audio format is supported on the device.
* Offloaded playback is where the decoding of an audio stream is not competing with other
* software resources. In general, it is supported by dedicated hardware, such as audio DSPs.
* @param format the audio format (codec, sample rate, channels) being checked.
* @return true if the given audio format can be offloaded.
*/
- public static boolean isOffloadedPlaybackSupported(@NonNull AudioFormat format) {
+ public boolean isOffloadedPlaybackSupported(@NonNull AudioFormat format) {
return AudioSystem.isOffloadSupported(format);
}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 6add381..5928d03 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -755,8 +755,8 @@
* <code>MODE_STREAM</code> will be used.
* <br>If the session ID is not specified with {@link #setSessionId(int)}, a new one will
* be generated.
+ * <br>Offload is false by default.
*/
- // TODO add that offload is false by default (intended white space
public static class Builder {
private AudioAttributes mAttributes;
private AudioFormat mFormat;
@@ -895,14 +895,11 @@
}
/**
- * @hide
- * TODO unhide (intentional whitespace
- * TODO should offload require POWER_SAVING?
* Sets whether this track will play through the offloaded audio path.
* When set to true, at build time, the audio format will be checked against
* {@link AudioManager#isOffloadedPlaybackSupported(AudioFormat)} to verify the audio format
* used by this track is supported on the device's offload path (if any).
- * <br>Offload is only supported for media audio data, and therefore require that
+ * <br>Offload is only supported for media audio streams, and therefore requires that
* the usage be {@link AudioAttributes#USAGE_MEDIA}.
* @param offload true to require the offload path for playback.
* @return the same Builder instance.
@@ -962,7 +959,7 @@
throw new UnsupportedOperationException(
"Cannot create AudioTrack, offload requires USAGE_MEDIA");
}
- if (!AudioManager.isOffloadedPlaybackSupported(mFormat)) {
+ if (!AudioSystem.isOffloadSupported(mFormat)) {
throw new UnsupportedOperationException(
"Cannot create AudioTrack, offload format not supported");
}
@@ -2942,14 +2939,31 @@
}
/**
- * @hide
- * TODO unhide (intentional white space to attract attention:
* Abstract class to receive event notification about the stream playback.
+ * See {@link AudioTrack#setStreamEventCallback(Executor, StreamEventCallback)} to register
+ * the callback on the given {@link AudioTrack} instance.
*/
public abstract static class StreamEventCallback {
- // TODO rename if supported for non offload tracks
+ /** @hide */ // add hidden empty constructor so it doesn't show in SDK
+ public StreamEventCallback() { }
+ /**
+ * Called when an offloaded track is no longer valid and has been discarded by the system.
+ * An example of this happening is when an offloaded track has been paused too long, and
+ * gets invalidated by the system to prevent any other offload.
+ * @param track the {@link AudioTrack} on which the event happened
+ */
public void onTearDown(AudioTrack track) { }
+ /**
+ * Called when all the buffers of an offloaded track that were queued in the audio system
+ * (e.g. the combination of the Android audio framework and the device's audio hardware)
+ * have been played after {@link AudioTrack#stop()} has been called.
+ * @param track the {@link AudioTrack} on which the event happened
+ */
public void onStreamPresentationEnd(AudioTrack track) { }
+ /**
+ * Called when more audio data can be written without blocking on an offloaded track.
+ * @param track the {@link AudioTrack} on which the event happened
+ */
public void onStreamDataRequest(AudioTrack track) { }
}
@@ -2958,11 +2972,9 @@
private final Object mStreamEventCbLock = new Object();
/**
- * @hide
- * TODO unhide (intentional white space to attract attention:
- * Registers a callback for notification of stream events.
+ * Sets the callback for the notification of stream events.
* @param executor {@link Executor} to handle the callbacks
- * @param eventCallback the callback to receive the stream events
+ * @param eventCallback the callback to receive the stream event notifications
*/
public void setStreamEventCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull StreamEventCallback eventCallback) {
@@ -2979,9 +2991,8 @@
}
/**
- * @hide
* Unregisters the callback for notification of stream events, previously set
- * by {@link #setStreamEventCallback(StreamEventCallback, Executor)}.
+ * by {@link #setStreamEventCallback(Executor, StreamEventCallback)}.
*/
public void removeStreamEventCallback() {
synchronized (mStreamEventCbLock) {
diff --git a/packages/SystemUI/res/color/qs_background_dark.xml b/packages/SystemUI/res/color/qs_background_dark.xml
index c19fa08..24afebd 100644
--- a/packages/SystemUI/res/color/qs_background_dark.xml
+++ b/packages/SystemUI/res/color/qs_background_dark.xml
@@ -15,6 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:alpha="0.87"
+ <item android:alpha="1"
android:color="?android:attr/colorBackgroundFloating"/>
</selector>
diff --git a/packages/SystemUI/res/drawable/qs_bg_gradient.xml b/packages/SystemUI/res/drawable/qs_bg_gradient.xml
new file mode 100644
index 0000000..a1ad528
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_bg_gradient.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:angle="270"
+ android:startColor="#ff000000"
+ android:endColor="#00000000"
+ android:type="linear" />
+</shape>
diff --git a/packages/SystemUI/res/layout/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml
index 59c0957..e52aa14 100644
--- a/packages/SystemUI/res/layout/battery_percentage_view.xml
+++ b/packages/SystemUI/res/layout/battery_percentage_view.xml
@@ -25,6 +25,6 @@
android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
android:textColor="?android:attr/textColorPrimary"
android:gravity="center_vertical|start"
- android:paddingEnd="@dimen/battery_level_padding_start"
+ android:paddingStart="@dimen/battery_level_padding_start"
android:importantForAccessibility="no"
/>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index 9ab8ac6..b3b6a0c 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -20,6 +20,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="0dp"
+ android:elevation="4dp"
android:orientation="vertical"
android:background="@drawable/qs_customizer_background"
android:gravity="center_horizontal">
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 1fd239b..0b9a7b2 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -23,7 +23,8 @@
android:clickable="true"
android:orientation="vertical"
android:paddingBottom="8dp"
- android:visibility="invisible">
+ android:visibility="invisible"
+ android:elevation="4dp" >
<include
android:id="@+id/qs_detail_header"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 3d09b74..9f6a946 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -21,6 +21,7 @@
android:id="@+id/qs_footer"
android:layout_width="match_parent"
android:layout_height="48dp"
+ android:elevation="4dp"
android:baselineAligned="false"
android:clickable="false"
android:clipChildren="false"
@@ -105,17 +106,6 @@
android:visibility="invisible"/>
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
-
- <com.android.systemui.statusbar.phone.ExpandableIndicator
- android:id="@+id/expand_indicator"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:clipToPadding="false"
- android:clickable="true"
- android:focusable="true"
- android:background="?android:attr/selectableItemBackgroundBorderless"
- android:contentDescription="@string/accessibility_quick_settings_expand"
- android:padding="14dp" />
</LinearLayout>
</com.android.systemui.qs.QSFooterImpl>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 5541f3d..5bcb7fd 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -18,17 +18,46 @@
android:id="@+id/quick_settings_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/qs_background_primary"
- android:elevation="4dp"
android:clipToPadding="false"
- android:clipChildren="false">
+ android:clipChildren="false" >
+
+ <!-- Main QS background -->
+ <View
+ android:id="@+id/quick_settings_background"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:elevation="4dp"
+ android:background="@drawable/qs_background_primary" />
+
+ <!-- Black part behind the status bar -->
+ <View
+ android:id="@+id/quick_settings_status_bar_background"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_header_system_icons_area_height"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ android:background="#ff000000" />
+
+ <!-- Gradient view behind QS -->
+ <View
+ android:id="@+id/quick_settings_gradient_view"
+ android:layout_width="match_parent"
+ android:layout_height="126dp"
+ android:layout_marginTop="@dimen/qs_header_system_icons_area_height"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ android:background="@drawable/qs_bg_gradient" />
+
<com.android.systemui.qs.QSPanel
android:id="@+id/quick_settings_panel"
- android:layout_marginTop="28dp"
+ android:layout_marginTop="@dimen/qs_header_system_icons_area_height"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="48dp" />
+ android:layout_marginBottom="48dp"
+ android:elevation="4dp"
+ />
+
<include layout="@layout/quick_status_bar_expanded_header" />
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
new file mode 100644
index 0000000..e0f0ed9
--- /dev/null
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<View
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/quick_qs_status_icons"
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:layout_marginBottom="22dp"
+ android:layout_below="@id/quick_status_bar_system_icons"
+ >
+
+</View>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index e8b418c..dacc3f9 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -28,23 +28,21 @@
android:clipToPadding="false"
android:paddingTop="0dp"
android:paddingEnd="0dp"
- android:paddingStart="0dp">
+ android:paddingStart="0dp"
+ android:elevation="4dp" >
<include layout="@layout/quick_status_bar_header_system_icons" />
+ <include layout="@layout/quick_qs_status_icons" />
<com.android.systemui.qs.QuickQSPanel
android:id="@+id/quick_qs_panel"
android:layout_width="match_parent"
android:layout_height="48dp"
- android:layout_alignParentEnd="true"
- android:layout_marginTop="31dp"
- android:layout_alignParentTop="true"
+ android:layout_below="@id/quick_qs_status_icons"
android:accessibilityTraversalAfter="@+id/date_time_group"
android:accessibilityTraversalBefore="@id/expand_indicator"
android:clipChildren="false"
android:clipToPadding="false"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
android:focusable="true"
android:importantForAccessibility="yes" />
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index 739a255..bfe1b62 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -17,6 +17,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/quick_status_bar_system_icons"
android:layout_width="match_parent"
android:layout_height="@dimen/qs_header_system_icons_area_height"
android:layout_alignParentEnd="true"
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 17b38cb..8c0b9ba 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -54,30 +54,47 @@
android:layout_height="match_parent"
android:layout="@layout/operator_name" />
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- android:layout_width="wrap_content"
+ <LinearLayout
android:layout_height="match_parent"
- android:singleLine="true"
- android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
- android:gravity="center_vertical|start"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ >
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ android:singleLine="true"
+ android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
+ android:gravity="center_vertical|start"
+ />
+
+ <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
+ PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
+ <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+ android:id="@+id/notification_icon_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="horizontal" />
+
+ </LinearLayout>
+
+ <!-- Space should cover the notch (if it exists) and let other views lay out around it -->
+ <android.widget.Space
+ android:id="@+id/cutout_space_view"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal|center_vertical"
/>
- <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
- PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
- <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
- android:id="@+id/notification_icon_area"
- android:layout_width="0dip"
+ <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
+ android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
- android:orientation="horizontal" />
-
- <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
android:orientation="horizontal"
+ android:gravity="center_vertical|end"
>
<include layout="@layout/system_icons" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index c5e5ee1..bef0830 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -43,8 +43,6 @@
android:layout_width="@dimen/qs_panel_width"
android:layout_height="match_parent"
android:layout_gravity="@integer/notification_panel_layout_gravity"
- android:layout_marginStart="@dimen/notification_side_paddings"
- android:layout_marginEnd="@dimen/notification_side_paddings"
android:clipToPadding="false"
android:clipChildren="false"
systemui:viewType="com.android.systemui.plugins.qs.QS" />
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index bfa92ad..1fafb2f 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -16,12 +16,13 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/system_icons"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical">
- <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/statusIcons"
- android:layout_width="wrap_content"
+ <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"
+ android:layout_width="0dp"
+ android:layout_weight="1"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal"/>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0134086..4e17c71 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -215,7 +215,7 @@
<dimen name="close_handle_underlap">32dp</dimen>
<!-- Height of the status bar header bar -->
- <dimen name="status_bar_header_height">124dp</dimen>
+ <dimen name="status_bar_header_height">178dp</dimen>
<!-- Height of the status bar header bar in the car setting. -->
<dimen name="car_status_bar_header_height">128dp</dimen>
@@ -223,8 +223,9 @@
<!-- The bottom padding of the status bar header. -->
<dimen name="status_bar_header_padding_bottom">48dp</dimen>
- <!-- The height of the container that holds the system icons in the quick settings header. -->
- <dimen name="qs_header_system_icons_area_height">40dp</dimen>
+ <!-- The height of the container that holds the battery and time in the quick settings header.
+ -->
+ <dimen name="qs_header_system_icons_area_height">48dp</dimen>
<!-- The height of the container that holds the system icons in the quick settings header in the
car setting. -->
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index fed97c5..edda613 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -93,5 +93,8 @@
<item type="id" name="action_snooze_long"/>
<item type="id" name="action_snooze_longer"/>
<item type="id" name="action_snooze_assistant_suggestion_1"/>
+
+ <!-- For StatusBarIconContainer to tag its icon views -->
+ <item type="id" name="status_bar_view_state_tag" />
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 2fe66a1..8666b0c 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -224,7 +224,6 @@
if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
updatePercentText();
addView(mBatteryPercentView,
- 0,
new ViewGroup.LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 7f0acc2..7320b86 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -45,8 +45,9 @@
protected float mQsExpansion;
private QSCustomizer mQSCustomizer;
private View mQSFooter;
- private float mFullElevation;
+ private View mBackground;
private float mRadius;
+ private int mSideMargins;
public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -60,12 +61,14 @@
mHeader = findViewById(R.id.header);
mQSCustomizer = findViewById(R.id.qs_customize);
mQSFooter = findViewById(R.id.qs_footer);
- mFullElevation = mQSPanel.getElevation();
+ mBackground = findViewById(R.id.quick_settings_background);
mRadius = getResources().getDimensionPixelSize(
Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
+ mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
setClickable(true);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+ setMargins();
}
@Override
@@ -131,6 +134,8 @@
mQSDetail.setBottom(getTop() + height);
// Pin QS Footer to the bottom of the panel.
mQSFooter.setTranslationY(height - mQSFooter.getHeight());
+ mBackground.setTop(mQSPanel.getTop());
+ mBackground.setBottom(height);
ExpandableOutlineView.getRoundedRectPath(0, 0, getWidth(), height, mRadius,
mRadius,
@@ -148,4 +153,19 @@
mQsExpansion = expansion;
updateExpansion();
}
+
+ private void setMargins() {
+ setMargins(mQSDetail);
+ setMargins(mBackground);
+ setMargins(mQSFooter);
+ setMargins(mQSPanel);
+ setMargins(mHeader);
+ setMargins(mQSCustomizer);
+ }
+
+ private void setMargins(View view) {
+ FrameLayout.LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ lp.rightMargin = mSideMargins;
+ lp.leftMargin = mSideMargins;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 927a49c..92475da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -61,18 +61,16 @@
public class QSFooterImpl extends FrameLayout implements QSFooter,
OnClickListener, OnUserInfoChangedListener, EmergencyListener,
SignalCallback, CommandQueue.Callbacks {
- private static final float EXPAND_INDICATOR_THRESHOLD = .93f;
-
private ActivityStarter mActivityStarter;
private UserInfoController mUserInfoController;
private SettingsButton mSettingsButton;
protected View mSettingsContainer;
+ private View mCarrierText;
private boolean mQsDisabled;
private QSPanel mQsPanel;
private boolean mExpanded;
- protected ExpandableIndicator mExpandIndicator;
private boolean mListening;
@@ -100,18 +98,18 @@
Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
mQsPanel.showEdit(view)));
- mExpandIndicator = findViewById(R.id.expand_indicator);
mSettingsButton = findViewById(R.id.settings_button);
mSettingsContainer = findViewById(R.id.settings_button_container);
mSettingsButton.setOnClickListener(this);
+ mCarrierText = findViewById(R.id.qs_carrier_text);
+
mMultiUserSwitch = findViewById(R.id.multi_user_switch);
mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
// RenderThread is doing more harm than good when touching the header (to expand quick
// settings), so disable it for this view
((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
- ((RippleDrawable) mExpandIndicator.getBackground()).setForceSoftware(true);
updateResources();
@@ -162,6 +160,8 @@
return new TouchAnimator.Builder()
.addFloat(mEdit, "alpha", 0, 1)
.addFloat(mMultiUserSwitch, "alpha", 0, 1)
+ .addFloat(mCarrierText, "alpha", 0, 1)
+ .addFloat(mSettingsButton, "alpha", 0, 1)
.build();
}
@@ -185,8 +185,6 @@
if (mSettingsAlpha != null) {
mSettingsAlpha.setPosition(headerExpansionFraction);
}
-
- mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD);
}
@Override
@@ -237,8 +235,6 @@
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
- mExpandIndicator.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
-
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 398592a..e6fd2f4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -38,6 +38,7 @@
import com.android.systemui.qs.QSDetail.Callback;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.SignalClusterView;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue.Callbacks {
@@ -69,15 +70,13 @@
updateResources();
- // Set the light/dark theming on the header status UI to match the current theme.
+ // Set light text on the header icons because they will always be on a black background
int colorForeground = Utils.getColorAttr(getContext(), android.R.attr.colorForeground);
- float intensity = colorForeground == Color.WHITE ? 0 : 1;
Rect tintArea = new Rect(0, 0, 0, 0);
-
- applyDarkness(R.id.battery, tintArea, intensity, colorForeground);
- applyDarkness(R.id.clock, tintArea, intensity, colorForeground);
+ applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
BatteryMeterView battery = findViewById(R.id.battery);
+ battery.setFillColor(Color.WHITE);
battery.setForceShowPercent(true);
mActivityStarter = Dependency.get(ActivityStarter.class);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 970d1de..e8b28f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -16,26 +16,34 @@
package com.android.systemui.statusbar.phone;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.EventLog;
+import android.view.DisplayCutout;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
-import com.android.systemui.BatteryMeterView;
-import com.android.systemui.DejankUtils;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import com.android.systemui.Dependency;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.util.leak.RotationUtils;
public class PhoneStatusBarView extends PanelBar {
private static final String TAG = "PhoneStatusBarView";
private static final boolean DEBUG = StatusBar.DEBUG;
private static final boolean DEBUG_GESTURES = false;
+ private static final int NO_VALUE = Integer.MIN_VALUE;
StatusBar mBar;
@@ -53,6 +61,10 @@
}
};
private DarkReceiver mBattery;
+ private int mLastOrientation;
+ private View mCutoutSpace;
+ @Nullable
+ private DisplayCutout mDisplayCutout;
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -76,6 +88,7 @@
public void onFinishInflate() {
mBarTransitions.init();
mBattery = findViewById(R.id.battery);
+ mCutoutSpace = findViewById(R.id.cutout_space_view);
}
@Override
@@ -83,12 +96,51 @@
super.onAttachedToWindow();
// Always have Battery meters in the status bar observe the dark/light modes.
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
+ if (updateOrientationAndCutout(getResources().getConfiguration().orientation)) {
+ postUpdateLayoutForCutout();
+ }
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);
+ mDisplayCutout = null;
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ // May trigger cutout space layout-ing
+ if (updateOrientationAndCutout(newConfig.orientation)) {
+ postUpdateLayoutForCutout();
+ }
+ }
+
+ /**
+ *
+ * @param newOrientation may pass NO_VALUE for no change
+ * @return boolean indicating if we need to update the cutout location / margins
+ */
+ private boolean updateOrientationAndCutout(int newOrientation) {
+ boolean changed = false;
+ if (newOrientation != NO_VALUE) {
+ if (mLastOrientation != newOrientation) {
+ changed = true;
+ mLastOrientation = newOrientation;
+ }
+ }
+
+ if (mDisplayCutout == null) {
+ DisplayCutout cutout = getRootWindowInsets().getDisplayCutout();
+ if (cutout != null) {
+ changed = true;
+ mDisplayCutout = cutout;
+ }
+ }
+
+ return changed;
}
@Override
@@ -214,4 +266,75 @@
R.dimen.status_bar_height);
setLayoutParams(layoutParams);
}
+
+ private void updateLayoutForCutout() {
+ updateCutoutLocation();
+ updateSafeInsets();
+ }
+
+ private void postUpdateLayoutForCutout() {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ updateLayoutForCutout();
+ }
+ };
+ // Let the cutout emulation draw first
+ postDelayed(r, 0);
+ }
+
+ private void updateCutoutLocation() {
+ if (mDisplayCutout == null || mDisplayCutout.isEmpty()
+ || mLastOrientation != ORIENTATION_PORTRAIT) {
+ mCutoutSpace.setVisibility(View.GONE);
+ return;
+ }
+
+ mCutoutSpace.setVisibility(View.VISIBLE);
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mCutoutSpace.getLayoutParams();
+ lp.width = mDisplayCutout.getBoundingRect().width();
+ lp.height = mDisplayCutout.getBoundingRect().height();
+ }
+
+ private void updateSafeInsets() {
+ // Depending on our rotation, we may have to work around a cutout in the middle of the view,
+ // or letterboxing from the right or left sides.
+
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+ if (mDisplayCutout == null || mDisplayCutout.isEmpty()) {
+ lp.leftMargin = 0;
+ lp.rightMargin = 0;
+ return;
+ }
+
+ int leftMargin = 0;
+ int rightMargin = 0;
+ switch (RotationUtils.getRotation(getContext())) {
+ /*
+ * Landscape: <-|
+ * Seascape: |->
+ */
+ case RotationUtils.ROTATION_LANDSCAPE:
+ leftMargin = getDisplayCutoutHeight();
+ break;
+ case RotationUtils.ROTATION_SEASCAPE:
+ rightMargin = getDisplayCutoutHeight();
+ break;
+ default:
+ break;
+ }
+
+ lp.leftMargin = leftMargin;
+ lp.rightMargin = rightMargin;
+ }
+
+ //TODO: Find a better way
+ private int getDisplayCutoutHeight() {
+ if (mDisplayCutout == null || mDisplayCutout.isEmpty()) {
+ return 0;
+ }
+
+ Rect r = mDisplayCutout.getBoundingRect();
+ return r.bottom - r.top;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index bcda60e..07610ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -62,7 +62,7 @@
}
/**
- * Version of ViewGroup that observers state from the DarkIconDispatcher.
+ * Version of ViewGroup that observes state from the DarkIconDispatcher.
*/
public static class DarkIconManager extends IconManager {
private final DarkIconDispatcher mDarkIconDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
new file mode 100644
index 0000000..1897171
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * A container for Status bar system icons. Limits the number of system icons and handles overflow
+ * similar to NotificationIconController. Can be used to layout nested StatusIconContainers
+ *
+ * Children are expected to be of type StatusBarIconView.
+ */
+package com.android.systemui.statusbar.phone;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+
+import android.view.View;
+import com.android.keyguard.AlphaOptimizedLinearLayout;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.stack.ViewState;
+
+public class StatusIconContainer extends AlphaOptimizedLinearLayout {
+
+ private static final String TAG = "StatusIconContainer";
+ private static final int MAX_ICONS = 5;
+ private static final int MAX_DOTS = 3;
+
+ public StatusIconContainer(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ float midY = getHeight() / 2.0f;
+
+ // Layout all child views so that we can move them around later
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ int width = child.getMeasuredWidth();
+ int height = child.getMeasuredHeight();
+ int top = (int) (midY - height / 2.0f);
+ child.layout(0, top, width, top + height);
+ }
+
+ resetViewStates();
+ calculateIconTranslations();
+ applyIconStates();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ final int count = getChildCount();
+ // Measure all children so that they report the correct width
+ for (int i = 0; i < count; i++) {
+ measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ @Override
+ public void onViewAdded(View child) {
+ super.onViewAdded(child);
+ ViewState vs = new ViewState();
+ child.setTag(R.id.status_bar_view_state_tag, vs);
+ }
+
+ @Override
+ public void onViewRemoved(View child) {
+ super.onViewRemoved(child);
+ child.setTag(R.id.status_bar_view_state_tag, null);
+ }
+
+ /**
+ * Layout is happening from end -> start
+ */
+ private void calculateIconTranslations() {
+ float translationX = getWidth();
+ float contentStart = getPaddingStart();
+ int childCount = getChildCount();
+ // Underflow === don't show content until that index
+ int firstUnderflowIndex = -1;
+ android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX);
+
+ //TODO: Dots
+ for (int i = childCount - 1; i >= 0; i--) {
+ View child = getChildAt(i);
+ if (!(child instanceof StatusBarIconView)) {
+ continue;
+ }
+
+ ViewState childState = getViewStateFromChild(child);
+ if (childState == null ) {
+ continue;
+ }
+
+ // Rely on StatusBarIcon for truth about visibility
+ if (!((StatusBarIconView) child).getStatusBarIcon().visible) {
+ childState.hidden = true;
+ continue;
+ }
+
+ childState.xTranslation = translationX - child.getWidth();
+
+ if (childState.xTranslation < contentStart) {
+ if (firstUnderflowIndex == -1) {
+ firstUnderflowIndex = i;
+ }
+ }
+
+ translationX -= child.getWidth();
+ }
+
+ if (firstUnderflowIndex != -1) {
+ for (int i = 0; i <= firstUnderflowIndex; i++) {
+ View child = getChildAt(i);
+ ViewState vs = getViewStateFromChild(child);
+ if (vs != null) {
+ vs.hidden = true;
+ }
+ }
+ }
+ }
+
+ private void applyIconStates() {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ ViewState vs = getViewStateFromChild(child);
+ if (vs != null) {
+ vs.applyToView(child);
+ }
+ }
+ }
+
+ private void resetViewStates() {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ ViewState vs = getViewStateFromChild(child);
+ if (vs == null) {
+ continue;
+ }
+
+ vs.initFrom(child);
+ vs.alpha = 1.0f;
+ if (child instanceof StatusBarIconView) {
+ vs.hidden = !((StatusBarIconView)child).getStatusBarIcon().visible;
+ } else {
+ vs.hidden = false;
+ }
+ }
+ }
+
+ private static @Nullable ViewState getViewStateFromChild(View child) {
+ return (ViewState) child.getTag(R.id.status_bar_view_state_tag);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 66524cc..2edcd01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -74,6 +74,8 @@
assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
.getVisibility());
+ assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock)
+ .getVisibility());
}
@Test
@@ -87,11 +89,15 @@
assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
.getVisibility());
+ assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.clock)
+ .getVisibility());
fragment.disable(0, 0, false);
assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
.getVisibility());
+ assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock)
+ .getVisibility());
}
@Test
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
index fbf11fc..7387ad4 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
@@ -161,7 +161,7 @@
public boolean containsDomain(String domain) {
final CrcShaDigests domainDigests = mDomainDigests;
if (domainDigests == null) {
- Slog.wtf(TAG, "domainDigests should not be null");
+ // mDomainDigests is not initialized
return false;
}
// First it does a quick CRC32 check.
@@ -177,7 +177,7 @@
public boolean containsIp(String ip) {
final CrcShaDigests ipDigests = mIpDigests;
if (ipDigests == null) {
- Slog.wtf(TAG, "ipDigests should not be null");
+ // mIpDigests is not initialized
return false;
}
// First it does a quick CRC32 check.
@@ -234,12 +234,20 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Domain CRC32 digest list:");
- mDomainDigests.crc32Digests.dump(fd, pw, args);
+ if (mDomainDigests != null) {
+ mDomainDigests.crc32Digests.dump(fd, pw, args);
+ }
pw.println("Domain SHA256 digest list:");
- mDomainDigests.sha256Digests.dump(fd, pw, args);
+ if (mDomainDigests != null) {
+ mDomainDigests.sha256Digests.dump(fd, pw, args);
+ }
pw.println("Ip CRC32 digest list:");
- mIpDigests.crc32Digests.dump(fd, pw, args);
+ if (mIpDigests != null) {
+ mIpDigests.crc32Digests.dump(fd, pw, args);
+ }
pw.println("Ip SHA256 digest list:");
- mIpDigests.sha256Digests.dump(fd, pw, args);
+ if (mIpDigests != null) {
+ mIpDigests.sha256Digests.dump(fd, pw, args);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index b474c62..ce3f512 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -463,10 +463,14 @@
mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token);
}
- // Update the client visibility if we are not running an animation. Otherwise, we'll
- // update client visibility state in onAnimationFinished.
- if (!visible && !delayed) {
- setClientHidden(true);
+ // If we're becoming visible, immediately change client visibility as well although it
+ // usually gets changed in AppWindowContainerController.setVisibility already. However,
+ // there seem to be some edge cases where we change our visibility but client visibility
+ // never gets updated.
+ // If we're becoming invisible, update the client visibility if we are not running an
+ // animation. Otherwise, we'll update client visibility in onAnimationFinished.
+ if (visible || !delayed) {
+ setClientHidden(!visible);
}
// If we are hidden but there is no delay needed we immediately
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
index 851d2c6..654acc2 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
@@ -33,12 +33,15 @@
import org.junit.runner.RunWith;
import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.io.PrintWriter;
import java.util.Arrays;
+
/**
* runtest frameworks-services -c com.android.server.net.watchlist.WatchlistConfigTests
*/
@@ -117,6 +120,15 @@
assertEquals(TEST_XML_1_HASH, HexDump.toHexString(config.getWatchlistConfigHash()));
}
+ @Test
+ public void testWatchlistConfig_testDumpDoesNotCrash() throws Exception {
+ WatchlistConfig config = new WatchlistConfig(new File("/not_exist_path.xml"));
+ ByteArrayOutputStream bs = new ByteArrayOutputStream(2048);
+ PrintWriter pw = new PrintWriter(bs);
+ // Make sure dump still works even watchlist does not exist
+ config.dump(null, pw, null);
+ }
+
private static void copyWatchlistConfigXml(Context context, String xmlAsset, File outFile)
throws IOException {
writeToFile(outFile, readAsset(context, xmlAsset));
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 10d6deb..9f2cb92 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -66,6 +66,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -174,6 +175,7 @@
}
@Test
+ @Ignore
public void testDefaultNetworkEvents() throws Exception {
final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
@@ -292,6 +294,7 @@
}
@Test
+ @Ignore
public void testEndToEndLogging() throws Exception {
// TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
IpConnectivityLog logger = new IpConnectivityLog(mService.impl);