diff options
Diffstat (limited to 'packages/SystemUI')
73 files changed, 2285 insertions, 1671 deletions
diff --git a/packages/SystemUI/res/anim/recents_from_app_enter.xml b/packages/SystemUI/res/anim/recents_from_app_enter.xml index 6abe8b3..10ddce6 100644 --- a/packages/SystemUI/res/anim/recents_from_app_enter.xml +++ b/packages/SystemUI/res/anim/recents_from_app_enter.xml @@ -16,7 +16,7 @@ ** limitations under the License. */ --> - +<!-- Recents Activity --> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="top"> diff --git a/packages/SystemUI/res/anim/recents_from_app_exit.xml b/packages/SystemUI/res/anim/recents_from_app_exit.xml index 1447a5a..c98ecf4 100644 --- a/packages/SystemUI/res/anim/recents_from_app_exit.xml +++ b/packages/SystemUI/res/anim/recents_from_app_exit.xml @@ -16,7 +16,7 @@ ** limitations under the License. */ --> - +<!-- Incoming Activity --> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="normal"> diff --git a/packages/SystemUI/res/anim/recents_from_search_launcher_enter.xml b/packages/SystemUI/res/anim/recents_from_search_launcher_enter.xml index 20e7764..7de4460 100644 --- a/packages/SystemUI/res/anim/recents_from_search_launcher_enter.xml +++ b/packages/SystemUI/res/anim/recents_from_search_launcher_enter.xml @@ -16,13 +16,13 @@ ** limitations under the License. */ --> - +<!-- Recents Activity --> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="normal"> - <alpha android:fromAlpha="0.0" android:toAlpha="1.0" + <alpha android:fromAlpha="1.0" android:toAlpha="1.0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@android:interpolator/linear" - android:duration="50"/> + android:duration="1"/> </set> diff --git a/packages/SystemUI/res/anim/recents_from_search_launcher_exit.xml b/packages/SystemUI/res/anim/recents_from_search_launcher_exit.xml index 92a8882..e0e2fc8 100644 --- a/packages/SystemUI/res/anim/recents_from_search_launcher_exit.xml +++ b/packages/SystemUI/res/anim/recents_from_search_launcher_exit.xml @@ -16,14 +16,13 @@ ** limitations under the License. */ --> - +<!-- Launcher Activity --> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="top"> <alpha android:fromAlpha="1.0" android:toAlpha="0.0" - android:startOffset="50" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@android:interpolator/linear_out_slow_in" - android:duration="100"/> + android:duration="@integer/recents_enter_from_home_transition_duration"/> </set> diff --git a/packages/SystemUI/res/anim/recents_to_search_launcher_enter.xml b/packages/SystemUI/res/anim/recents_to_search_launcher_enter.xml index 4e1d66d..ea82835 100644 --- a/packages/SystemUI/res/anim/recents_to_search_launcher_enter.xml +++ b/packages/SystemUI/res/anim/recents_to_search_launcher_enter.xml @@ -16,7 +16,7 @@ ** limitations under the License. */ --> - +<!-- Launcher Activity --> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="normal"> diff --git a/packages/SystemUI/res/anim/recents_to_search_launcher_exit.xml b/packages/SystemUI/res/anim/recents_to_search_launcher_exit.xml index d7a12a4..a8bdc8e 100644 --- a/packages/SystemUI/res/anim/recents_to_search_launcher_exit.xml +++ b/packages/SystemUI/res/anim/recents_to_search_launcher_exit.xml @@ -16,14 +16,13 @@ ** limitations under the License. */ --> - +<!-- Recents Activity --> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="top"> - <alpha android:fromAlpha="1.0" android:toAlpha="0.0" - android:startOffset="100" + <alpha android:fromAlpha="1.0" android:toAlpha="1.0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@android:interpolator/linear" - android:duration="50"/> + android:duration="100"/> </set> diff --git a/packages/SystemUI/res/drawable-hdpi/btn_fab_recents.png b/packages/SystemUI/res/drawable-hdpi/btn_fab_recents.png Binary files differdeleted file mode 100644 index b95fde5..0000000 --- a/packages/SystemUI/res/drawable-hdpi/btn_fab_recents.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/btn_fab_recents.png b/packages/SystemUI/res/drawable-mdpi/btn_fab_recents.png Binary files differdeleted file mode 100644 index 6bbed06..0000000 --- a/packages/SystemUI/res/drawable-mdpi/btn_fab_recents.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/btn_fab_recents.png b/packages/SystemUI/res/drawable-xhdpi/btn_fab_recents.png Binary files differdeleted file mode 100644 index 4cdd0aa..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/btn_fab_recents.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/btn_fab_recents.png b/packages/SystemUI/res/drawable-xxhdpi/btn_fab_recents.png Binary files differdeleted file mode 100644 index 2220d60..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/btn_fab_recents.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable/recents_lock_to_task_button_bg.xml b/packages/SystemUI/res/drawable/recents_lock_to_task_button_bg.xml index 6424a65..8a8164a 100644 --- a/packages/SystemUI/res/drawable/recents_lock_to_task_button_bg.xml +++ b/packages/SystemUI/res/drawable/recents_lock_to_task_button_bg.xml @@ -16,5 +16,11 @@ <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="#ff9cdfd9"> - <item android:drawable="@drawable/btn_fab_recents" /> + <item> + <shape android:shape="oval"> + <solid android:color="#9cc8c4" /> + <size android:width="@dimen/recents_lock_to_app_size" + android:height="@dimen/recents_lock_to_app_size" /> + </shape> + </item> </ripple>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml index 0c0be29..b5d2f86 100644 --- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml @@ -20,7 +20,7 @@ <com.android.systemui.recent.RecentsPanelView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" + xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/recents_root" android:layout_height="match_parent" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml index c2733fb..456d2f9 100644 --- a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml +++ b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml @@ -19,7 +19,7 @@ <!-- navigation bar for sw600dp (small tablets) --> <com.android.systemui.statusbar.phone.NavigationBarView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" + xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_height="match_parent" android:layout_width="match_parent" android:background="@drawable/system_bar_background" @@ -57,6 +57,7 @@ android:layout_width="128dp" android:paddingStart="25dp" android:paddingEnd="25dp" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_back" + android:scaleType="centerInside" systemui:keyCode="4" android:layout_weight="0" android:contentDescription="@string/accessibility_back" @@ -65,6 +66,7 @@ android:layout_width="128dp" android:paddingStart="25dp" android:paddingEnd="25dp" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_home" + android:scaleType="centerInside" systemui:keyCode="3" systemui:keyRepeat="true" android:layout_weight="0" @@ -74,6 +76,7 @@ android:layout_width="128dp" android:paddingStart="25dp" android:paddingEnd="25dp" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_recent" + android:scaleType="centerInside" android:layout_weight="0" android:contentDescription="@string/accessibility_recent" /> @@ -91,6 +94,7 @@ android:layout_width="@dimen/navigation_extra_key_width" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_menu" + android:scaleType="centerInside" android:layout_marginEnd="2dp" systemui:keyCode="82" android:visibility="invisible" @@ -198,6 +202,7 @@ android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_back" + android:scaleType="centerInside" systemui:keyCode="4" android:layout_weight="0" android:contentDescription="@string/accessibility_back" @@ -206,6 +211,7 @@ android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_home" + android:scaleType="centerInside" systemui:keyCode="3" systemui:keyRepeat="true" android:layout_weight="0" @@ -215,6 +221,7 @@ android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_recent" + android:scaleType="centerInside" android:layout_weight="0" android:contentDescription="@string/accessibility_recent" /> @@ -233,6 +240,7 @@ android:layout_height="match_parent" android:layout_marginEnd="2dp" android:src="@drawable/ic_sysbar_menu" + android:scaleType="centerInside" systemui:keyCode="82" android:visibility="invisible" android:contentDescription="@string/accessibility_menu" /> diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml index 9d7c5a0..898389d 100644 --- a/packages/SystemUI/res/layout/navigation_bar.xml +++ b/packages/SystemUI/res/layout/navigation_bar.xml @@ -20,7 +20,7 @@ <com.android.systemui.statusbar.phone.NavigationBarView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" + xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_height="match_parent" android:layout_width="match_parent" android:background="@drawable/system_bar_background" diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml index f776a87..a996260 100644 --- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml +++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml @@ -14,7 +14,7 @@ limitations under the License. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" + xmlns:systemui="http://schemas.android.com/apk/res-auto" android:paddingLeft="16dp" android:paddingRight="16dp" style="@style/BrightnessDialogContainer"> diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml index d1d3828..9c6f67c 100644 --- a/packages/SystemUI/res/layout/recents_task_view.xml +++ b/packages/SystemUI/res/layout/recents_task_view.xml @@ -29,8 +29,8 @@ <include layout="@layout/recents_task_view_header" /> <FrameLayout android:id="@+id/lock_to_app_fab" - android:layout_width="48dp" - android:layout_height="48dp" + android:layout_width="@dimen/recents_lock_to_app_size" + android:layout_height="@dimen/recents_lock_to_app_size" android:layout_gravity="bottom|right" android:layout_marginRight="15dp" android:layout_marginBottom="15dp" @@ -38,8 +38,8 @@ android:contentDescription="@string/recents_lock_to_app_button_label" android:background="@drawable/recents_lock_to_task_button_bg"> <ImageView - android:layout_width="24dp" - android:layout_height="24dp" + android:layout_width="@dimen/recents_lock_to_app_icon_size" + android:layout_height="@dimen/recents_lock_to_app_icon_size" android:layout_gravity="center" android:src="@drawable/recents_lock_to_app_pin" /> </FrameLayout> diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml index 9912e89..2fb7cdb 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml @@ -18,7 +18,7 @@ <!-- Extends RelativeLayout --> <com.android.systemui.statusbar.phone.StatusBarHeaderView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" + xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/header" android:layout_width="@dimen/notification_panel_width" android:layout_height="@dimen/status_bar_header_height" diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml index ef85847..4526af5 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml @@ -32,6 +32,7 @@ /> <LinearLayout + android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView diff --git a/packages/SystemUI/res/layout/status_bar_recent_panel.xml b/packages/SystemUI/res/layout/status_bar_recent_panel.xml index 2f3968d..588873a 100644 --- a/packages/SystemUI/res/layout/status_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout/status_bar_recent_panel.xml @@ -20,7 +20,7 @@ <com.android.systemui.recent.RecentsPanelView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" + xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/recents_root" android:layout_height="match_parent" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 44e0fd1..1152eea 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -144,42 +144,61 @@ <!-- The duration in seconds to wait before the dismiss buttons are shown. --> <integer name="recents_task_bar_dismiss_delay_seconds">1</integer> + <!-- The min animation duration for animating views that are currently visible. --> <integer name="recents_filter_animate_current_views_duration">250</integer> + <!-- The min animation duration for animating views that are newly visible. --> <integer name="recents_filter_animate_new_views_duration">250</integer> - <!-- The min animation duration for animating the task bar in. --> - <integer name="recents_animate_task_bar_enter_duration">225</integer> - <!-- The animation delay for animating the first task in. This should roughly be the animation - duration of the transition in to recents. --> - <integer name="recents_animate_task_bar_enter_delay">275</integer> - <!-- The min animation duration for animating the task bar out. --> - <integer name="recents_animate_task_exit_to_home_duration">225</integer> - <!-- The min animation duration for animating the task bar out. --> - <integer name="recents_animate_task_bar_exit_duration">125</integer> - <!-- The animation delay for animating the first task in. This should roughly be the animation - duration of the transition in to recents from home. --> - <integer name="recents_animate_task_enter_from_home_delay">150</integer> - <!-- The min animation duration for animating the task in when transitioning from home. --> - <integer name="recents_animate_task_enter_from_home_duration">250</integer> - <!-- The animation stagger to apply to each task animation when transitioning from home. --> - <integer name="recents_animate_task_enter_from_home_stagger_delay">12</integer> - <!-- The short duration when animating in/out the lock to app button. --> - <integer name="recents_animate_lock_to_app_button_short_duration">150</integer> - <!-- The long duration when animating in/out the lock to app button. --> - <integer name="recents_animate_lock_to_app_button_long_duration">300</integer> + + <!-- The duration of the window transition when coming to Recents from an app. + In order to defer the in-app animations until after the transition is complete, + we also need to use this value as the starting delay when animating the first + task decorations in. --> + <integer name="recents_enter_from_app_transition_duration">325</integer> + + <!-- The duration for animating the task decorations in after transitioning from an app. --> + <integer name="recents_task_enter_from_app_duration">200</integer> + + <!-- The duration for animating the task decorations out before transitioning to an app. --> + <integer name="recents_task_exit_to_app_duration">125</integer> + + <!-- The duration of the window transition when coming to Recents from the Launcher. + In order to defer the in-app animations until after the transition is complete, + we also need to use this value as the starting delay when animating the task views + in from the bottom of the screen. --> + <integer name="recents_enter_from_home_transition_duration">100</integer> + + <!-- The duration for animating the task from the bottom of the screen when transitioning + from home. --> + <integer name="recents_task_enter_from_home_duration">225</integer> + + <!-- The stagger for each task when animating the task from the bottom of the screen when + transitioning from home. --> + <integer name="recents_task_enter_from_home_stagger_delay">12</integer> + + <!-- The duration of the animation of the tasks to the bottom of the screen when leaving + Recents to go back to the Launcher. --> + <integer name="recents_task_exit_to_home_duration">225</integer> + <!-- The min animation duration for animating the nav bar scrim in. --> <integer name="recents_nav_bar_scrim_enter_duration">400</integer> + <!-- The animation duration for animating the removal of a task view. --> <integer name="recents_animate_task_view_remove_duration">250</integer> + <!-- The animation duration for scrolling the stack to a particular item. --> <integer name="recents_animate_task_stack_scroll_duration">225</integer> + <!-- The minimum alpha for the dim applied to cards that go deeper into the stack. --> <integer name="recents_max_task_stack_view_dim">96</integer> + <!-- The delay to enforce between each alt-tab key press. --> <integer name="recents_alt_tab_key_delay">200</integer> + <!-- Transposes the search bar layout in landscape. --> <bool name="recents_has_transposed_search_bar">true</bool> + <!-- Transposes the nav bar in landscape (only used for purposes of layout). --> <bool name="recents_has_transposed_nav_bar">true</bool> <!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 3d9f723..1143553 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -226,9 +226,6 @@ <!-- The amount of highlight to make on each task view. --> <dimen name="recents_task_view_highlight">1.5dp</dimen> - <!-- The height of the lock-to-app button. --> - <dimen name="recents_task_view_lock_to_app_button_height">48dp</dimen> - <!-- The amount to offset when animating into an affiliate group. --> <dimen name="recents_task_view_affiliate_group_enter_offset">64dp</dimen> @@ -257,6 +254,12 @@ <!-- The min alpha to apply to a task affiliation group color. --> <item name="recents_task_affiliation_color_min_alpha_percentage" format="float" type="dimen">0.6</item> + <!-- The size of the lock-to-app button. --> + <dimen name="recents_lock_to_app_size">56dp</dimen> + + <!-- The size of the lock-to-app button icon. --> + <dimen name="recents_lock_to_app_icon_size">28dp</dimen> + <!-- Space reserved for the cards behind the top card in the top stack --> <dimen name="top_stack_peek_amount">12dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java new file mode 100644 index 0000000..2ff8f8a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java @@ -0,0 +1,101 @@ +/* + * 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; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Paint; +import android.view.View; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; + +import com.android.systemui.statusbar.phone.NotificationPanelView; + +/** + * Helper to invert the colors of views and fade between the states. + */ +public class ViewInvertHelper { + + private final Paint mDarkPaint = new Paint(); + private final Interpolator mLinearOutSlowInInterpolator; + private final View mTarget; + private final ColorMatrix mMatrix = new ColorMatrix(); + private final ColorMatrix mGrayscaleMatrix = new ColorMatrix(); + private final long mFadeDuration; + + public ViewInvertHelper(View target, long fadeDuration) { + mTarget = target; + mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(mTarget.getContext(), + android.R.interpolator.linear_out_slow_in); + mFadeDuration = fadeDuration; + } + + public void fade(final boolean invert, long delay) { + float startIntensity = invert ? 0f : 1f; + float endIntensity = invert ? 1f : 0f; + ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + updateInvertPaint((Float) animation.getAnimatedValue()); + mTarget.setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint); + } + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (!invert) { + mTarget.setLayerType(View.LAYER_TYPE_NONE, null); + } + } + }); + animator.setDuration(mFadeDuration); + animator.setInterpolator(mLinearOutSlowInInterpolator); + animator.setStartDelay(delay); + animator.start(); + } + + public void update(boolean invert) { + if (invert) { + updateInvertPaint(1f); + mTarget.setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint); + } else { + mTarget.setLayerType(View.LAYER_TYPE_NONE, null); + } + } + + public View getTarget() { + return mTarget; + } + + private void updateInvertPaint(float intensity) { + float components = 1 - 2 * intensity; + final float[] invert = { + components, 0f, 0f, 0f, 255f * intensity, + 0f, components, 0f, 0f, 255f * intensity, + 0f, 0f, components, 0f, 255f * intensity, + 0f, 0f, 0f, 1f, 0f + }; + mMatrix.set(invert); + mGrayscaleMatrix.setSaturation(1 - intensity); + mMatrix.preConcat(mGrayscaleMatrix); + mDarkPaint.setColorFilter(new ColorMatrixColorFilter(mMatrix)); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java index 0ab6626..4f812bc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java +++ b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java @@ -26,24 +26,26 @@ import com.android.systemui.statusbar.policy.Listenable; /** Helper for managing a secure setting. **/ public abstract class SecureSetting extends ContentObserver implements Listenable { + private static final int DEFAULT = 0; + private final Context mContext; private final String mSettingName; private boolean mListening; private int mUserId; + private int mObservedValue = DEFAULT; - protected abstract void handleValueChanged(int value); + protected abstract void handleValueChanged(int value, boolean observedChange); public SecureSetting(Context context, Handler handler, String settingName) { super(handler); mContext = context; mSettingName = settingName; mUserId = ActivityManager.getCurrentUser(); - setListening(true); } public int getValue() { - return Secure.getIntForUser(mContext.getContentResolver(), mSettingName, 0, mUserId); + return Secure.getIntForUser(mContext.getContentResolver(), mSettingName, DEFAULT, mUserId); } public void setValue(int value) { @@ -52,18 +54,23 @@ public abstract class SecureSetting extends ContentObserver implements Listenabl @Override public void setListening(boolean listening) { + if (listening == mListening) return; mListening = listening; if (listening) { + mObservedValue = getValue(); mContext.getContentResolver().registerContentObserver( Secure.getUriFor(mSettingName), false, this, mUserId); } else { mContext.getContentResolver().unregisterContentObserver(this); + mObservedValue = DEFAULT; } } @Override public void onChange(boolean selfChange) { - handleValueChanged(getValue()); + final int value = getValue(); + handleValueChanged(value, value != mObservedValue); + mObservedValue = value; } public void setUserId(int userId) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index 4d77348..1bc1d77 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -77,6 +77,10 @@ public class BluetoothTile extends QSTile<QSTile.BooleanState> { @Override protected void handleSecondaryClick() { + if (!mState.value) { + mState.value = true; + mController.setBluetoothEnabled(true); + } showDetail(true); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index 178590b..80ddd4a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -29,7 +29,8 @@ import com.android.systemui.qs.QSTile; import com.android.systemui.qs.QSTileView; import com.android.systemui.qs.SignalTileView; import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkController.DataUsageInfo; +import com.android.systemui.statusbar.policy.NetworkController.MobileDataController; +import com.android.systemui.statusbar.policy.NetworkController.MobileDataController.DataUsageInfo; import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; /** Quick settings tile: Cellular **/ @@ -38,11 +39,13 @@ public class CellularTile extends QSTile<QSTile.SignalState> { "com.android.settings", "com.android.settings.Settings$DataUsageSummaryActivity")); private final NetworkController mController; + private final MobileDataController mDataController; private final CellularDetailAdapter mDetailAdapter; public CellularTile(Host host) { super(host); mController = host.getNetworkController(); + mDataController = mController.getMobileDataController(); mDetailAdapter = new CellularDetailAdapter(); } @@ -72,7 +75,7 @@ public class CellularTile extends QSTile<QSTile.SignalState> { @Override protected void handleClick() { - if (mController.isMobileDataSupported()) { + if (mDataController.isMobileDataSupported()) { showDetail(true); } else { mHost.startSettingsActivity(CELLULAR_SETTINGS); @@ -199,7 +202,8 @@ public class CellularTile extends QSTile<QSTile.SignalState> { @Override public Boolean getToggleState() { - return mController.isMobileDataSupported() ? mController.isMobileDataEnabled() : null; + return mDataController.isMobileDataSupported() + ? mDataController.isMobileDataEnabled() : null; } @Override @@ -209,7 +213,7 @@ public class CellularTile extends QSTile<QSTile.SignalState> { @Override public void setToggleState(boolean state) { - mController.setMobileDataEnabled(state); + mDataController.setMobileDataEnabled(state); } @Override @@ -217,7 +221,7 @@ public class CellularTile extends QSTile<QSTile.SignalState> { final DataUsageDetailView v = (DataUsageDetailView) (convertView != null ? convertView : LayoutInflater.from(mContext).inflate(R.layout.data_usage, parent, false)); - final DataUsageInfo info = mController.getDataUsageInfo(); + final DataUsageInfo info = mDataController.getDataUsageInfo(); if (info == null) return v; v.bind(info); return v; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java index b565afa..5963a45 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -41,8 +41,10 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { mSetting = new SecureSetting(mContext, mHandler, Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) { @Override - protected void handleValueChanged(int value) { - mUsageTracker.trackUsage(); + protected void handleValueChanged(int value, boolean observedChange) { + if (value != 0 || observedChange) { + mUsageTracker.trackUsage(); + } if (mListening) { handleRefreshState(value); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java index 7bdb58f..eb816b7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java @@ -20,7 +20,6 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.util.AttributeSet; -import android.util.TypedValue; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; @@ -61,7 +60,7 @@ public class DataUsageDetailView extends LinearLayout { R.dimen.qs_data_usage_text_size); } - public void bind(NetworkController.DataUsageInfo info) { + public void bind(NetworkController.MobileDataController.DataUsageInfo info) { final Resources res = mContext.getResources(); final int titleId; final long bytes; 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 7aa884e..4fb1189 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -31,7 +31,8 @@ import com.android.systemui.qs.QSTile; import com.android.systemui.qs.QSTileView; import com.android.systemui.qs.SignalTileView; import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkController.AccessPoint; +import com.android.systemui.statusbar.policy.NetworkController.AccessPointController; +import com.android.systemui.statusbar.policy.NetworkController.AccessPointController.AccessPoint; import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; /** Quick settings tile: Wifi **/ @@ -39,12 +40,14 @@ public class WifiTile extends QSTile<QSTile.SignalState> { private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS); private final NetworkController mController; + private final AccessPointController mWifiController; private final WifiDetailAdapter mDetailAdapter; private final QSTile.SignalState mStateBeforeClick = newTileState(); public WifiTile(Host host) { super(host); mController = host.getNetworkController(); + mWifiController = mController.getAccessPointController(); mDetailAdapter = new WifiDetailAdapter(); } @@ -62,10 +65,10 @@ public class WifiTile extends QSTile<QSTile.SignalState> { public void setListening(boolean listening) { if (listening) { mController.addNetworkSignalChangedCallback(mCallback); - mController.addAccessPointCallback(mDetailAdapter); + mWifiController.addAccessPointCallback(mDetailAdapter); } else { mController.removeNetworkSignalChangedCallback(mCallback); - mController.removeAccessPointCallback(mDetailAdapter); + mWifiController.removeAccessPointCallback(mDetailAdapter); } } @@ -87,7 +90,7 @@ public class WifiTile extends QSTile<QSTile.SignalState> { @Override protected void handleSecondaryClick() { - if (!mController.canConfigWifi()) { + if (!mWifiController.canConfigWifi()) { mHost.startSettingsActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)); return; } @@ -231,7 +234,7 @@ public class WifiTile extends QSTile<QSTile.SignalState> { }; private final class WifiDetailAdapter implements DetailAdapter, - NetworkController.AccessPointCallback, QSDetailItems.Callback { + NetworkController.AccessPointController.AccessPointCallback, QSDetailItems.Callback { private QSDetailItems mItems; private AccessPoint[] mAccessPoints; @@ -261,7 +264,7 @@ public class WifiTile extends QSTile<QSTile.SignalState> { public View createDetailView(Context context, View convertView, ViewGroup parent) { if (DEBUG) Log.d(TAG, "createDetailView convertView=" + (convertView != null)); mAccessPoints = null; - mController.scanForAccessPoints(); + mWifiController.scanForAccessPoints(); fireScanStateChanged(true); mItems = QSDetailItems.convertOrInflate(context, convertView, parent); mItems.setTagSuffix("Wifi"); @@ -287,7 +290,7 @@ public class WifiTile extends QSTile<QSTile.SignalState> { if (item == null || item.tag == null) return; final AccessPoint ap = (AccessPoint) item.tag; if (!ap.isConnected) { - if (mController.connect(ap)) { + if (mWifiController.connect(ap)) { mHost.collapsePanels(); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java index bb9a105..38ce467 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java @@ -32,6 +32,7 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; import android.os.Handler; +import android.os.SystemClock; import android.os.UserHandle; import android.util.Pair; import android.view.LayoutInflater; @@ -361,8 +362,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta // If the user has toggled it too quickly, then just eat up the event here (it's better than // showing a janky screenshot). // NOTE: Ideally, the screenshot mechanism would take the window transform into account - long currentTime = System.currentTimeMillis(); - if ((currentTime > mLastToggleTime) && (currentTime - mLastToggleTime) < sMinToggleDelay) { + if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) { return; } @@ -377,7 +377,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT); - mLastToggleTime = System.currentTimeMillis(); + mLastToggleTime = SystemClock.elapsedRealtime(); return; } else { // Otherwise, start the recents activity @@ -559,7 +559,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_HOME, stackVr); } } - mLastToggleTime = System.currentTimeMillis(); + mLastToggleTime = SystemClock.elapsedRealtime(); } /** Starts the recents activity */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index d1eadd8..6dc2edb 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -28,6 +28,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.Bundle; +import android.os.SystemClock; import android.os.UserHandle; import android.util.Pair; import android.view.KeyEvent; @@ -431,8 +432,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Update if we are getting a configuration change if (savedInstanceState != null) { // Update RecentsConfiguration - mConfig = RecentsConfiguration.reinitialize(this, - RecentsTaskLoader.getInstance().getSystemServicesProxy()); mConfig.updateOnConfigurationChange(); // Trigger the enter animation onEnterAnimationTriggered(); @@ -566,13 +565,13 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_TAB: { - boolean hasRepKeyTimeElapsed = (System.currentTimeMillis() - + boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() - mLastTabKeyEventTime) > mConfig.altTabKeyDelay; if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) { // Focus the next task in the stack final boolean backward = event.isShiftPressed(); mRecentsView.focusNextTask(!backward); - mLastTabKeyEventTime = System.currentTimeMillis(); + mLastTabKeyEventTime = SystemClock.elapsedRealtime(); } return true; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index 43d9dc1..2b33d14 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -86,11 +86,15 @@ public class RecentsConfiguration { public float taskStackWidthPaddingPct; public float taskStackOverscrollPct; + /** Transitions */ + public int transitionEnterFromAppDelay; + public int transitionEnterFromHomeDelay; + /** Task view animation and styles */ - public int taskViewEnterFromHomeDelay; + public int taskViewEnterFromAppDuration; public int taskViewEnterFromHomeDuration; public int taskViewEnterFromHomeStaggerDelay; - public int taskViewEnterFromHomeStaggerDuration; + public int taskViewExitToAppDuration; public int taskViewExitToHomeDuration; public int taskViewRemoveAnimDuration; public int taskViewRemoveAnimTranslationXPx; @@ -110,16 +114,8 @@ public class RecentsConfiguration { /** Task bar size & animations */ public int taskBarHeight; - public int taskBarEnterAnimDuration; - public int taskBarEnterAnimDelay; - public int taskBarExitAnimDuration; public int taskBarDismissDozeDelaySeconds; - /** Lock to app */ - public int taskViewLockToAppButtonHeight; - public int taskViewLockToAppShortAnimDuration; - public int taskViewLockToAppLongAnimDuration; - /** Nav bar scrim */ public int navBarScrimEnterDuration; @@ -226,15 +222,23 @@ public class RecentsConfiguration { taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim); taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.recents_stack_top_padding); + // Transition + transitionEnterFromAppDelay = + res.getInteger(R.integer.recents_enter_from_app_transition_duration); + transitionEnterFromHomeDelay = + res.getInteger(R.integer.recents_enter_from_home_transition_duration); + // Task view animation and styles - taskViewEnterFromHomeDelay = - res.getInteger(R.integer.recents_animate_task_enter_from_home_delay); + taskViewEnterFromAppDuration = + res.getInteger(R.integer.recents_task_enter_from_app_duration); taskViewEnterFromHomeDuration = - res.getInteger(R.integer.recents_animate_task_enter_from_home_duration); + res.getInteger(R.integer.recents_task_enter_from_home_duration); taskViewEnterFromHomeStaggerDelay = - res.getInteger(R.integer.recents_animate_task_enter_from_home_stagger_delay); + res.getInteger(R.integer.recents_task_enter_from_home_stagger_delay); + taskViewExitToAppDuration = + res.getInteger(R.integer.recents_task_exit_to_app_duration); taskViewExitToHomeDuration = - res.getInteger(R.integer.recents_animate_task_exit_to_home_duration); + res.getInteger(R.integer.recents_task_exit_to_home_duration); taskViewRemoveAnimDuration = res.getInteger(R.integer.recents_animate_task_view_remove_duration); taskViewRemoveAnimTranslationXPx = @@ -265,23 +269,9 @@ public class RecentsConfiguration { // Task bar size & animations taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height); - taskBarEnterAnimDuration = - res.getInteger(R.integer.recents_animate_task_bar_enter_duration); - taskBarEnterAnimDelay = - res.getInteger(R.integer.recents_animate_task_bar_enter_delay); - taskBarExitAnimDuration = - res.getInteger(R.integer.recents_animate_task_bar_exit_duration); taskBarDismissDozeDelaySeconds = res.getInteger(R.integer.recents_task_bar_dismiss_delay_seconds); - // Lock to app - taskViewLockToAppButtonHeight = - res.getDimensionPixelSize(R.dimen.recents_task_view_lock_to_app_button_height); - taskViewLockToAppShortAnimDuration = - res.getInteger(R.integer.recents_animate_lock_to_app_button_short_duration); - taskViewLockToAppLongAnimDuration = - res.getInteger(R.integer.recents_animate_lock_to_app_button_long_duration); - // Nav bar scrim navBarScrimEnterDuration = res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration); 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 67a36b9..ba2903a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java @@ -170,66 +170,66 @@ class TaskResourceLoader implements Runnable { } else { RecentsConfiguration config = RecentsConfiguration.getInstance(); SystemServicesProxy ssp = mSystemServicesProxy; - // If we've stopped the loader, then fall thorugh to the above logic to wait on + // If we've stopped the loader, then fall through to the above logic to wait on // the load thread - if (ssp == null) continue; - - // Load the next item from the queue - final Task t = mLoadQueue.nextTask(); - if (t != null) { - Drawable cachedIcon = mApplicationIconCache.get(t.key); - Bitmap cachedThumbnail = mThumbnailCache.get(t.key); - - // Load the application icon if it is stale or we haven't cached one yet - if (cachedIcon == null) { - cachedIcon = getTaskDescriptionIcon(t.key, t.icon, t.iconFilename, ssp, - mContext.getResources()); - + if (ssp != null) { + // Load the next item from the queue + final Task t = mLoadQueue.nextTask(); + if (t != null) { + Drawable cachedIcon = mApplicationIconCache.get(t.key); + Bitmap cachedThumbnail = mThumbnailCache.get(t.key); + + // Load the application icon if it is stale or we haven't cached one yet if (cachedIcon == null) { - ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent(), - t.key.userId); - if (info != null) { - if (DEBUG) Log.d(TAG, "Loading icon: " + t.key); - cachedIcon = ssp.getActivityIcon(info, t.key.userId); + cachedIcon = getTaskDescriptionIcon(t.key, t.icon, t.iconFilename, ssp, + mContext.getResources()); + + if (cachedIcon == null) { + ActivityInfo info = ssp.getActivityInfo( + t.key.baseIntent.getComponent(), t.key.userId); + if (info != null) { + if (DEBUG) Log.d(TAG, "Loading icon: " + t.key); + cachedIcon = ssp.getActivityIcon(info, t.key.userId); + } } - } - if (cachedIcon == null) { - cachedIcon = mDefaultApplicationIcon; - } + if (cachedIcon == null) { + cachedIcon = mDefaultApplicationIcon; + } - // At this point, even if we can't load the icon, we will set the default - // icon. - mApplicationIconCache.put(t.key, cachedIcon); - } - // Load the thumbnail if it is stale or we haven't cached one yet - if (cachedThumbnail == null) { - if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) { - if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key); - cachedThumbnail = ssp.getTaskThumbnail(t.key.id); + // At this point, even if we can't load the icon, we will set the + // default icon. + mApplicationIconCache.put(t.key, cachedIcon); } + // Load the thumbnail if it is stale or we haven't cached one yet if (cachedThumbnail == null) { - cachedThumbnail = mDefaultThumbnail; + if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) { + if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key); + cachedThumbnail = ssp.getTaskThumbnail(t.key.id); + } + if (cachedThumbnail == null) { + cachedThumbnail = mDefaultThumbnail; + } + // When svelte, we trim the memory to just the visible thumbnails when + // leaving, so don't thrash the cache as the user scrolls (just load + // them from scratch each time) + if (config.svelteLevel < RecentsConfiguration.SVELTE_LIMIT_CACHE) { + mThumbnailCache.put(t.key, cachedThumbnail); + } } - // When svelte, we trim the memory to just the visible thumbnails when - // leaving, so don't thrash the cache as the user scrolls (just load them - // from scratch each time) - if (config.svelteLevel < RecentsConfiguration.SVELTE_LIMIT_CACHE) { - mThumbnailCache.put(t.key, cachedThumbnail); + if (!mCancelled) { + // Notify that the task data has changed + final Drawable newIcon = cachedIcon; + final Bitmap newThumbnail = cachedThumbnail == mDefaultThumbnail + ? null : cachedThumbnail; + mMainThreadHandler.post(new Runnable() { + @Override + public void run() { + t.notifyTaskDataLoaded(newThumbnail, newIcon); + } + }); } } - if (!mCancelled) { - // Notify that the task data has changed - final Drawable newIcon = cachedIcon; - final Bitmap newThumbnail = cachedThumbnail == mDefaultThumbnail - ? null : cachedThumbnail; - mMainThreadHandler.post(new Runnable() { - @Override - public void run() { - t.notifyTaskDataLoaded(newThumbnail, newIcon); - } - }); - } } // If there are no other items in the list, then just wait until something is added diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java index 5f8f3f2..fb05c01 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java @@ -16,7 +16,6 @@ package com.android.systemui.recents.views; -import android.animation.ObjectAnimator; import android.graphics.Outline; import android.graphics.Rect; import android.view.View; @@ -35,8 +34,6 @@ public class AnimateableViewBounds extends ViewOutlineProvider { float mAlpha = 1f; final float mMinAlpha = 0.25f; - ObjectAnimator mClipBottomAnimator; - public AnimateableViewBounds(TaskView source, int cornerRadius) { mConfig = RecentsConfiguration.getInstance(); mSourceView = source; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java index 162897e..1086160 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java @@ -64,7 +64,9 @@ public class SystemBarScrimViews { mStatusBarScrimView.setTranslationY(-mStatusBarScrimView.getMeasuredHeight()); mStatusBarScrimView.animate() .translationY(0) - .setStartDelay(mConfig.taskBarEnterAnimDelay) + .setStartDelay(mConfig.launchedFromHome ? + mConfig.transitionEnterFromHomeDelay : + mConfig.transitionEnterFromAppDelay) .setDuration(mConfig.navBarScrimEnterDuration) .setInterpolator(mConfig.quintOutInterpolator) .withStartAction(new Runnable() { @@ -79,7 +81,9 @@ public class SystemBarScrimViews { mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight()); mNavBarScrimView.animate() .translationY(0) - .setStartDelay(mConfig.taskBarEnterAnimDelay) + .setStartDelay(mConfig.launchedFromHome ? + mConfig.transitionEnterFromHomeDelay : + mConfig.transitionEnterFromAppDelay) .setDuration(mConfig.navBarScrimEnterDuration) .setInterpolator(mConfig.quintOutInterpolator) .withStartAction(new Runnable() { @@ -101,7 +105,7 @@ public class SystemBarScrimViews { mStatusBarScrimView.animate() .translationY(-mStatusBarScrimView.getMeasuredHeight()) .setStartDelay(0) - .setDuration(mConfig.taskBarExitAnimDuration) + .setDuration(mConfig.taskViewExitToAppDuration) .setInterpolator(mConfig.fastOutSlowInInterpolator) .start(); } @@ -109,7 +113,7 @@ public class SystemBarScrimViews { mNavBarScrimView.animate() .translationY(mNavBarScrimView.getMeasuredHeight()) .setStartDelay(0) - .setDuration(mConfig.taskBarExitAnimDuration) + .setDuration(mConfig.taskViewExitToAppDuration) .setInterpolator(mConfig.fastOutSlowInInterpolator) .start(); } 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 1731d38..5732879 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -186,6 +186,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mUIDozeTrigger.stopDozing(); mUIDozeTrigger.resetTrigger(); } + mStackScroller.reset(); } /** Requests that the views be synchronized with the model */ @@ -659,9 +660,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // If this is the first layout, then scroll to the front of the stack and synchronize the // stack views immediately to load all the views if (mAwaitingFirstLayout) { - mStackScroller.setStackScrollToInitialState(); - requestSynchronizeStackViewsWithModel(); - synchronizeStackViewsWithModel(); + if (mStackScroller.setStackScrollToInitialState()) { + requestSynchronizeStackViewsWithModel(); + synchronizeStackViewsWithModel(); + } } // Measure each of the TaskViews @@ -915,7 +917,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal TaskView frontTv = getChildViewForTask(newFrontMostTask); if (frontTv != null) { frontTv.onTaskBound(newFrontMostTask); - frontTv.fadeInActionButton(false); + frontTv.fadeInActionButton(0, mConfig.taskViewEnterFromAppDuration); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java index 04f7c6f..f7067be 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java @@ -48,6 +48,11 @@ public class TaskStackViewScroller { setStackScroll(getStackScroll()); } + /** Resets the task scroller. */ + void reset() { + mStackScrollP = 0f; + } + /** Sets the callbacks */ void setCallbacks(TaskStackViewScrollerCallbacks cb) { mCb = cb; @@ -71,9 +76,14 @@ public class TaskStackViewScroller { mStackScrollP = s; } - /** Sets the current stack scroll to the initial state when you first enter recents */ - public void setStackScrollToInitialState() { + /** + * Sets the current stack scroll to the initial state when you first enter recents. + * @return whether the stack progress changed. + */ + public boolean setStackScrollToInitialState() { + float prevStackScrollP = mStackScrollP; setStackScroll(getBoundedStackScroll(mLayoutAlgorithm.mInitialScrollP)); + return Float.compare(prevStackScrollP, mStackScrollP) != 0; } /** Bounds the current scroll if necessary */ 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 27d2fda..d42fa15 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -24,7 +24,6 @@ import android.graphics.*; import android.util.AttributeSet; import android.view.View; import android.view.ViewOutlineProvider; -import android.view.ViewPropertyAnimator; import android.view.animation.AccelerateInterpolator; import android.widget.FrameLayout; import com.android.systemui.R; @@ -271,24 +270,25 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, if (Constants.DebugFlags.App.EnableThumbnailAlphaOnFrontmost) { // Animate the thumbnail alpha before the dim animation (to prevent updating the // hardware layer) - mThumbnailView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay, + mThumbnailView.startEnterRecentsAnimation(mConfig.transitionEnterFromAppDelay, new Runnable() { @Override public void run() { - animateDimToProgress(0, mConfig.taskBarEnterAnimDuration, + animateDimToProgress(0, mConfig.taskViewEnterFromAppDuration, ctx.postAnimationTrigger.decrementOnAnimationEnd()); } }); } else { // Immediately start the dim animation - animateDimToProgress(mConfig.taskBarEnterAnimDelay, - mConfig.taskBarEnterAnimDuration, + animateDimToProgress(mConfig.transitionEnterFromAppDelay, + mConfig.taskViewEnterFromAppDuration, ctx.postAnimationTrigger.decrementOnAnimationEnd()); } ctx.postAnimationTrigger.increment(); // Animate the action button in - fadeInActionButton(true); + fadeInActionButton(mConfig.transitionEnterFromAppDelay, + mConfig.taskViewEnterFromAppDuration); } else { // Animate the task up if it was occluding the launch target if (ctx.currentTaskOccludesLaunchTarget) { @@ -296,7 +296,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, setAlpha(0f); animate().alpha(1f) .translationY(transform.translationY) - .setStartDelay(mConfig.taskBarEnterAnimDelay) + .setStartDelay(mConfig.transitionEnterFromAppDelay) .setUpdateListener(null) .setInterpolator(mConfig.fastOutSlowInInterpolator) .setDuration(mConfig.taskViewEnterFromHomeDuration) @@ -311,12 +311,12 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, ctx.postAnimationTrigger.increment(); } } - startDelay = mConfig.taskBarEnterAnimDelay; + startDelay = mConfig.transitionEnterFromAppDelay; } else if (mConfig.launchedFromHome) { // Animate the tasks up int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1); - int delay = mConfig.taskViewEnterFromHomeDelay + + int delay = mConfig.transitionEnterFromHomeDelay + frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay; setScaleX(transform.scale); @@ -353,19 +353,17 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, }, (startDelay / 2)); } - public void fadeInActionButton(boolean withDelay) { + public void fadeInActionButton(int delay, int duration) { // Hide the action button mActionButtonView.setAlpha(0f); // Animate the action button in - ViewPropertyAnimator animator = mActionButtonView.animate().alpha(1f) - .setDuration(mConfig.taskBarEnterAnimDuration) + mActionButtonView.animate().alpha(1f) + .setStartDelay(delay) + .setDuration(duration) .setInterpolator(PhoneStatusBar.ALPHA_IN) - .withLayer(); - if (withDelay) { - animator.setStartDelay(mConfig.taskBarEnterAnimDelay); - } - animator.start(); + .withLayer() + .start(); } /** Animates this task view as it leaves recents by pressing home. */ @@ -391,7 +389,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, // Animate the dim if (mDimAlpha > 0) { ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0); - anim.setDuration(mConfig.taskBarExitAnimDuration); + anim.setDuration(mConfig.taskViewExitToAppDuration); anim.setInterpolator(mConfig.fastOutLinearInInterpolator); anim.start(); } @@ -406,7 +404,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, mActionButtonView.animate() .alpha(0f) .setStartDelay(0) - .setDuration(mConfig.taskBarExitAnimDuration) + .setDuration(mConfig.taskViewExitToAppDuration) .setInterpolator(mConfig.fastOutLinearInInterpolator) .withLayer() .start(); @@ -421,7 +419,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, .setStartDelay(0) .setUpdateListener(null) .setInterpolator(mConfig.fastOutLinearInInterpolator) - .setDuration(mConfig.taskBarExitAnimDuration) + .setDuration(mConfig.taskViewExitToAppDuration) .start(); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java index 5de84bd..1e5d9fc 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java @@ -235,7 +235,7 @@ public class TaskViewHeader extends FrameLayout { .alpha(0f) .setStartDelay(0) .setInterpolator(mConfig.fastOutSlowInInterpolator) - .setDuration(mConfig.taskBarExitAnimDuration) + .setDuration(mConfig.taskViewExitToAppDuration) .withLayer() .start(); } @@ -249,7 +249,7 @@ public class TaskViewHeader extends FrameLayout { .alpha(1f) .setStartDelay(0) .setInterpolator(mConfig.fastOutLinearInInterpolator) - .setDuration(mConfig.taskBarEnterAnimDuration) + .setDuration(mConfig.taskViewEnterFromAppDuration) .withLayer() .start(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java index c83248e..117a7d3 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java @@ -237,12 +237,12 @@ public class TaskViewThumbnail extends View { /** Animates this task thumbnail as it enters Recents. */ void startEnterRecentsAnimation(int delay, Runnable postAnimRunnable) { startFadeAnimation(mConfig.taskViewThumbnailAlpha, delay, - mConfig.taskBarEnterAnimDuration, postAnimRunnable); + mConfig.taskViewEnterFromAppDuration, postAnimRunnable); } /** Animates this task thumbnail as it exits Recents. */ void startLaunchTaskAnimation(Runnable postAnimRunnable) { - startFadeAnimation(1f, 0, mConfig.taskBarExitAnimDuration, postAnimRunnable); + startFadeAnimation(1f, 0, mConfig.taskViewExitToAppDuration, postAnimRunnable); } /** Starts a new thumbnail alpha animation. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index 02b9378..7b60307 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -43,6 +43,7 @@ import android.view.animation.LinearInterpolator; import android.view.animation.PathInterpolator; import com.android.systemui.R; +import com.android.systemui.statusbar.phone.NotificationPanelView; /** * Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer} @@ -53,6 +54,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private static final long DOUBLETAP_TIMEOUT_MS = 1200; private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220; private static final int ACTIVATE_ANIMATION_LENGTH = 220; + private static final int DARK_ANIMATION_LENGTH = 170; /** * The amount of width, which is kept in the end when performing a disappear animation (also @@ -84,6 +86,11 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView */ private static final float VERTICAL_ANIMATION_START = 1.0f; + /** + * Scale for the background to animate from when exiting dark mode. + */ + private static final float DARK_EXIT_SCALE_START = 0.93f; + private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR = new PathInterpolator(0.6f, 0, 0.5f, 1); private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR @@ -94,7 +101,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private boolean mDimmed; private boolean mDark; - private final Paint mDarkPaint = createDarkPaint(); private int mBgTint = 0; private final int mRoundedRectCornerRadius; @@ -332,40 +338,32 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView if (mDimmed != dimmed) { mDimmed = dimmed; if (fade) { - fadeBackground(); + fadeDimmedBackground(); } else { updateBackground(); } } } - public void setDark(boolean dark, boolean fade) { - // TODO implement fade - if (mDark != dark) { - mDark = dark; - if (mDark) { - setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint); + public void setDark(boolean dark, boolean fade, long delay) { + super.setDark(dark, fade, delay); + if (mDark == dark) { + return; + } + mDark = dark; + if (!dark && fade) { + if (mActivated) { + mBackgroundDimmed.setVisibility(View.VISIBLE); + mBackgroundNormal.setVisibility(View.VISIBLE); } else { - setLayerType(View.LAYER_TYPE_NONE, null); + mBackgroundDimmed.setVisibility(View.VISIBLE); + mBackgroundNormal.setVisibility(View.INVISIBLE); } + fadeDarkToDimmed(delay); + } else { + updateBackground(); } - } - - private static Paint createDarkPaint() { - final Paint p = new Paint(); - final float[] invert = { - -1f, 0f, 0f, 1f, 1f, - 0f, -1f, 0f, 1f, 1f, - 0f, 0f, -1f, 1f, 1f, - 0f, 0f, 0f, 1f, 0f - }; - final ColorMatrix m = new ColorMatrix(invert); - final ColorMatrix grayscale = new ColorMatrix(); - grayscale.setSaturation(0); - m.preConcat(grayscale); - p.setColorFilter(new ColorMatrixColorFilter(m)); - return p; - } + } public void setShowingLegacyBackground(boolean showing) { mShowingLegacyBackground = showing; @@ -402,7 +400,39 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mBackgroundNormal.setRippleColor(rippleColor); } - private void fadeBackground() { + /** + * Fades the dimmed background when exiting dark mode. + */ + private void fadeDarkToDimmed(long delay) { + mBackgroundDimmed.setAlpha(0f); + mBackgroundDimmed.setPivotX(mBackgroundDimmed.getWidth() / 2f); + mBackgroundDimmed.setPivotY(getActualHeight() / 2f); + mBackgroundDimmed.setScaleX(DARK_EXIT_SCALE_START); + mBackgroundDimmed.setScaleY(DARK_EXIT_SCALE_START); + mBackgroundDimmed.animate() + .alpha(1f) + .scaleX(1f) + .scaleY(1f) + .setDuration(DARK_ANIMATION_LENGTH) + .setStartDelay(delay) + .setInterpolator(mLinearOutSlowInInterpolator) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + // Jump state if we are cancelled + mBackgroundDimmed.setScaleX(1f); + mBackgroundDimmed.setScaleY(1f); + mBackgroundDimmed.setAlpha(1f); + } + }) + .start(); + } + + /** + * Fades the background when the dimmed state changes. + */ + private void fadeDimmedBackground() { + mBackgroundDimmed.animate().cancel(); mBackgroundNormal.animate().cancel(); if (mDimmed) { mBackgroundDimmed.setVisibility(View.VISIBLE); @@ -443,11 +473,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } private void updateBackground() { - if (mDimmed) { + cancelFadeAnimations(); + if (mDark) { + mBackgroundDimmed.setVisibility(View.INVISIBLE); + mBackgroundNormal.setVisibility(View.INVISIBLE); + } else if (mDimmed) { mBackgroundDimmed.setVisibility(View.VISIBLE); mBackgroundNormal.setVisibility(View.INVISIBLE); } else { - cancelFadeAnimations(); mBackgroundDimmed.setVisibility(View.INVISIBLE); mBackgroundNormal.setVisibility(View.VISIBLE); mBackgroundNormal.setAlpha(1f); @@ -459,6 +492,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView if (mBackgroundAnimator != null) { mBackgroundAnimator.cancel(); } + mBackgroundDimmed.animate().cancel(); mBackgroundNormal.animate().cancel(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 7345440..8ad8406 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -207,11 +207,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } @Override - public void setDark(boolean dark, boolean fade) { - super.setDark(dark, fade); + public void setDark(boolean dark, boolean fade, long delay) { + super.setDark(dark, fade, delay); final NotificationContentView showing = getShowingLayout(); if (showing != null) { - showing.setDark(dark, fade); + showing.setDark(dark, fade, delay); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index bf1e78e..ebc663c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -39,6 +39,7 @@ public abstract class ExpandableView extends FrameLayout { private int mActualHeight; protected int mClipTopAmount; private boolean mActualHeightInitialized; + private boolean mDark; private ArrayList<View> mMatchParentViews = new ArrayList<View>(); public ExpandableView(Context context, AttributeSet attrs) { @@ -185,8 +186,14 @@ public abstract class ExpandableView extends FrameLayout { * * @param dark Whether the notification should be dark. * @param fade Whether an animation should be played to change the state. + * @param delay If fading, the delay of the animation. */ - public void setDark(boolean dark, boolean fade) { + public void setDark(boolean dark, boolean fade, long delay) { + mDark = dark; + } + + public boolean isDark() { + return mDark; } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index 99214a0..27da6fd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -42,14 +42,14 @@ import com.android.systemui.R; public class NotificationContentView extends FrameLayout { private static final long ANIMATION_DURATION_LENGTH = 170; - private static final Paint INVERT_PAINT = createInvertPaint(); - private static final ColorFilter NO_COLOR_FILTER = new ColorFilter(); private final Rect mClipBounds = new Rect(); private View mContractedChild; private View mExpandedChild; + private NotificationViewWrapper mContractedWrapper; + private int mSmallHeight; private int mClipTopAmount; private int mActualHeight; @@ -84,8 +84,8 @@ public class NotificationContentView extends FrameLayout { } @Override - protected void onFinishInflate() { - super.onFinishInflate(); + protected void onAttachedToWindow() { + super.onAttachedToWindow(); updateVisibility(); } @@ -122,6 +122,7 @@ public class NotificationContentView extends FrameLayout { sanitizeContractedLayoutParams(child); addView(child); mContractedChild = child; + mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child); selectLayout(false /* animate */, true /* force */); } @@ -249,38 +250,10 @@ public class NotificationContentView extends FrameLayout { return mExpandedChild != null; } - public void setDark(boolean dark, boolean fade) { + public void setDark(boolean dark, boolean fade, long delay) { if (mDark == dark || mContractedChild == null) return; mDark = dark; - setImageViewDark(dark, fade, com.android.internal.R.id.right_icon); - setImageViewDark(dark, fade, com.android.internal.R.id.icon); - } - - private void setImageViewDark(boolean dark, boolean fade, int imageViewId) { - // TODO: implement fade - final ImageView v = (ImageView) mContractedChild.findViewById(imageViewId); - if (v == null) return; - final Drawable d = v.getBackground(); - if (dark) { - v.setLayerType(LAYER_TYPE_HARDWARE, INVERT_PAINT); - if (d != null) { - v.setTag(R.id.doze_saved_filter_tag, d.getColorFilter() != null ? d.getColorFilter() - : NO_COLOR_FILTER); - d.setColorFilter(getResources().getColor(R.color.doze_small_icon_background_color), - PorterDuff.Mode.SRC_ATOP); - v.setImageAlpha(getResources().getInteger(R.integer.doze_small_icon_alpha)); - } - } else { - v.setLayerType(LAYER_TYPE_NONE, null); - if (d != null) { - final ColorFilter filter = (ColorFilter) v.getTag(R.id.doze_saved_filter_tag); - if (filter != null) { - d.setColorFilter(filter == NO_COLOR_FILTER ? null : filter); - v.setTag(R.id.doze_saved_filter_tag, null); - } - v.setImageAlpha(0xff); - } - } + mContractedWrapper.setDark(dark, fade, delay); } @Override @@ -290,16 +263,4 @@ public class NotificationContentView extends FrameLayout { // layout, and saves us some layers. return false; } - - private static Paint createInvertPaint() { - final Paint p = new Paint(); - final float[] invert = { - -1f, 0f, 0f, 1f, 1f, - 0f, -1f, 0f, 1f, 1f, - 0f, 0f, -1f, 1f, 1f, - 0f, 0f, 0f, 1f, 0f - }; - p.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(invert))); - return p; - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationCustomViewWrapper.java new file mode 100644 index 0000000..045be3e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationCustomViewWrapper.java @@ -0,0 +1,48 @@ +/* + * 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.statusbar; + +import android.view.View; + +import com.android.systemui.ViewInvertHelper; +import com.android.systemui.statusbar.phone.NotificationPanelView; + +/** + * Wraps a notification containing a custom view. + */ +public class NotificationCustomViewWrapper extends NotificationViewWrapper { + + private final ViewInvertHelper mInvertHelper; + private boolean mDark; + + protected NotificationCustomViewWrapper(View view) { + super(view); + mInvertHelper = new ViewInvertHelper(view, NotificationPanelView.DOZE_ANIMATION_DURATION); + } + + @Override + public void setDark(boolean dark, boolean fade, long delay) { + if (mDark != dark) { + mDark = dark; + if (fade) { + mInvertHelper.fade(dark, delay); + } else { + mInvertHelper.update(dark); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java new file mode 100644 index 0000000..8f63a79 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java @@ -0,0 +1,42 @@ +/* + * 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.statusbar; + +import android.content.Context; +import android.view.View; + +/** + * Wraps a media notification. + */ +public class NotificationMediaViewWrapper extends NotificationTemplateViewWrapper { + + private boolean mDark; + + protected NotificationMediaViewWrapper(Context ctx, View view) { + super(ctx, view); + } + + @Override + public void setDark(boolean dark, boolean fade, long delay) { + if (mDark != dark) { + mDark = dark; + + // Only update the large icon, because the rest is already inverted. + setPictureGrayscale(dark, fade, delay); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java index edfd205..bfa3aa5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java @@ -21,6 +21,8 @@ import android.util.AttributeSet; import android.widget.TextView; import com.android.systemui.R; +import com.android.systemui.ViewInvertHelper; +import com.android.systemui.statusbar.phone.NotificationPanelView; /** * Container view for overflowing notification icons on Keyguard. @@ -28,6 +30,8 @@ import com.android.systemui.R; public class NotificationOverflowContainer extends ActivatableNotificationView { private NotificationOverflowIconsView mIconsView; + private ViewInvertHelper mViewInvertHelper; + private boolean mDark; public NotificationOverflowContainer(Context context, AttributeSet attrs) { super(context, attrs); @@ -39,6 +43,20 @@ public class NotificationOverflowContainer extends ActivatableNotificationView { mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view); mIconsView.setMoreText((TextView) findViewById(R.id.more_text)); mIconsView.setOverflowIndicator(findViewById(R.id.more_icon_overflow)); + mViewInvertHelper = new ViewInvertHelper(findViewById(R.id.content), + NotificationPanelView.DOZE_ANIMATION_DURATION); + } + + @Override + public void setDark(boolean dark, boolean fade, long delay) { + super.setDark(dark, fade, delay); + if (mDark == dark) return; + mDark = dark; + if (fade) { + mViewInvertHelper.fade(dark, delay); + } else { + mViewInvertHelper.update(dark); + } } public NotificationOverflowIconsView getIconsView() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java new file mode 100644 index 0000000..8dc14b0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java @@ -0,0 +1,223 @@ +/* + * 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.statusbar; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.view.View; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; +import android.widget.ImageView; + +import com.android.systemui.R; +import com.android.systemui.ViewInvertHelper; +import com.android.systemui.statusbar.phone.NotificationPanelView; + +/** + * Wraps a notification view inflated from a template. + */ +public class NotificationTemplateViewWrapper extends NotificationViewWrapper { + + private final ViewInvertHelper mInvertHelper; + private final ImageView mIcon; + protected final ImageView mPicture; + private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix(); + private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter( + 0, PorterDuff.Mode.SRC_ATOP); + private final int mIconDarkAlpha; + private final int mIconBackgroundColor; + private final int mIconBackgroundDarkColor; + private final Interpolator mLinearOutSlowInInterpolator; + + private boolean mDark; + + protected NotificationTemplateViewWrapper(Context ctx, View view) { + super(view); + mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha); + mIconBackgroundDarkColor = + ctx.getResources().getColor(R.color.doze_small_icon_background_color); + mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx, + android.R.interpolator.linear_out_slow_in); + View mainColumn = view.findViewById(com.android.internal.R.id.notification_main_column); + mInvertHelper = mainColumn != null + ? new ViewInvertHelper(mainColumn, NotificationPanelView.DOZE_ANIMATION_DURATION) + : null; + ImageView largeIcon = (ImageView) view.findViewById(com.android.internal.R.id.icon); + ImageView rightIcon = (ImageView) view.findViewById(com.android.internal.R.id.right_icon); + mIcon = resolveIcon(largeIcon, rightIcon); + mPicture = resolvePicture(largeIcon); + mIconBackgroundColor = resolveBackgroundColor(mIcon); + } + + private ImageView resolveIcon(ImageView largeIcon, ImageView rightIcon) { + return largeIcon != null && largeIcon.getBackground() != null ? largeIcon + : rightIcon != null && rightIcon.getBackground() != null ? rightIcon + : null; + } + + private ImageView resolvePicture(ImageView largeIcon) { + return largeIcon != null && largeIcon.getBackground() == null + ? largeIcon + : null; + } + + private int resolveBackgroundColor(ImageView icon) { + if (icon != null && icon.getBackground() != null) { + ColorFilter filter = icon.getBackground().getColorFilter(); + if (filter instanceof PorterDuffColorFilter) { + return ((PorterDuffColorFilter) filter).getColor(); + } + } + return 0; + } + + @Override + public void setDark(boolean dark, boolean fade, long delay) { + if (mDark != dark) { + mDark = dark; + if (mInvertHelper != null) { + if (fade) { + mInvertHelper.fade(dark, delay); + } else { + mInvertHelper.update(dark); + } + } + if (mIcon != null) { + if (fade) { + fadeIconColorFilter(mIcon, dark, delay); + fadeIconAlpha(mIcon, dark, delay); + } else { + updateIconColorFilter(mIcon, dark); + updateIconAlpha(mIcon, dark); + } + } + setPictureGrayscale(dark, fade, delay); + } + } + + protected void setPictureGrayscale(boolean grayscale, boolean fade, long delay) { + if (mPicture != null) { + if (fade) { + fadeGrayscale(mPicture, grayscale, delay); + } else { + updateGrayscale(mPicture, grayscale); + } + } + } + + private void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener, + boolean dark, long delay, Animator.AnimatorListener listener) { + float startIntensity = dark ? 0f : 1f; + float endIntensity = dark ? 1f : 0f; + ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity); + animator.addUpdateListener(updateListener); + animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION); + animator.setInterpolator(mLinearOutSlowInInterpolator); + animator.setStartDelay(delay); + if (listener != null) { + animator.addListener(listener); + } + animator.start(); + } + + private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) { + startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + updateIconColorFilter(target, (Float) animation.getAnimatedValue()); + } + }, dark, delay, null /* listener */); + } + + private void fadeIconAlpha(final ImageView target, boolean dark, long delay) { + startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float t = (float) animation.getAnimatedValue(); + target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t)); + } + }, dark, delay, null /* listener */); + } + + protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) { + startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + updateGrayscaleMatrix((float) animation.getAnimatedValue()); + target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix)); + } + }, dark, delay, new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (!dark) { + target.setColorFilter(null); + } + } + }); + } + + private void updateIconColorFilter(ImageView target, boolean dark) { + updateIconColorFilter(target, dark ? 1f : 0f); + } + + private void updateIconColorFilter(ImageView target, float intensity) { + int color = interpolateColor(mIconBackgroundColor, mIconBackgroundDarkColor, intensity); + mIconColorFilter.setColor(color); + target.getBackground().mutate().setColorFilter(mIconColorFilter); + } + + private void updateIconAlpha(ImageView target, boolean dark) { + target.setImageAlpha(dark ? mIconDarkAlpha : 255); + } + + protected void updateGrayscale(ImageView target, boolean dark) { + if (dark) { + updateGrayscaleMatrix(1f); + target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix)); + } else { + target.setColorFilter(null); + } + } + + private void updateGrayscaleMatrix(float intensity) { + mGrayscaleColorMatrix.setSaturation(1 - intensity); + } + + private static int interpolateColor(int source, int target, float t) { + int aSource = Color.alpha(source); + int rSource = Color.red(source); + int gSource = Color.green(source); + int bSource = Color.blue(source); + int aTarget = Color.alpha(target); + int rTarget = Color.red(target); + int gTarget = Color.green(target); + int bTarget = Color.blue(target); + return Color.argb( + (int) (aSource * (1f - t) + aTarget * t), + (int) (rSource * (1f - t) + rTarget * t), + (int) (gSource * (1f - t) + gTarget * t), + (int) (bSource * (1f - t) + bTarget * t)); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java new file mode 100644 index 0000000..0a02573 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java @@ -0,0 +1,56 @@ +/* + * 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.statusbar; + +import android.content.Context; +import android.view.View; + +import com.android.internal.R; + +/** + * Wraps the actual notification content view; used to implement behaviors which are different for + * the individual templates and custom views. + */ +public abstract class NotificationViewWrapper { + + protected final View mView; + + public static NotificationViewWrapper wrap(Context ctx, View v) { + + // TODO: Figure out a better way to find out which template the view is. + if (v.findViewById(com.android.internal.R.id.media_actions) != null) { + return new NotificationMediaViewWrapper(ctx, v); + } else if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) { + return new NotificationTemplateViewWrapper(ctx, v); + } else { + return new NotificationCustomViewWrapper(v); + } + } + + protected NotificationViewWrapper(View view) { + mView = view; + } + + /** + * In dark mode, we draw as little as possible, assuming a black background. + * + * @param dark whether we should display ourselves in dark mode + * @param fade whether to animate the transition if the mode changes + * @param delay if fading, the delay of the animation + */ + public abstract void setDark(boolean dark, boolean fade, long delay); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java index 9154a48..418c57f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java @@ -48,6 +48,7 @@ public class SignalClusterView private int mMobileStrengthId = 0, mMobileTypeId = 0; private boolean mIsAirplaneMode = false; private int mAirplaneIconId = 0; + private int mAirplaneContentDescription; private String mWifiDescription, mMobileDescription, mMobileTypeDescription; private boolean mIsMobileTypeIconWide; @@ -160,9 +161,10 @@ public class SignalClusterView } @Override - public void setIsAirplaneMode(boolean is, int airplaneIconId) { + public void setIsAirplaneMode(boolean is, int airplaneIconId, int contentDescription) { mIsAirplaneMode = is; mAirplaneIconId = airplaneIconId; + mAirplaneContentDescription = contentDescription; apply(); } @@ -236,6 +238,8 @@ public class SignalClusterView if (mIsAirplaneMode) { mAirplane.setImageResource(mAirplaneIconId); + mAirplane.setContentDescription(mAirplaneContentDescription != 0 ? + mContext.getString(mAirplaneContentDescription) : ""); mAirplane.setVisibility(View.VISIBLE); } else { mAirplane.setVisibility(View.GONE); 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 f5c994a..3fca56d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -79,6 +79,7 @@ import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; +import android.text.TextUtils; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.EventLog; @@ -821,7 +822,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, signalClusterQs.setNetworkController(mNetworkController); final boolean isAPhone = mNetworkController.hasVoiceCallingFeature(); if (isAPhone) { - mNetworkController.addEmergencyLabelView(mHeader); + mNetworkController.addEmergencyListener(new NetworkControllerImpl.EmergencyListener() { + @Override + public void setEmergencyCallsOnly(boolean emergencyOnly) { + mHeader.setShowEmergencyCallsOnly(emergencyOnly); + } + }); } mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label); @@ -830,13 +836,19 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (mShowCarrierInPanel) { mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE); - // for mobile devices, we always show mobile connection info here (SPN/PLMN) - // for other devices, we show whatever network is connected - if (mNetworkController.hasMobileDataFeature()) { - mNetworkController.addMobileLabelView(mCarrierLabel); - } else { - mNetworkController.addCombinedLabelView(mCarrierLabel); - } + mNetworkController.addCarrierLabel(new NetworkControllerImpl.CarrierLabelListener() { + @Override + public void setCarrierLabel(String label) { + mCarrierLabel.setText(label); + if (mNetworkController.hasMobileDataFeature()) { + if (TextUtils.isEmpty(label)) { + mCarrierLabel.setVisibility(View.GONE); + } else { + mCarrierLabel.setVisibility(View.VISIBLE); + } + } + } + }); // set up the dynamic hide/show of the label // TODO: uncomment, handle this for the Stack scroller aswell @@ -2669,6 +2681,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override public void setInteracting(int barWindow, boolean interacting) { + final boolean changing = ((mInteractingWindows & barWindow) != 0) != interacting; mInteractingWindows = interacting ? (mInteractingWindows | barWindow) : (mInteractingWindows & ~barWindow); @@ -2677,6 +2690,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } else { resumeSuspendedAutohide(); } + // manually dismiss the volume panel when interacting with the nav bar + if (changing && interacting && barWindow == StatusBarManager.WINDOW_NAVIGATION_BAR) { + if (mVolumeComponent != null) { + mVolumeComponent.dismissNow(); + } + } checkBarModes(); } @@ -3689,14 +3708,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (mState != StatusBarState.KEYGUARD && !mNotificationPanel.isDozing()) { return; } - mNotificationPanel.setDozing(mDozing, mDozeScrimController.isPulsing() /*animate*/); - if (mDozing) { - mStackScroller.setDark(true, false /*animate*/); - } else { - mStackScroller.setDark(false, false /*animate*/); - } + boolean animate = !mDozing && mDozeScrimController.isPulsing(); + mNotificationPanel.setDozing(mDozing, animate); + mStackScroller.setDark(mDozing, animate); mScrimController.setDozing(mDozing); - mDozeScrimController.setDozing(mDozing, mDozeScrimController.isPulsing() /* animate */); + mDozeScrimController.setDozing(mDozing, animate); } public void updateStackScrollerState(boolean goingToFullShade) { 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 45a1386..8ce608c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -122,7 +122,7 @@ public class QSTileHost implements QSTile.Host { tile.userSwitch(newUserId); } mSecurity.onUserSwitched(newUserId); - mNetwork.onUserSwitched(newUserId); + mNetwork.getAccessPointController().onUserSwitched(newUserId); mObserver.register(); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java index 0a385d7..6fec97e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java @@ -36,15 +36,13 @@ import android.util.ArraySet; import android.util.Log; import com.android.systemui.R; -import com.android.systemui.statusbar.policy.NetworkController.AccessPoint; -import com.android.systemui.statusbar.policy.NetworkController.AccessPointCallback; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; -public class AccessPointController { +public class AccessPointControllerImpl implements NetworkController.AccessPointController { private static final String TAG = "AccessPointController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -69,7 +67,7 @@ public class AccessPointController { private boolean mScanning; private int mCurrentUser; - public AccessPointController(Context context) { + public AccessPointControllerImpl(Context context) { mContext = context; mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); @@ -81,25 +79,28 @@ public class AccessPointController { new UserHandle(mCurrentUser)); } - void onUserSwitched(int newUserId) { + public void onUserSwitched(int newUserId) { mCurrentUser = newUserId; } - public void addCallback(AccessPointCallback callback) { + @Override + public void addAccessPointCallback(AccessPointCallback callback) { if (callback == null || mCallbacks.contains(callback)) return; if (DEBUG) Log.d(TAG, "addCallback " + callback); mCallbacks.add(callback); mReceiver.setListening(!mCallbacks.isEmpty()); } - public void removeCallback(AccessPointCallback callback) { + @Override + public void removeAccessPointCallback(AccessPointCallback callback) { if (callback == null) return; if (DEBUG) Log.d(TAG, "removeCallback " + callback); mCallbacks.remove(callback); mReceiver.setListening(!mCallbacks.isEmpty()); } - public void scan() { + @Override + public void scanForAccessPoints() { if (mScanning) return; if (DEBUG) Log.d(TAG, "scan!"); mScanning = mWifiManager.startScan(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java index 7ac2a98..b7c74e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java @@ -33,11 +33,5 @@ public class AccessibilityContentDescriptions { R.string.accessibility_wifi_three_bars, R.string.accessibility_wifi_signal_full }; - static final int[] WIMAX_CONNECTION_STRENGTH = { - R.string.accessibility_no_wimax, - R.string.accessibility_wimax_one_bar, - R.string.accessibility_wimax_two_bars, - R.string.accessibility_wimax_three_bars, - R.string.accessibility_wimax_signal_full - }; + static final int WIFI_NO_CONNECTION = R.string.accessibility_no_wifi; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java index 33d68bf..20f0a83 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java @@ -38,12 +38,10 @@ import android.text.format.DateUtils; import android.text.format.Time; import android.util.Log; -import com.android.systemui.statusbar.policy.NetworkController.DataUsageInfo; - import java.util.Date; import java.util.Locale; -public class MobileDataController { +public class MobileDataControllerImpl implements NetworkController.MobileDataController { private static final String TAG = "MobileDataController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -61,8 +59,9 @@ public class MobileDataController { private INetworkStatsSession mSession; private Callback mCallback; + private NetworkControllerImpl mNetworkController; - public MobileDataController(Context context) { + public MobileDataControllerImpl(Context context) { mContext = context; mTelephonyManager = TelephonyManager.from(context); mConnectivityManager = ConnectivityManager.from(context); @@ -71,6 +70,10 @@ public class MobileDataController { mPolicyManager = NetworkPolicyManager.from(mContext); } + public void setNetworkController(NetworkControllerImpl networkController) { + mNetworkController = networkController; + } + private INetworkStatsSession getSession() { if (mSession == null) { try { @@ -155,6 +158,9 @@ public class MobileDataController { } else { usage.warningLevel = DEFAULT_WARNING_LEVEL; } + if (usage != null) { + usage.carrier = mNetworkController.getMobileNetworkName(); + } return usage; } catch (RemoteException e) { return warn("remote call failed"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index bb29d01..b024f58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -22,6 +22,8 @@ public interface NetworkController { void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb); void removeNetworkSignalChangedCallback(NetworkSignalChangedCallback cb); void setWifiEnabled(boolean enabled); + AccessPointController getAccessPointController(); + MobileDataController getMobileDataController(); public interface NetworkSignalChangedCallback { void onWifiSignalChanged(boolean enabled, boolean connected, int wifiSignalIconId, @@ -36,38 +38,50 @@ public interface NetworkController { void onMobileDataEnabled(boolean enabled); } - void addAccessPointCallback(AccessPointCallback callback); - void removeAccessPointCallback(AccessPointCallback callback); - void scanForAccessPoints(); - boolean connect(AccessPoint ap); - boolean isMobileDataSupported(); - boolean isMobileDataEnabled(); - void setMobileDataEnabled(boolean enabled); - DataUsageInfo getDataUsageInfo(); - boolean canConfigWifi(); - void onUserSwitched(int newUserId); + /** + * Tracks changes in access points. Allows listening for changes, scanning for new APs, + * and connecting to new ones. + */ + public interface AccessPointController { + void addAccessPointCallback(AccessPointCallback callback); + void removeAccessPointCallback(AccessPointCallback callback); + void scanForAccessPoints(); + boolean connect(AccessPoint ap); + boolean canConfigWifi(); + void onUserSwitched(int newUserId); - public interface AccessPointCallback { - void onAccessPointsChanged(AccessPoint[] accessPoints); - } + public interface AccessPointCallback { + void onAccessPointsChanged(AccessPoint[] accessPoints); + } - public static class AccessPoint { - public static final int NO_NETWORK = -1; // see WifiManager + public static class AccessPoint { + public static final int NO_NETWORK = -1; // see WifiManager - public int networkId; - public int iconId; - public String ssid; - public boolean isConnected; - public boolean isConfigured; - public boolean hasSecurity; - public int level; // 0 - 5 + public int networkId; + public int iconId; + public String ssid; + public boolean isConnected; + public boolean isConfigured; + public boolean hasSecurity; + public int level; // 0 - 5 + } } - public static class DataUsageInfo { - public String carrier; - public String period; - public long limitLevel; - public long warningLevel; - public long usageLevel; + /** + * Tracks mobile data support and usage. + */ + public interface MobileDataController { + boolean isMobileDataSupported(); + boolean isMobileDataEnabled(); + void setMobileDataEnabled(boolean enabled); + DataUsageInfo getDataUsageInfo(); + + public static class DataUsageInfo { + public String carrier; + public String period; + public long limitLevel; + public long warningLevel; + public long usageLevel; + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 0caded5..5a97c75 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -26,7 +26,6 @@ import android.net.NetworkInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import android.net.wimax.WimaxManagerConstants; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; @@ -37,9 +36,8 @@ import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; +import android.text.format.DateFormat; import android.util.Log; -import android.view.View; -import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.IccCardConstants; @@ -48,130 +46,66 @@ import com.android.internal.telephony.cdma.EriInfo; import com.android.internal.util.AsyncChannel; import com.android.systemui.DemoMode; import com.android.systemui.R; -import com.android.systemui.statusbar.phone.StatusBarHeaderView; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Objects; /** Platform implementation of the network controller. **/ public class NetworkControllerImpl extends BroadcastReceiver implements NetworkController, DemoMode { // debug - static final String TAG = "StatusBar.NetworkController"; - static final boolean DEBUG = false; - static final boolean CHATTY = false; // additional diagnostics, but not logspew - - // telephony - boolean mHspaDataDistinguishable; - final TelephonyManager mPhone; - boolean mDataConnected; - IccCardConstants.State mSimState = IccCardConstants.State.READY; - int mPhoneState = TelephonyManager.CALL_STATE_IDLE; - int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN; - int mDataState = TelephonyManager.DATA_DISCONNECTED; - int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE; - ServiceState mServiceState; - SignalStrength mSignalStrength; - int[] mDataIconList = TelephonyIcons.DATA_G[0]; - String mNetworkName; - String mNetworkNameDefault; - String mNetworkNameSeparator; - int mPhoneSignalIconId; - int mQSPhoneSignalIconId; - int mDataDirectionIconId; // data + data direction on phones - int mDataSignalIconId; - int mDataTypeIconId; - int mQSDataTypeIconId; - int mAirplaneIconId; - boolean mDataActive; - boolean mNoSim; - int mLastSignalLevel; - boolean mShowPhoneRSSIForData = false; - boolean mShowAtLeastThreeGees = false; - boolean mAlwaysShowCdmaRssi = false; - - String mContentDescriptionPhoneSignal; - String mContentDescriptionWifi; - String mContentDescriptionWimax; - String mContentDescriptionCombinedSignal; - String mContentDescriptionDataType; - - // wifi - final WifiManager mWifiManager; - AsyncChannel mWifiChannel; - boolean mWifiEnabled, mWifiConnected; - int mWifiRssi, mWifiLevel; - String mWifiSsid; - int mWifiIconId = 0; - int mQSWifiIconId = 0; - int mWifiActivity = WifiManager.DATA_ACTIVITY_NONE; + static final String TAG = "NetworkController"; + static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + // additional diagnostics, but not logspew + static final boolean CHATTY = Log.isLoggable(TAG + ".Chat", Log.DEBUG); + // Save the previous states of all SignalController state info. + static final boolean RECORD_HISTORY = true; + // How many to save, must be a power of 2. + static final int HISTORY_SIZE = 16; + + private static final int INET_CONDITION_THRESHOLD = 50; + + private final Context mContext; + private final TelephonyManager mPhone; + private final WifiManager mWifiManager; + private final ConnectivityManager mConnectivityManager; + private final boolean mHasMobileDataFeature; + + // Subcontrollers. + @VisibleForTesting + final WifiSignalController mWifiSignalController; + @VisibleForTesting + final MobileSignalController mMobileSignalController; + private final AccessPointController mAccessPoints; + private final MobileDataControllerImpl mMobileDataController; // bluetooth private boolean mBluetoothTethered = false; - private int mBluetoothTetherIconId = - com.android.internal.R.drawable.stat_sys_tether_bluetooth; - - //wimax - private boolean mWimaxSupported = false; - private boolean mIsWimaxEnabled = false; - private boolean mWimaxConnected = false; - private boolean mWimaxIdle = false; - private int mWimaxIconId = 0; - private int mWimaxSignal = 0; - private int mWimaxState = 0; - private int mWimaxExtraState = 0; // data connectivity (regardless of state, can we access the internet?) // state of inet connection - 0 not connected, 100 connected private boolean mConnected = false; private int mConnectedNetworkType = ConnectivityManager.TYPE_NONE; private String mConnectedNetworkTypeName; - private int mLastConnectedNetworkType = ConnectivityManager.TYPE_NONE; - - private int mInetCondition = 0; - private int mLastInetCondition = 0; - private static final int INET_CONDITION_THRESHOLD = 50; + private boolean mInetCondition; // Used for Logging and demo. + // States that don't belong to a subcontroller. private boolean mAirplaneMode = false; - private boolean mLastAirplaneMode = true; - private Locale mLocale = null; - private Locale mLastLocale = null; - - // our ui - Context mContext; - ArrayList<TextView> mCombinedLabelViews = new ArrayList<TextView>(); - ArrayList<TextView> mMobileLabelViews = new ArrayList<TextView>(); - ArrayList<TextView> mWifiLabelViews = new ArrayList<TextView>(); - ArrayList<StatusBarHeaderView> mEmergencyViews = new ArrayList<>(); - ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>(); - ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks = - new ArrayList<NetworkSignalChangedCallback>(); - int mLastPhoneSignalIconId = -1; - int mLastDataDirectionIconId = -1; - int mLastWifiIconId = -1; - int mLastWimaxIconId = -1; - int mLastCombinedSignalIconId = -1; - int mLastDataTypeIconId = -1; - String mLastCombinedLabel = ""; - private boolean mHasMobileDataFeature; - - boolean mDataAndWifiStacked = false; - - public interface SignalCluster { - void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription); - void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon, - String contentDescription, String typeContentDescription, boolean isTypeIconWide); - void setIsAirplaneMode(boolean is, int airplaneIcon); - } - - private final AccessPointController mAccessPoints; - private final MobileDataController mMobileDataController; - private final ConnectivityManager mConnectivityManager; + // All the callbacks. + private ArrayList<EmergencyListener> mEmergencyListeners = new ArrayList<EmergencyListener>(); + private ArrayList<CarrierLabelListener> mCarrierListeners = + new ArrayList<CarrierLabelListener>(); + private ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>(); + private ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks = + new ArrayList<NetworkSignalChangedCallback>(); /** * Construct this controller object and register for updates. @@ -179,68 +113,51 @@ public class NetworkControllerImpl extends BroadcastReceiver public NetworkControllerImpl(Context context) { this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE), (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE), - (WifiManager) context.getSystemService(Context.WIFI_SERVICE)); + (WifiManager) context.getSystemService(Context.WIFI_SERVICE), + Config.readConfig(context), new AccessPointControllerImpl(context), + new MobileDataControllerImpl(context)); registerListeners(); } @VisibleForTesting NetworkControllerImpl(Context context, ConnectivityManager connectivityManager, - TelephonyManager telephonyManager, WifiManager wifiManager) { + TelephonyManager telephonyManager, WifiManager wifiManager, Config config, + AccessPointControllerImpl accessPointController, + MobileDataControllerImpl mobileDataController) { mContext = context; - final Resources res = context.getResources(); mConnectivityManager = connectivityManager; mHasMobileDataFeature = mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); - mShowPhoneRSSIForData = res.getBoolean(R.bool.config_showPhoneRSSIForData); - mShowAtLeastThreeGees = res.getBoolean(R.bool.config_showMin3G); - mAlwaysShowCdmaRssi = res.getBoolean( - com.android.internal.R.bool.config_alwaysUseCdmaRssi); - - // set up the default wifi icon, used when no radios have ever appeared - updateWifiIcons(); - updateWimaxIcons(); - // telephony mPhone = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); - mHspaDataDistinguishable = mContext.getResources().getBoolean( - R.bool.config_hspa_data_distinguishable); - mNetworkNameSeparator = mContext.getString(R.string.status_bar_network_name_separator); - mNetworkNameDefault = mContext.getString( - com.android.internal.R.string.lockscreen_carrier_default); - mNetworkName = mNetworkNameDefault; // wifi mWifiManager = wifiManager; - Handler handler = new WifiHandler(); - mWifiChannel = new AsyncChannel(); - Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger(); - if (wifiMessenger != null) { - mWifiChannel.connect(mContext, handler, wifiMessenger); - } - - // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it - updateAirplaneMode(); - mLastLocale = mContext.getResources().getConfiguration().locale; - mAccessPoints = new AccessPointController(mContext); - mMobileDataController = new MobileDataController(mContext); - mMobileDataController.setCallback(new MobileDataController.Callback() { + mLocale = mContext.getResources().getConfiguration().locale; + mAccessPoints = accessPointController; + mMobileDataController = mobileDataController; + mMobileDataController.setNetworkController(this); + // TODO: Find a way to move this into MobileDataController. + mMobileDataController.setCallback(new MobileDataControllerImpl.Callback() { @Override public void onMobileDataEnabled(boolean enabled) { notifyMobileDataEnabled(enabled); } }); + mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature, + mSignalsChangedCallbacks, mSignalClusters, this); + mMobileSignalController = new MobileSignalController(mContext, config, + mHasMobileDataFeature, mPhone, mSignalsChangedCallbacks, mSignalClusters, this); + + // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it + updateAirplaneMode(true); } private void registerListeners() { - mPhone.listen(mPhoneStateListener, - PhoneStateListener.LISTEN_SERVICE_STATE - | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS - | PhoneStateListener.LISTEN_CALL_STATE - | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE - | PhoneStateListener.LISTEN_DATA_ACTIVITY); + mMobileSignalController.registerListener(); // broadcasts IntentFilter filter = new IntentFilter(); @@ -253,29 +170,38 @@ public class NetworkControllerImpl extends BroadcastReceiver filter.addAction(ConnectivityManager.INET_CONDITION_ACTION); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); - mWimaxSupported = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_wimaxEnabled); - if(mWimaxSupported) { - filter.addAction(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION); - filter.addAction(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION); - filter.addAction(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION); - } mContext.registerReceiver(this, filter); } + private void unregisterListeners() { + mMobileSignalController.unregisterListener(); + mContext.unregisterReceiver(this); + } + @Override - public boolean canConfigWifi() { - return mAccessPoints.canConfigWifi(); + public AccessPointController getAccessPointController() { + return mAccessPoints; } @Override - public void onUserSwitched(int newUserId) { - mAccessPoints.onUserSwitched(newUserId); + public MobileDataController getMobileDataController() { + return mMobileDataController; + } + + public void addEmergencyListener(EmergencyListener listener) { + mEmergencyListeners.add(listener); + refreshCarrierLabel(); + } + + public void addCarrierLabel(CarrierLabelListener listener) { + mCarrierListeners.add(listener); + refreshCarrierLabel(); } private void notifyMobileDataEnabled(boolean enabled) { - for (NetworkSignalChangedCallback cb : mSignalsChangedCallbacks) { - cb.onMobileDataEnabled(enabled); + int length = mSignalsChangedCallbacks.size(); + for (int i = 0; i < length; i++) { + mSignalsChangedCallbacks.get(i).onMobileDataEnabled(enabled); } } @@ -287,34 +213,40 @@ public class NetworkControllerImpl extends BroadcastReceiver return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE; } - public boolean isEmergencyOnly() { - return (mServiceState != null && mServiceState.isEmergencyOnly()); - } - - public void addCombinedLabelView(TextView v) { - mCombinedLabelViews.add(v); + public String getMobileNetworkName() { + return mMobileSignalController.mCurrentState.networkName; } - public void addMobileLabelView(TextView v) { - mMobileLabelViews.add(v); + public boolean isEmergencyOnly() { + return mMobileSignalController.isEmergencyOnly(); } - public void addWifiLabelView(TextView v) { - mWifiLabelViews.add(v); - } + /** + * Emergency status may have changed (triggered by MobileSignalController), + * so we should recheck and send out the state to listeners. + */ + void recalculateEmergency() { + final boolean emergencyOnly = isEmergencyOnly(); - public void addEmergencyLabelView(StatusBarHeaderView v) { - mEmergencyViews.add(v); + int length = mEmergencyListeners.size(); + for (int i = 0; i < length; i++) { + mEmergencyListeners.get(i).setEmergencyCallsOnly(emergencyOnly); + } } public void addSignalCluster(SignalCluster cluster) { mSignalClusters.add(cluster); - refreshSignalCluster(cluster); + cluster.setIsAirplaneMode(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON, + R.string.accessibility_airplane_mode); + mWifiSignalController.notifyListeners(); + mMobileSignalController.notifyListeners(); } public void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) { mSignalsChangedCallbacks.add(cb); - notifySignalsChangedCallbacks(cb); + cb.onAirplaneModeChanged(mAirplaneMode); + mWifiSignalController.notifyListeners(); + mMobileSignalController.notifyListeners(); } public void removeNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) { @@ -322,26 +254,6 @@ public class NetworkControllerImpl extends BroadcastReceiver } @Override - public void addAccessPointCallback(AccessPointCallback callback) { - mAccessPoints.addCallback(callback); - } - - @Override - public void removeAccessPointCallback(AccessPointCallback callback) { - mAccessPoints.removeCallback(callback); - } - - @Override - public void scanForAccessPoints() { - mAccessPoints.scan(); - } - - @Override - public boolean connect(AccessPoint ap) { - return mAccessPoints.connect(ap); - } - - @Override public void setWifiEnabled(final boolean enabled) { new AsyncTask<Void, Void, Void>() { @Override @@ -349,7 +261,7 @@ public class NetworkControllerImpl extends BroadcastReceiver // Disable tethering if enabling Wifi final int wifiApState = mWifiManager.getWifiApState(); if (enabled && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) || - (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) { + (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) { mWifiManager.setWifiApEnabled(null, false); } @@ -360,1275 +272,1250 @@ public class NetworkControllerImpl extends BroadcastReceiver } @Override - public DataUsageInfo getDataUsageInfo() { - final DataUsageInfo info = mMobileDataController.getDataUsageInfo(); - if (info != null) { - info.carrier = mNetworkName; + public void onReceive(Context context, Intent intent) { + if (CHATTY) { + Log.d(TAG, "onReceive: intent=" + intent); } - return info; + final String action = intent.getAction(); + if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE) || + action.equals(ConnectivityManager.INET_CONDITION_ACTION)) { + updateConnectivity(intent); + refreshCarrierLabel(); + } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { + refreshLocale(); + refreshCarrierLabel(); + } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { + refreshLocale(); + updateAirplaneMode(false); + refreshCarrierLabel(); + } + mWifiSignalController.handleBroadcast(intent); + mMobileSignalController.handleBroadcast(intent); } - @Override - public boolean isMobileDataSupported() { - return mMobileDataController.isMobileDataSupported(); + private void updateAirplaneMode(boolean force) { + boolean airplaneMode = (Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) == 1); + if (airplaneMode != mAirplaneMode || force) { + mAirplaneMode = airplaneMode; + mMobileSignalController.setAirplaneMode(mAirplaneMode); + notifyAirplaneCallbacks(); + refreshCarrierLabel(); + } } - @Override - public boolean isMobileDataEnabled() { - return mMobileDataController.isMobileDataEnabled(); + private void refreshLocale() { + Locale current = mContext.getResources().getConfiguration().locale; + if (current.equals(mLocale)) { + mLocale = current; + notifyAllListeners(); + } } - @Override - public void setMobileDataEnabled(boolean enabled) { - mMobileDataController.setMobileDataEnabled(enabled); + /** + * Turns inet condition into a boolean indexing for a specific network. + * returns 0 for bad connectivity on this network. + * returns 1 for good connectivity on this network. + */ + private int inetConditionForNetwork(int networkType, boolean inetCondition) { + return (inetCondition && mConnectedNetworkType == networkType) ? 1 : 0; } - private boolean isTypeIconWide(int iconId) { - return TelephonyIcons.ICON_LTE == iconId || TelephonyIcons.ICON_1X == iconId - || TelephonyIcons.ICON_3G == iconId || TelephonyIcons.ICON_4G == iconId; + private void notifyAllListeners() { + // Something changed, trigger everything! + notifyAirplaneCallbacks(); + mMobileSignalController.notifyListeners(); + mWifiSignalController.notifyListeners(); } - private boolean isQsTypeIconWide(int iconId) { - return TelephonyIcons.QS_ICON_LTE == iconId || TelephonyIcons.QS_ICON_1X == iconId - || TelephonyIcons.QS_ICON_3G == iconId || TelephonyIcons.QS_ICON_4G == iconId; + private void notifyAirplaneCallbacks() { + int length = mSignalClusters.size(); + for (int i = 0; i < length; i++) { + mSignalClusters.get(i).setIsAirplaneMode(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON, + R.string.accessibility_airplane_mode); + } + // update QS + int signalsChangedLength = mSignalsChangedCallbacks.size(); + for (int i = 0; i < signalsChangedLength; i++) { + mSignalsChangedCallbacks.get(i).onAirplaneModeChanged(mAirplaneMode); + } } - public void refreshSignalCluster(SignalCluster cluster) { - if (mDemoMode) return; - cluster.setWifiIndicators( - // only show wifi in the cluster if connected or if wifi-only - mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature), - mWifiIconId, - mContentDescriptionWifi); - - if (mIsWimaxEnabled && mWimaxConnected) { - // wimax is special - cluster.setMobileDataIndicators( - true, - mAlwaysShowCdmaRssi ? mPhoneSignalIconId : mWimaxIconId, - mDataTypeIconId, - mContentDescriptionWimax, - mContentDescriptionDataType, - false /* isTypeIconWide */ ); - } else { - // normal mobile data - cluster.setMobileDataIndicators( - mHasMobileDataFeature, - mShowPhoneRSSIForData ? mPhoneSignalIconId : mDataSignalIconId, - mDataTypeIconId, - mContentDescriptionPhoneSignal, - mContentDescriptionDataType, - isTypeIconWide(mDataTypeIconId)); - } - cluster.setIsAirplaneMode(mAirplaneMode, mAirplaneIconId); - } + /** + * Update the Inet conditions and what network we are connected to. + */ + private void updateConnectivity(Intent intent) { + final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); - void notifySignalsChangedCallbacks(NetworkSignalChangedCallback cb) { - // only show wifi in the cluster if connected or if wifi-only - boolean wifiEnabled = mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature); - String wifiDesc = wifiEnabled ? - mWifiSsid : null; - boolean wifiIn = wifiEnabled && mWifiSsid != null - && (mWifiActivity == WifiManager.DATA_ACTIVITY_INOUT - || mWifiActivity == WifiManager.DATA_ACTIVITY_IN); - boolean wifiOut = wifiEnabled && mWifiSsid != null - && (mWifiActivity == WifiManager.DATA_ACTIVITY_INOUT - || mWifiActivity == WifiManager.DATA_ACTIVITY_OUT); - cb.onWifiSignalChanged(mWifiEnabled, mWifiConnected, mQSWifiIconId, wifiIn, wifiOut, - mContentDescriptionWifi, wifiDesc); - - boolean mobileIn = mDataConnected && (mDataActivity == TelephonyManager.DATA_ACTIVITY_INOUT - || mDataActivity == TelephonyManager.DATA_ACTIVITY_IN); - boolean mobileOut = mDataConnected && (mDataActivity == TelephonyManager.DATA_ACTIVITY_INOUT - || mDataActivity == TelephonyManager.DATA_ACTIVITY_OUT); - if (isEmergencyOnly()) { - cb.onMobileDataSignalChanged(false, mQSPhoneSignalIconId, - mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut, - mContentDescriptionDataType, null, mNoSim, isQsTypeIconWide(mQSDataTypeIconId)); + // Are we connected at all, by any interface? + mConnected = info != null && info.isConnected(); + if (mConnected) { + mConnectedNetworkType = info.getType(); + mConnectedNetworkTypeName = info.getTypeName(); } else { - if (mIsWimaxEnabled && mWimaxConnected) { - // Wimax is special - cb.onMobileDataSignalChanged(true, mQSPhoneSignalIconId, - mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut, - mContentDescriptionDataType, mNetworkName, mNoSim, - isQsTypeIconWide(mQSDataTypeIconId)); - } else { - // Normal mobile data - cb.onMobileDataSignalChanged(mHasMobileDataFeature, mQSPhoneSignalIconId, - mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut, - mContentDescriptionDataType, mNetworkName, mNoSim, - isQsTypeIconWide(mQSDataTypeIconId)); - } + mConnectedNetworkType = ConnectivityManager.TYPE_NONE; + mConnectedNetworkTypeName = null; } - cb.onAirplaneModeChanged(mAirplaneMode); - } - public void setStackedMode(boolean stacked) { - mDataAndWifiStacked = true; - } + int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0); - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (action.equals(WifiManager.RSSI_CHANGED_ACTION) - || action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION) - || action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { - updateWifiState(intent); - refreshViews(); - } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { - updateSimState(intent); - updateDataIcon(); - refreshViews(); - } else if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) { - updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false), - intent.getStringExtra(TelephonyIntents.EXTRA_SPN), - intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false), - intent.getStringExtra(TelephonyIntents.EXTRA_PLMN)); - refreshViews(); - } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE) || - action.equals(ConnectivityManager.INET_CONDITION_ACTION)) { - updateConnectivity(intent); - refreshViews(); - } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { - refreshLocale(); - refreshViews(); - } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { - refreshLocale(); - updateAirplaneMode(); - refreshViews(); - } else if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION) || - action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION) || - action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) { - updateWimaxState(intent); - refreshViews(); + if (CHATTY) { + Log.d(TAG, "updateConnectivity: networkInfo=" + info); + Log.d(TAG, "updateConnectivity: connectionStatus=" + connectionStatus); } + + mInetCondition = connectionStatus > INET_CONDITION_THRESHOLD; + + if (info != null && info.getType() == ConnectivityManager.TYPE_BLUETOOTH) { + mBluetoothTethered = info.isConnected(); + } else { + mBluetoothTethered = false; + } + + // We want to update all the icons, all at once, for any condition change + mMobileSignalController.setInetCondition(mInetCondition ? 1 : 0, + inetConditionForNetwork(mMobileSignalController.getNetworkType(), mInetCondition)); + mWifiSignalController.setInetCondition( + inetConditionForNetwork(mWifiSignalController.getNetworkType(), mInetCondition)); } + /** + * Recalculate and update the carrier label. + */ + void refreshCarrierLabel() { + Context context = mContext; - // ===== Telephony ============================================================== + WifiSignalController.WifiState wifiState = mWifiSignalController.getState(); + MobileSignalController.MobileState mobileState = mMobileSignalController.getState(); + String label = mMobileSignalController.getLabel("", mConnected, mHasMobileDataFeature); - PhoneStateListener mPhoneStateListener = new PhoneStateListener() { - @Override - public void onSignalStrengthsChanged(SignalStrength signalStrength) { - if (DEBUG) { - Log.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength + - ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel()))); - } - mSignalStrength = signalStrength; - updateTelephonySignalStrength(); - refreshViews(); + // TODO Simplify this ugliness, some of the flows below shouldn't be possible anymore + // but stay for the sake of history. + if (mBluetoothTethered && !mHasMobileDataFeature) { + label = mContext.getString(R.string.bluetooth_tethered); } - @Override - public void onServiceStateChanged(ServiceState state) { - if (DEBUG) { - Log.d(TAG, "onServiceStateChanged voiceState=" + state.getVoiceRegState() - + " dataState=" + state.getDataRegState()); - } - mServiceState = state; - updateTelephonySignalStrength(); - updateDataNetType(); - updateDataIcon(); - refreshViews(); + final boolean ethernetConnected = + (mConnectedNetworkType == ConnectivityManager.TYPE_ETHERNET); + if (ethernetConnected && !mHasMobileDataFeature) { + label = context.getString(R.string.ethernet_label); } - @Override - public void onCallStateChanged(int state, String incomingNumber) { - if (DEBUG) { - Log.d(TAG, "onCallStateChanged state=" + state); - } - // In cdma, if a voice call is made, RSSI should switch to 1x. - if (isCdma()) { - updateTelephonySignalStrength(); - refreshViews(); + if (mAirplaneMode && (!mobileState.connected && !mobileState.isEmergency)) { + // combined values from connected wifi take precedence over airplane mode + if (wifiState.connected && mHasMobileDataFeature) { + // Suppress "No internet connection." from mobile if wifi connected. + label = ""; + } else { + if (!mHasMobileDataFeature) { + label = context.getString( + R.string.status_bar_settings_signal_meter_disconnected); + } } + } else if (!mobileState.dataConnected && !wifiState.connected && !mBluetoothTethered && + !ethernetConnected && !mHasMobileDataFeature) { + // Pretty much no connection. + label = context.getString(R.string.status_bar_settings_signal_meter_disconnected); } - @Override - public void onDataConnectionStateChanged(int state, int networkType) { - if (DEBUG) { - Log.d(TAG, "onDataConnectionStateChanged: state=" + state - + " type=" + networkType); - } - mDataState = state; - mDataNetType = networkType; - updateDataNetType(); - updateDataIcon(); - refreshViews(); + // for mobile devices, we always show mobile connection info here (SPN/PLMN) + // for other devices, we show whatever network is connected + // This is determined above by references to mHasMobileDataFeature. + int length = mCarrierListeners.size(); + for (int i = 0; i < length; i++) { + mCarrierListeners.get(i).setCarrierLabel(label); } + } - @Override - public void onDataActivity(int direction) { - if (DEBUG) { - Log.d(TAG, "onDataActivity: direction=" + direction); - } - mDataActivity = direction; - updateDataIcon(); - refreshViews(); - } - }; + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("NetworkController state:"); + pw.println(String.format(" %s network type %d (%s)", + mConnected ? "CONNECTED" : "DISCONNECTED", + mConnectedNetworkType, mConnectedNetworkTypeName)); + pw.println(" - telephony ------"); + pw.print(" hasVoiceCallingFeature()="); + pw.println(hasVoiceCallingFeature()); - private final void updateSimState(Intent intent) { - String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); - if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { - mSimState = IccCardConstants.State.ABSENT; - } - else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) { - mSimState = IccCardConstants.State.READY; - } - else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { - final String lockedReason = - intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON); - if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { - mSimState = IccCardConstants.State.PIN_REQUIRED; + pw.println(" - Bluetooth ----"); + pw.print(" mBtReverseTethered="); + pw.println(mBluetoothTethered); + + pw.println(" - connectivity ------"); + pw.print(" mInetCondition="); + pw.println(mInetCondition); + pw.print(" mAirplaneMode="); + pw.println(mAirplaneMode); + pw.print(" mLocale="); + pw.println(mLocale); + + mMobileSignalController.dump(pw); + mWifiSignalController.dump(pw); + } + + private boolean mDemoMode; + private int mDemoInetCondition; + private WifiSignalController.WifiState mDemoWifiState; + private MobileSignalController.MobileState mDemoMobileState; + + @Override + public void dispatchDemoCommand(String command, Bundle args) { + if (!mDemoMode && command.equals(COMMAND_ENTER)) { + if (DEBUG) Log.d(TAG, "Entering demo mode"); + unregisterListeners(); + mDemoMode = true; + mDemoInetCondition = mInetCondition ? 1 : 0; + mDemoWifiState = mWifiSignalController.getState(); + mDemoMobileState = mMobileSignalController.getState(); + } else if (mDemoMode && command.equals(COMMAND_EXIT)) { + if (DEBUG) Log.d(TAG, "Exiting demo mode"); + mDemoMode = false; + mWifiSignalController.resetLastState(); + mMobileSignalController.resetLastState(); + registerListeners(); + notifyAllListeners(); + refreshCarrierLabel(); + } else if (mDemoMode && command.equals(COMMAND_NETWORK)) { + String airplane = args.getString("airplane"); + if (airplane != null) { + boolean show = airplane.equals("show"); + int length = mSignalClusters.size(); + for (int i = 0; i < length; i++) { + mSignalClusters.get(i).setIsAirplaneMode(show, TelephonyIcons.FLIGHT_MODE_ICON, + R.string.accessibility_airplane_mode); + } } - else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { - mSimState = IccCardConstants.State.PUK_REQUIRED; + String fully = args.getString("fully"); + if (fully != null) { + mDemoInetCondition = Boolean.parseBoolean(fully) ? 1 : 0; + mWifiSignalController.setInetCondition(mDemoInetCondition); + mMobileSignalController.setInetCondition(mDemoInetCondition, mDemoInetCondition); + } + String wifi = args.getString("wifi"); + if (wifi != null) { + boolean show = wifi.equals("show"); + String level = args.getString("level"); + if (level != null) { + mDemoWifiState.level = level.equals("null") ? -1 + : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1); + mDemoWifiState.connected = mDemoWifiState.level >= 0; + } + mDemoWifiState.enabled = show; + mWifiSignalController.notifyListeners(); } - else { - mSimState = IccCardConstants.State.NETWORK_LOCKED; + String mobile = args.getString("mobile"); + if (mobile != null) { + boolean show = mobile.equals("show"); + String datatype = args.getString("datatype"); + if (datatype != null) { + mDemoMobileState.iconGroup = + datatype.equals("1x") ? TelephonyIcons.ONE_X : + datatype.equals("3g") ? TelephonyIcons.THREE_G : + datatype.equals("4g") ? TelephonyIcons.FOUR_G : + datatype.equals("e") ? TelephonyIcons.E : + datatype.equals("g") ? TelephonyIcons.G : + datatype.equals("h") ? TelephonyIcons.H : + datatype.equals("lte") ? TelephonyIcons.LTE : + datatype.equals("roam") ? TelephonyIcons.ROAMING : + TelephonyIcons.UNKNOWN; + } + int[][] icons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH; + String level = args.getString("level"); + if (level != null) { + mDemoMobileState.level = level.equals("null") ? -1 + : Math.min(Integer.parseInt(level), icons[0].length - 1); + mDemoMobileState.connected = mDemoMobileState.level >= 0; + } + mDemoMobileState.enabled = show; + mMobileSignalController.notifyListeners(); } - } else { - mSimState = IccCardConstants.State.UNKNOWN; + refreshCarrierLabel(); } - if (DEBUG) Log.d(TAG, "updateSimState: mSimState=" + mSimState); - } - - private boolean isCdma() { - return (mSignalStrength != null) && !mSignalStrength.isGsm(); } - private boolean hasService() { - boolean retVal; - if (mServiceState != null) { - // Consider the device to be in service if either voice or data service is available. - // Some SIM cards are marketed as data-only and do not support voice service, and on - // these SIM cards, we want to show signal bars for data service as well as the "no - // service" or "emergency calls only" text that indicates that voice is not available. - switch(mServiceState.getVoiceRegState()) { - case ServiceState.STATE_POWER_OFF: - retVal = false; - break; - case ServiceState.STATE_OUT_OF_SERVICE: - case ServiceState.STATE_EMERGENCY_ONLY: - retVal = mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE; - break; - default: - retVal = true; + static class WifiSignalController extends + SignalController<WifiSignalController.WifiState, SignalController.IconGroup> { + private final WifiManager mWifiManager; + private final AsyncChannel mWifiChannel; + private final boolean mHasMobileData; + + public WifiSignalController(Context context, boolean hasMobileData, + List<NetworkSignalChangedCallback> signalCallbacks, + List<SignalCluster> signalClusters, NetworkControllerImpl networkController) { + super("WifiSignalController", context, ConnectivityManager.TYPE_WIFI, signalCallbacks, + signalClusters, networkController); + mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + mHasMobileData = hasMobileData; + Handler handler = new WifiHandler(); + mWifiChannel = new AsyncChannel(); + Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger(); + if (wifiMessenger != null) { + mWifiChannel.connect(context, handler, wifiMessenger); } - } else { - retVal = false; + // WiFi only has one state. + mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup( + "Wi-Fi Icons", + WifiIcons.WIFI_SIGNAL_STRENGTH, + WifiIcons.QS_WIFI_SIGNAL_STRENGTH, + AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH, + WifiIcons.WIFI_NO_NETWORK, + WifiIcons.QS_WIFI_NO_NETWORK, + WifiIcons.WIFI_NO_NETWORK, + WifiIcons.QS_WIFI_NO_NETWORK, + AccessibilityContentDescriptions.WIFI_NO_CONNECTION + ); } - if (DEBUG) Log.d(TAG, "hasService: mServiceState=" + mServiceState + " retVal=" + retVal); - return retVal; - } - - private void updateAirplaneMode() { - mAirplaneMode = (Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, 0) == 1); - } - private void refreshLocale() { - mLocale = mContext.getResources().getConfiguration().locale; - } + @Override + public WifiState cleanState() { + return new WifiState(); + } - private final void updateTelephonySignalStrength() { - if (DEBUG) { - Log.d(TAG, "updateTelephonySignalStrength: hasService=" + hasService() - + " ss=" + mSignalStrength); - } - if (!hasService()) { - if (CHATTY) Log.d(TAG, "updateTelephonySignalStrength: !hasService()"); - mPhoneSignalIconId = R.drawable.stat_sys_signal_null; - mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal; - mDataSignalIconId = R.drawable.stat_sys_signal_null; - mContentDescriptionPhoneSignal = mContext.getString( - AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0]); - } else { - if (mSignalStrength == null) { - if (CHATTY) Log.d(TAG, "updateTelephonySignalStrength: mSignalStrength == null"); - mPhoneSignalIconId = R.drawable.stat_sys_signal_null; - mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal; - mDataSignalIconId = R.drawable.stat_sys_signal_null; - mContentDescriptionPhoneSignal = mContext.getString( - AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0]); - } else { - int iconLevel; - int[] iconList; - if (isCdma() && mAlwaysShowCdmaRssi) { - mLastSignalLevel = iconLevel = mSignalStrength.getCdmaLevel(); - if (DEBUG) { - Log.d(TAG, "updateTelephonySignalStrength:" - + " mAlwaysShowCdmaRssi=" + mAlwaysShowCdmaRssi - + " set to cdmaLevel=" + mSignalStrength.getCdmaLevel() - + " instead of level=" + mSignalStrength.getLevel()); - } - } else { - mLastSignalLevel = iconLevel = mSignalStrength.getLevel(); - } + /** + * {@inheritDoc} + */ + @Override + public void notifyListeners() { + // only show wifi in the cluster if connected or if wifi-only + boolean wifiEnabled = mCurrentState.enabled + && (mCurrentState.connected || !mHasMobileData); + String wifiDesc = wifiEnabled ? mCurrentState.ssid : null; + boolean ssidPresent = wifiEnabled && mCurrentState.ssid != null; + String contentDescription = getStringIfExists(getContentDescription()); + int length = mSignalsChangedCallbacks.size(); + for (int i = 0; i < length; i++) { + mSignalsChangedCallbacks.get(i).onWifiSignalChanged(mCurrentState.enabled, + mCurrentState.connected, getQsCurrentIconId(), + ssidPresent && mCurrentState.activityIn, + ssidPresent && mCurrentState.activityOut, contentDescription, wifiDesc); + } - if (isRoaming()) { - iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition]; - } else { - iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition]; - } - mPhoneSignalIconId = iconList[iconLevel]; - mQSPhoneSignalIconId = - TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[mInetCondition][iconLevel]; - mContentDescriptionPhoneSignal = mContext.getString( - AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[iconLevel]); - mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel]; - if (DEBUG) Log.d(TAG, "updateTelephonySignalStrength: iconLevel=" + iconLevel); + int signalClustersLength = mSignalClusters.size(); + for (int i = 0; i < signalClustersLength; i++) { + mSignalClusters.get(i).setWifiIndicators( + // only show wifi in the cluster if connected or if wifi-only + mCurrentState.enabled && (mCurrentState.connected || !mHasMobileData), + getCurrentIconId(), contentDescription); } } - } - - private int inetConditionForNetwork(int networkType) { - return (mInetCondition == 1 && mConnectedNetworkType == networkType) ? 1 : 0; - } - private final void updateDataNetType() { - int inetCondition; - mDataTypeIconId = mQSDataTypeIconId = 0; - if (mIsWimaxEnabled && mWimaxConnected) { - // wimax is a special 4g network not handled by telephony - inetCondition = inetConditionForNetwork(ConnectivityManager.TYPE_WIMAX); - mDataIconList = TelephonyIcons.DATA_4G[inetCondition]; - mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_4g; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_4G[inetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_4g); - } else { - inetCondition = inetConditionForNetwork(ConnectivityManager.TYPE_MOBILE); - final boolean showDataTypeIcon = (inetCondition > 0); - switch (mDataNetType) { - case TelephonyManager.NETWORK_TYPE_UNKNOWN: - if (!mShowAtLeastThreeGees) { - mDataIconList = TelephonyIcons.DATA_G[inetCondition]; - mContentDescriptionDataType = ""; - break; - } else { - // fall through - } - case TelephonyManager.NETWORK_TYPE_EDGE: - if (!mShowAtLeastThreeGees) { - mDataIconList = TelephonyIcons.DATA_E[inetCondition]; - mDataTypeIconId = showDataTypeIcon ? - R.drawable.stat_sys_data_fully_connected_e : 0; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_E[inetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_edge); - break; - } else { - // fall through - } - case TelephonyManager.NETWORK_TYPE_UMTS: - mDataIconList = TelephonyIcons.DATA_3G[inetCondition]; - mDataTypeIconId = showDataTypeIcon ? - R.drawable.stat_sys_data_fully_connected_3g : 0; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[inetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_3g); - break; - case TelephonyManager.NETWORK_TYPE_HSDPA: - case TelephonyManager.NETWORK_TYPE_HSUPA: - case TelephonyManager.NETWORK_TYPE_HSPA: - case TelephonyManager.NETWORK_TYPE_HSPAP: - if (mHspaDataDistinguishable) { - mDataIconList = TelephonyIcons.DATA_H[inetCondition]; - mDataTypeIconId = showDataTypeIcon ? - R.drawable.stat_sys_data_fully_connected_h : 0; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_H[inetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_3_5g); + /** + * Extract wifi state directly from broadcasts about changes in wifi state. + */ + public void handleBroadcast(Intent intent) { + String action = intent.getAction(); + if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { + mCurrentState.enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, + WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; + } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { + final NetworkInfo networkInfo = (NetworkInfo) + intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); + mCurrentState.connected = networkInfo != null && networkInfo.isConnected(); + // If Connected grab the signal strength and ssid. + if (mCurrentState.connected) { + // try getting it out of the intent first + WifiInfo info = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) != null + ? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) + : mWifiManager.getConnectionInfo(); + if (info != null) { + mCurrentState.ssid = huntForSsid(info); } else { - mDataIconList = TelephonyIcons.DATA_3G[inetCondition]; - mDataTypeIconId = showDataTypeIcon ? - R.drawable.stat_sys_data_fully_connected_3g : 0; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[inetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_3g); + mCurrentState.ssid = null; } - break; - case TelephonyManager.NETWORK_TYPE_CDMA: - if (!mShowAtLeastThreeGees) { - // display 1xRTT for IS95A/B - mDataIconList = TelephonyIcons.DATA_1X[inetCondition]; - mDataTypeIconId = showDataTypeIcon ? - R.drawable.stat_sys_data_fully_connected_1x : 0; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_1X[inetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_cdma); - break; - } else { - // fall through - } - case TelephonyManager.NETWORK_TYPE_1xRTT: - if (!mShowAtLeastThreeGees) { - mDataIconList = TelephonyIcons.DATA_1X[inetCondition]; - mDataTypeIconId = showDataTypeIcon ? - R.drawable.stat_sys_data_fully_connected_1x : 0; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_1X[inetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_cdma); - break; - } else { - // fall through - } - case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through - case TelephonyManager.NETWORK_TYPE_EVDO_A: - case TelephonyManager.NETWORK_TYPE_EVDO_B: - case TelephonyManager.NETWORK_TYPE_EHRPD: - mDataIconList = TelephonyIcons.DATA_3G[inetCondition]; - mDataTypeIconId = showDataTypeIcon ? - R.drawable.stat_sys_data_fully_connected_3g : 0; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[inetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_3g); - break; - case TelephonyManager.NETWORK_TYPE_LTE: - boolean show4GforLTE = mContext.getResources().getBoolean(R.bool.config_show4GForLTE); - if (show4GforLTE) { - mDataIconList = TelephonyIcons.DATA_4G[inetCondition]; - mDataTypeIconId = showDataTypeIcon ? - R.drawable.stat_sys_data_fully_connected_4g : 0; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_4G[inetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_4g); - } else { - mDataIconList = TelephonyIcons.DATA_LTE[inetCondition]; - mDataTypeIconId = showDataTypeIcon ? TelephonyIcons.ICON_LTE : 0; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_LTE[inetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_lte); - } - break; - default: - if (!mShowAtLeastThreeGees) { - mDataIconList = TelephonyIcons.DATA_G[inetCondition]; - mDataTypeIconId = showDataTypeIcon ? - R.drawable.stat_sys_data_fully_connected_g : 0; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_G[inetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_gprs); - } else { - mDataIconList = TelephonyIcons.DATA_3G[inetCondition]; - mDataTypeIconId = showDataTypeIcon ? - R.drawable.stat_sys_data_fully_connected_3g : 0; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[inetCondition]; - mContentDescriptionDataType = mContext.getString( - R.string.accessibility_data_connection_3g); - } - break; + } else if (!mCurrentState.connected) { + mCurrentState.ssid = null; + } + } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { + mCurrentState.rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200); + mCurrentState.level = WifiManager.calculateSignalLevel( + mCurrentState.rssi, WifiIcons.WIFI_LEVEL_COUNT); } - } - if (isRoaming()) { - mDataTypeIconId = TelephonyIcons.ROAMING_ICON; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition]; + notifyListenersIfNecessary(); } - } - boolean isCdmaEri() { - if (mServiceState != null) { - final int iconIndex = mServiceState.getCdmaEriIconIndex(); - if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) { - final int iconMode = mServiceState.getCdmaEriIconMode(); - if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL - || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) { - return true; + private String huntForSsid(WifiInfo info) { + String ssid = info.getSSID(); + if (ssid != null) { + return ssid; + } + // OK, it's not in the connectionInfo; we have to go hunting for it + List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks(); + int length = networks.size(); + for (int i = 0; i < length; i++) { + if (networks.get(i).networkId == info.getNetworkId()) { + return networks.get(i).SSID; } } + return null; } - return false; - } - private boolean isRoaming() { - if (isCdma()) { - return isCdmaEri(); - } else { - return mServiceState != null && mServiceState.getRoaming(); + @VisibleForTesting + void setActivity(int wifiActivity) { + mCurrentState.activityIn = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT + || wifiActivity == WifiManager.DATA_ACTIVITY_IN; + mCurrentState.activityOut = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT + || wifiActivity == WifiManager.DATA_ACTIVITY_OUT; + notifyListenersIfNecessary(); } - } - private final void updateDataIcon() { - int iconId; - boolean visible = true; - - if (!isCdma()) { - // GSM case, we have to check also the sim state - if (mSimState == IccCardConstants.State.READY || - mSimState == IccCardConstants.State.UNKNOWN) { - mNoSim = false; - if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { - switch (mDataActivity) { - case TelephonyManager.DATA_ACTIVITY_IN: - iconId = mDataIconList[1]; - break; - case TelephonyManager.DATA_ACTIVITY_OUT: - iconId = mDataIconList[2]; - break; - case TelephonyManager.DATA_ACTIVITY_INOUT: - iconId = mDataIconList[3]; - break; - default: - iconId = mDataIconList[0]; - break; - } - mDataDirectionIconId = iconId; - } else { - iconId = 0; - visible = false; - } - } else { - iconId = 0; - mNoSim = true; - visible = false; // no SIM? no data - } - } else { - // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT - if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { - switch (mDataActivity) { - case TelephonyManager.DATA_ACTIVITY_IN: - iconId = mDataIconList[1]; - break; - case TelephonyManager.DATA_ACTIVITY_OUT: - iconId = mDataIconList[2]; + /** + * Handler to receive the data activity on wifi. + */ + class WifiHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: + if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { + mWifiChannel.sendMessage(Message.obtain(this, + AsyncChannel.CMD_CHANNEL_FULL_CONNECTION)); + } else { + Log.e(mTag, "Failed to connect to wifi"); + } break; - case TelephonyManager.DATA_ACTIVITY_INOUT: - iconId = mDataIconList[3]; + case WifiManager.DATA_ACTIVITY_NOTIFICATION: + setActivity(msg.arg1); break; - case TelephonyManager.DATA_ACTIVITY_DORMANT: default: - iconId = mDataIconList[0]; + // Ignore break; } - } else { - iconId = 0; - visible = false; } } - mDataDirectionIconId = iconId; - mDataConnected = visible; - } + static class WifiState extends SignalController.State { + String ssid; - void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) { - if (false) { - Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn - + " showPlmn=" + showPlmn + " plmn=" + plmn); - } - StringBuilder str = new StringBuilder(); - boolean something = false; - if (showPlmn && plmn != null) { - str.append(plmn); - something = true; - } - if (showSpn && spn != null) { - if (something) { - str.append(mNetworkNameSeparator); + @Override + public void copyFrom(State s) { + WifiState state = (WifiState) s; + ssid = state.ssid; + super.copyFrom(s); } - str.append(spn); - something = true; - } - if (something) { - mNetworkName = str.toString(); - } else { - mNetworkName = mNetworkNameDefault; - } - } - // ===== Wifi =================================================================== + @Override + protected void toString(StringBuilder builder) { + builder.append("ssid=").append(ssid).append(','); + super.toString(builder); + } - class WifiHandler extends Handler { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: - if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { - mWifiChannel.sendMessage(Message.obtain(this, - AsyncChannel.CMD_CHANNEL_FULL_CONNECTION)); - } else { - Log.e(TAG, "Failed to connect to wifi"); - } - break; - case WifiManager.DATA_ACTIVITY_NOTIFICATION: - if (msg.arg1 != mWifiActivity) { - mWifiActivity = msg.arg1; - refreshViews(); - } - break; - default: - //Ignore - break; + @Override + public boolean equals(Object o) { + return super.equals(o) + && Objects.equals(((WifiState) o).ssid, ssid); } } } - private void updateWifiState(Intent intent) { - final String action = intent.getAction(); - if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { - mWifiEnabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, - WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; - - } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { - final NetworkInfo networkInfo = (NetworkInfo) - intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); - boolean wasConnected = mWifiConnected; - mWifiConnected = networkInfo != null && networkInfo.isConnected(); - // If Connected grab the signal strength and ssid - if (mWifiConnected) { - // try getting it out of the intent first - WifiInfo info = (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO); - if (info == null) { - info = mWifiManager.getConnectionInfo(); + static class MobileSignalController extends SignalController<MobileSignalController.MobileState, + MobileSignalController.MobileIconGroup> { + private final Config mConfig; + private final TelephonyManager mPhone; + private final String mNetworkNameDefault; + private final String mNetworkNameSeparator; + + // @VisibleForDemoMode + Map<Integer, MobileIconGroup> mNetworkToIconLookup; + + // Since some pieces of the phone state are interdependent we store it locally, + // this could potentially become part of MobileState for simplification/complication + // of code. + private IccCardConstants.State mSimState = IccCardConstants.State.READY; + private int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN; + private int mDataState = TelephonyManager.DATA_DISCONNECTED; + private ServiceState mServiceState; + private SignalStrength mSignalStrength; + private MobileIconGroup mDefaultIcons; + + // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't + // need listener lists anymore. + public MobileSignalController(Context context, Config config, boolean hasMobileData, + TelephonyManager phone, List<NetworkSignalChangedCallback> signalCallbacks, + List<SignalCluster> signalClusters, NetworkControllerImpl networkController) { + super("MobileSignalController", context, ConnectivityManager.TYPE_MOBILE, + signalCallbacks, signalClusters, networkController); + mConfig = config; + mPhone = phone; + mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator); + mNetworkNameDefault = getStringIfExists( + com.android.internal.R.string.lockscreen_carrier_default); + + mapIconSets(); + + mLastState.networkName = mCurrentState.networkName = mNetworkNameDefault; + mLastState.enabled = mCurrentState.enabled = hasMobileData; + mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons; + } + + /** + * Get (the mobile parts of) the carrier string. + * + * @param currentLabel can be used for concatenation, currently just empty + * @param connected whether the device has connection to the internet at all + * @param isMobileLabel whether to always return the network or just when data is connected + */ + public String getLabel(String currentLabel, boolean connected, boolean isMobileLabel) { + if (!mCurrentState.enabled) { + return ""; + } else { + String mobileLabel = ""; + // We want to show the carrier name if in service and either: + // - We are connected to mobile data, or + // - We are not connected to mobile data, as long as the *reason* packets are not + // being routed over that link is that we have better connectivity via wifi. + // If data is disconnected for some other reason but wifi (or ethernet/bluetooth) + // is connected, we show nothing. + // Otherwise (nothing connected) we show "No internet connection". + if (mCurrentState.dataConnected) { + mobileLabel = mCurrentState.networkName; + } else if (connected || mCurrentState.isEmergency) { + if (mCurrentState.connected || mCurrentState.isEmergency) { + // The isEmergencyOnly test covers the case of a phone with no SIM + mobileLabel = mCurrentState.networkName; + } + } else { + mobileLabel = mContext + .getString(R.string.status_bar_settings_signal_meter_disconnected); } - if (info != null) { - mWifiSsid = huntForSsid(info); + + // Now for things that should only be shown when actually using mobile data. + if (isMobileLabel) { + return mobileLabel; } else { - mWifiSsid = null; + return mCurrentState.dataConnected ? mobileLabel : currentLabel; } - } else if (!mWifiConnected) { - mWifiSsid = null; } - } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { - mWifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200); - mWifiLevel = WifiManager.calculateSignalLevel( - mWifiRssi, WifiIcons.WIFI_LEVEL_COUNT); } - updateWifiIcons(); - } + public int getDataContentDescription() { + return getIcons().mDataContentDescription; + } - private void updateWifiIcons() { - int inetCondition = inetConditionForNetwork(ConnectivityManager.TYPE_WIFI); - if (mWifiConnected) { - mWifiIconId = WifiIcons.WIFI_SIGNAL_STRENGTH[inetCondition][mWifiLevel]; - mQSWifiIconId = WifiIcons.QS_WIFI_SIGNAL_STRENGTH[inetCondition][mWifiLevel]; - mContentDescriptionWifi = mContext.getString( - AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[mWifiLevel]); - } else { - if (mDataAndWifiStacked) { - mWifiIconId = 0; - mQSWifiIconId = 0; - } else { - mWifiIconId = mWifiEnabled ? R.drawable.stat_sys_wifi_signal_null : 0; - mQSWifiIconId = mWifiEnabled ? R.drawable.ic_qs_wifi_no_network : 0; - } - mContentDescriptionWifi = mContext.getString(R.string.accessibility_no_wifi); + public void setAirplaneMode(boolean airplaneMode) { + mCurrentState.airplaneMode = airplaneMode; + notifyListenersIfNecessary(); } - } - private String huntForSsid(WifiInfo info) { - String ssid = info.getSSID(); - if (ssid != null) { - return ssid; + public void setInetCondition(int inetCondition, int inetConditionForNetwork) { + // For mobile data, use general inet condition for phone signal indexing, + // and network specific for data indexing (I think this might be a bug, but + // keeping for now). + // TODO: Update with explanation of why. + mCurrentState.inetForNetwork = inetConditionForNetwork; + setInetCondition(inetCondition); } - // OK, it's not in the connectionInfo; we have to go hunting for it - List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks(); - for (WifiConfiguration net : networks) { - if (net.networkId == info.getNetworkId()) { - return net.SSID; - } + + /** + * Start listening for phone state changes. + */ + public void registerListener() { + mPhone.listen(mPhoneStateListener, + PhoneStateListener.LISTEN_SERVICE_STATE + | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS + | PhoneStateListener.LISTEN_CALL_STATE + | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE + | PhoneStateListener.LISTEN_DATA_ACTIVITY); } - return null; - } + /** + * Stop listening for phone state changes. + */ + public void unregisterListener() { + mPhone.listen(mPhoneStateListener, 0); + } - // ===== Wimax =================================================================== - private final void updateWimaxState(Intent intent) { - final String action = intent.getAction(); - boolean wasConnected = mWimaxConnected; - if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION)) { - int wimaxStatus = intent.getIntExtra(WimaxManagerConstants.EXTRA_4G_STATE, - WimaxManagerConstants.NET_4G_STATE_UNKNOWN); - mIsWimaxEnabled = (wimaxStatus == - WimaxManagerConstants.NET_4G_STATE_ENABLED); - } else if (action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION)) { - mWimaxSignal = intent.getIntExtra(WimaxManagerConstants.EXTRA_NEW_SIGNAL_LEVEL, 0); - } else if (action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) { - mWimaxState = intent.getIntExtra(WimaxManagerConstants.EXTRA_WIMAX_STATE, - WimaxManagerConstants.NET_4G_STATE_UNKNOWN); - mWimaxExtraState = intent.getIntExtra( - WimaxManagerConstants.EXTRA_WIMAX_STATE_DETAIL, - WimaxManagerConstants.NET_4G_STATE_UNKNOWN); - mWimaxConnected = (mWimaxState == - WimaxManagerConstants.WIMAX_STATE_CONNECTED); - mWimaxIdle = (mWimaxExtraState == WimaxManagerConstants.WIMAX_IDLE); - } - updateDataNetType(); - updateWimaxIcons(); - } + /** + * Produce a mapping of data network types to icon groups for simple and quick use in + * updateTelephony. + * + * TODO: See if config can change with locale, this may need to be regenerated on Locale + * change. + */ + private void mapIconSets() { + mNetworkToIconLookup = new HashMap<Integer, MobileIconGroup>(); + + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyIcons.THREE_G); + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyIcons.THREE_G); + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyIcons.THREE_G); + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyIcons.THREE_G); + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UMTS, TelephonyIcons.THREE_G); - private void updateWimaxIcons() { - if (mIsWimaxEnabled) { - if (mWimaxConnected) { - int inetCondition = inetConditionForNetwork(ConnectivityManager.TYPE_WIMAX); - if (mWimaxIdle) - mWimaxIconId = WimaxIcons.WIMAX_IDLE; - else - mWimaxIconId = WimaxIcons.WIMAX_SIGNAL_STRENGTH[inetCondition][mWimaxSignal]; - mContentDescriptionWimax = mContext.getString( - AccessibilityContentDescriptions.WIMAX_CONNECTION_STRENGTH[mWimaxSignal]); + if (!mConfig.showAtLeastThreeGees) { + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN, + TelephonyIcons.UNKNOWN); + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, TelephonyIcons.E); + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA, TelephonyIcons.ONE_X); + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyIcons.ONE_X); + + mDefaultIcons = TelephonyIcons.G; } else { - mWimaxIconId = WimaxIcons.WIMAX_DISCONNECTED; - mContentDescriptionWimax = mContext.getString(R.string.accessibility_no_wimax); + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN, + TelephonyIcons.THREE_G); + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, + TelephonyIcons.THREE_G); + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA, + TelephonyIcons.THREE_G); + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT, + TelephonyIcons.THREE_G); + mDefaultIcons = TelephonyIcons.THREE_G; } - } else { - mWimaxIconId = 0; - } - } - // ===== Full or limited Internet connectivity ================================== + MobileIconGroup hGroup = TelephonyIcons.THREE_G; + if (mConfig.hspaDataDistinguishable) { + hGroup = TelephonyIcons.H; + } + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSDPA, hGroup); + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSUPA, hGroup); + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPA, hGroup); + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPAP, hGroup); - private void updateConnectivity(Intent intent) { - if (CHATTY) { - Log.d(TAG, "updateConnectivity: intent=" + intent); + if (mConfig.show4gForLte) { + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.FOUR_G); + } else { + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.LTE); + } } - final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); - - // Are we connected at all, by any interface? - mConnected = info != null && info.isConnected(); - if (mConnected) { - mConnectedNetworkType = info.getType(); - mConnectedNetworkTypeName = info.getTypeName(); - } else { - mConnectedNetworkType = ConnectivityManager.TYPE_NONE; - mConnectedNetworkTypeName = null; + /** + * {@inheritDoc} + */ + @Override + public void notifyListeners() { + MobileIconGroup icons = getIcons(); + + String contentDescription = getStringIfExists(getContentDescription()); + String dataContentDescription = getStringIfExists(icons.mDataContentDescription); + int qsTypeIcon = icons.mQsDataType[mCurrentState.inetForNetwork]; + int length = mSignalsChangedCallbacks.size(); + for (int i = 0; i < length; i++) { + mSignalsChangedCallbacks.get(i).onMobileDataSignalChanged(mCurrentState.enabled + && !mCurrentState.isEmergency && !mCurrentState.airplaneMode, + getQsCurrentIconId(), contentDescription, + qsTypeIcon, + mCurrentState.dataConnected && mCurrentState.activityIn, + mCurrentState.dataConnected && mCurrentState.activityOut, + dataContentDescription, + mCurrentState.isEmergency ? null : mCurrentState.networkName, + mCurrentState.noSim, + // Only wide if actually showing something. + icons.mIsWide && qsTypeIcon != 0); + } + boolean showDataIcon = mCurrentState.inetForNetwork != 0 + || mCurrentState.iconGroup == TelephonyIcons.ROAMING; + int typeIcon = showDataIcon ? icons.mDataType : 0; + int signalClustersLength = mSignalClusters.size(); + for (int i = 0; i < signalClustersLength; i++) { + mSignalClusters.get(i).setMobileDataIndicators( + mCurrentState.enabled && !mCurrentState.airplaneMode, + getCurrentIconId(), + typeIcon, + contentDescription, + dataContentDescription, + // Only wide if actually showing something. + icons.mIsWide && typeIcon != 0); + } } - int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0); - - if (CHATTY) { - Log.d(TAG, "updateConnectivity: networkInfo=" + info); - Log.d(TAG, "updateConnectivity: connectionStatus=" + connectionStatus); + @Override + public MobileState cleanState() { + return new MobileState(); + } + + private boolean hasService() { + if (mServiceState != null) { + // Consider the device to be in service if either voice or data + // service is available. Some SIM cards are marketed as data-only + // and do not support voice service, and on these SIM cards, we + // want to show signal bars for data service as well as the "no + // service" or "emergency calls only" text that indicates that voice + // is not available. + switch (mServiceState.getVoiceRegState()) { + case ServiceState.STATE_POWER_OFF: + return false; + case ServiceState.STATE_OUT_OF_SERVICE: + case ServiceState.STATE_EMERGENCY_ONLY: + return mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE; + default: + return true; + } + } else { + return false; + } } - mInetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0); - - if (info != null && info.getType() == ConnectivityManager.TYPE_BLUETOOTH) { - mBluetoothTethered = info.isConnected(); - } else { - mBluetoothTethered = false; + private boolean isCdma() { + return (mSignalStrength != null) && !mSignalStrength.isGsm(); } - // We want to update all the icons, all at once, for any condition change - updateDataNetType(); - updateWimaxIcons(); - updateDataIcon(); - updateTelephonySignalStrength(); - updateWifiIcons(); - } - - - // ===== Update the views ======================================================= + public boolean isEmergencyOnly() { + return (mServiceState != null && mServiceState.isEmergencyOnly()); + } - void refreshViews() { - Context context = mContext; + private boolean isRoaming() { + if (isCdma()) { + final int iconMode = mServiceState.getCdmaEriIconMode(); + return mServiceState.getCdmaEriIconIndex() != EriInfo.ROAMING_INDICATOR_OFF + && (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL + || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH); + } else { + return mServiceState != null && mServiceState.getRoaming(); + } + } - int combinedSignalIconId = 0; - String combinedLabel = ""; - String wifiLabel = ""; - String mobileLabel = ""; - int N; - final boolean emergencyOnly = isEmergencyOnly(); + public void handleBroadcast(Intent intent) { + String action = intent.getAction(); + if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { + String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); + final String lockedReason = + intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON); + updateSimState(stateExtra, lockedReason); + updateTelephony(); + } else if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) { + updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false), + intent.getStringExtra(TelephonyIntents.EXTRA_SPN), + intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false), + intent.getStringExtra(TelephonyIntents.EXTRA_PLMN)); + notifyListenersIfNecessary(); + } + } - if (!mHasMobileDataFeature) { - mDataSignalIconId = mPhoneSignalIconId = 0; - mQSPhoneSignalIconId = 0; - mobileLabel = ""; - } else { - // We want to show the carrier name if in service and either: - // - We are connected to mobile data, or - // - We are not connected to mobile data, as long as the *reason* packets are not - // being routed over that link is that we have better connectivity via wifi. - // If data is disconnected for some other reason but wifi (or ethernet/bluetooth) - // is connected, we show nothing. - // Otherwise (nothing connected) we show "No internet connection". - - if (mDataConnected) { - mobileLabel = mNetworkName; - } else if (mConnected || emergencyOnly) { - if (hasService() || emergencyOnly) { - // The isEmergencyOnly test covers the case of a phone with no SIM - mobileLabel = mNetworkName; + /** + * Determines the current sim state, based on a TelephonyIntents.ACTION_SIM_STATE_CHANGED + * broadcast. + */ + private final void updateSimState(String stateExtra, String lockedReason) { + if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { + mSimState = IccCardConstants.State.ABSENT; + } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) { + mSimState = IccCardConstants.State.READY; + } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { + if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { + mSimState = IccCardConstants.State.PIN_REQUIRED; + } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { + mSimState = IccCardConstants.State.PUK_REQUIRED; } else { - // Tablets, basically - mobileLabel = ""; + mSimState = IccCardConstants.State.NETWORK_LOCKED; } } else { - mobileLabel - = context.getString(R.string.status_bar_settings_signal_meter_disconnected); + mSimState = IccCardConstants.State.UNKNOWN; } + if (DEBUG) Log.d(TAG, "updateSimState: mSimState=" + mSimState); + } - // Now for things that should only be shown when actually using mobile data. - if (mDataConnected) { - combinedSignalIconId = mDataSignalIconId; - - combinedLabel = mobileLabel; - combinedSignalIconId = mDataSignalIconId; // set by updateDataIcon() - mContentDescriptionCombinedSignal = mContentDescriptionDataType; + /** + * Updates the network's name based on incoming spn and plmn. + */ + void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) { + if (CHATTY) { + Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn + + " showPlmn=" + showPlmn + " plmn=" + plmn); + } + StringBuilder str = new StringBuilder(); + if (showPlmn && plmn != null) { + str.append(plmn); + } + if (showSpn && spn != null) { + if (str.length() != 0) { + str.append(mNetworkNameSeparator); + } + str.append(spn); + } + if (str.length() != 0) { + mCurrentState.networkName = str.toString(); + } else { + mCurrentState.networkName = mNetworkNameDefault; } } - if (mWifiConnected) { - if (mWifiSsid == null) { - wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_wifi_nossid); + /** + * Updates the current state based on mServiceState, mSignalStrength, mDataNetType, + * mDataState, and mSimState. It should be called any time one of these is updated. + * This will call listeners if necessary. + */ + private final void updateTelephony() { + if (DEBUG) { + Log.d(TAG, "updateTelephonySignalStrength: hasService=" + hasService() + + " ss=" + mSignalStrength); + } + mCurrentState.connected = hasService() && mSignalStrength != null; + if (mCurrentState.connected) { + if (!mSignalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) { + mCurrentState.level = mSignalStrength.getCdmaLevel(); + } else { + mCurrentState.level = mSignalStrength.getLevel(); + } + } + if (mNetworkToIconLookup.containsKey(mDataNetType)) { + mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType); } else { - wifiLabel = mWifiSsid; - if (DEBUG) { - wifiLabel += "xxxxXXXXxxxxXXXX"; + mCurrentState.iconGroup = mDefaultIcons; + } + mCurrentState.dataConnected = mCurrentState.connected + && mDataState == TelephonyManager.DATA_CONNECTED; + if (!isCdma()) { + if (mSimState == IccCardConstants.State.READY || + mSimState == IccCardConstants.State.UNKNOWN) { + mCurrentState.noSim = false; + } else { + mCurrentState.noSim = true; + // No sim, no data. + mCurrentState.dataConnected = false; } } - combinedLabel = wifiLabel; - combinedSignalIconId = mWifiIconId; // set by updateWifiIcons() - mContentDescriptionCombinedSignal = mContentDescriptionWifi; - } else { - if (mHasMobileDataFeature) { - wifiLabel = ""; - } else { - wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected); + if (isRoaming()) { + mCurrentState.iconGroup = TelephonyIcons.ROAMING; + } + if (isEmergencyOnly() != mCurrentState.isEmergency) { + mCurrentState.isEmergency = isEmergencyOnly(); + mNetworkController.recalculateEmergency(); } + notifyListenersIfNecessary(); } - if (mBluetoothTethered) { - combinedLabel = mContext.getString(R.string.bluetooth_tethered); - combinedSignalIconId = mBluetoothTetherIconId; - mContentDescriptionCombinedSignal = mContext.getString( - R.string.accessibility_bluetooth_tether); + @VisibleForTesting + void setActivity(int activity) { + mCurrentState.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT + || activity == TelephonyManager.DATA_ACTIVITY_IN; + mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT + || activity == TelephonyManager.DATA_ACTIVITY_OUT; + notifyListenersIfNecessary(); } - final boolean ethernetConnected = (mConnectedNetworkType == ConnectivityManager.TYPE_ETHERNET); - if (ethernetConnected) { - combinedLabel = context.getString(R.string.ethernet_label); + @Override + public void dump(PrintWriter pw) { + super.dump(pw); + pw.println(" mServiceState=" + mServiceState + ","); + pw.println(" mSignalStrength=" + mSignalStrength + ","); + pw.println(" mDataState=" + mDataState + ","); + pw.println(" mDataNetType=" + mDataNetType + ","); } - if (mAirplaneMode && - (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly()))) { - // Only display the flight-mode icon if not in "emergency calls only" mode. - - // look again; your radios are now airplanes - mContentDescriptionPhoneSignal = mContext.getString( - R.string.accessibility_airplane_mode); - mAirplaneIconId = TelephonyIcons.FLIGHT_MODE_ICON; - mPhoneSignalIconId = mDataSignalIconId = mDataTypeIconId = mQSDataTypeIconId = 0; - mQSPhoneSignalIconId = 0; - - // combined values from connected wifi take precedence over airplane mode - if (mWifiConnected) { - // Suppress "No internet connection." from mobile if wifi connected. - mobileLabel = ""; - } else { - if (mHasMobileDataFeature) { - // let the mobile icon show "No internet connection." - wifiLabel = ""; - } else { - wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected); - combinedLabel = wifiLabel; + PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + @Override + public void onSignalStrengthsChanged(SignalStrength signalStrength) { + if (DEBUG) { + Log.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength + + ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel()))); } - mContentDescriptionCombinedSignal = mContentDescriptionPhoneSignal; - combinedSignalIconId = mDataSignalIconId; + mSignalStrength = signalStrength; + updateTelephony(); } - } - else if (!mDataConnected && !mWifiConnected && !mBluetoothTethered && !mWimaxConnected && !ethernetConnected) { - // pretty much totally disconnected - combinedLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected); - // On devices without mobile radios, we want to show the wifi icon - combinedSignalIconId = - mHasMobileDataFeature ? mDataSignalIconId : mWifiIconId; - mContentDescriptionCombinedSignal = mHasMobileDataFeature - ? mContentDescriptionDataType : mContentDescriptionWifi; + @Override + public void onServiceStateChanged(ServiceState state) { + if (DEBUG) { + Log.d(TAG, "onServiceStateChanged voiceState=" + state.getVoiceRegState() + + " dataState=" + state.getDataRegState()); + } + mServiceState = state; + updateTelephony(); + } - int inetCondition = inetConditionForNetwork(ConnectivityManager.TYPE_MOBILE); + @Override + public void onDataConnectionStateChanged(int state, int networkType) { + if (DEBUG) { + Log.d(TAG, "onDataConnectionStateChanged: state=" + state + + " type=" + networkType); + } + mDataState = state; + mDataNetType = networkType; + updateTelephony(); + } - mDataTypeIconId = 0; - mQSDataTypeIconId = 0; - if (isRoaming()) { - mDataTypeIconId = TelephonyIcons.ROAMING_ICON; - mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition]; + @Override + public void onDataActivity(int direction) { + if (DEBUG) { + Log.d(TAG, "onDataActivity: direction=" + direction); + } + setActivity(direction); + } + }; + + static class MobileIconGroup extends SignalController.IconGroup { + final int mDataContentDescription; // mContentDescriptionDataType + final int mDataType; + final boolean mIsWide; + final int[] mQsDataType; + + public MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc, + int sbNullState, int qsNullState, int sbDiscState, int qsDiscState, + int discContentDesc, int dataContentDesc, int dataType, boolean isWide, + int[] qsDataType) { + super(name, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState, + qsDiscState, discContentDesc); + mDataContentDescription = dataContentDesc; + mDataType = dataType; + mIsWide = isWide; + mQsDataType = qsDataType; } } - if (mDemoMode) { - mQSWifiIconId = mDemoWifiLevel < 0 ? R.drawable.ic_qs_wifi_no_network - : WifiIcons.QS_WIFI_SIGNAL_STRENGTH[mDemoInetCondition][mDemoWifiLevel]; - mQSPhoneSignalIconId = mDemoMobileLevel < 0 ? R.drawable.ic_qs_signal_no_signal : - TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[mDemoInetCondition][mDemoMobileLevel]; - mQSDataTypeIconId = mDemoQSDataTypeIconId; - } - - if (DEBUG) { - Log.d(TAG, "refreshViews connected={" - + (mWifiConnected?" wifi":"") - + (mDataConnected?" data":"") - + " } level=" - + ((mSignalStrength == null)?"??":Integer.toString(mSignalStrength.getLevel())) - + " combinedSignalIconId=0x" - + Integer.toHexString(combinedSignalIconId) - + "/" + getResourceName(combinedSignalIconId) - + " mobileLabel=" + mobileLabel - + " wifiLabel=" + wifiLabel - + " emergencyOnly=" + emergencyOnly - + " combinedLabel=" + combinedLabel - + " mAirplaneMode=" + mAirplaneMode - + " mDataActivity=" + mDataActivity - + " mPhoneSignalIconId=0x" + Integer.toHexString(mPhoneSignalIconId) - + " mQSPhoneSignalIconId=0x" + Integer.toHexString(mQSPhoneSignalIconId) - + " mDataDirectionIconId=0x" + Integer.toHexString(mDataDirectionIconId) - + " mDataSignalIconId=0x" + Integer.toHexString(mDataSignalIconId) - + " mDataTypeIconId=0x" + Integer.toHexString(mDataTypeIconId) - + " mQSDataTypeIconId=0x" + Integer.toHexString(mQSDataTypeIconId) - + " mWifiIconId=0x" + Integer.toHexString(mWifiIconId) - + " mQSWifiIconId=0x" + Integer.toHexString(mQSWifiIconId) - + " mBluetoothTetherIconId=0x" + Integer.toHexString(mBluetoothTetherIconId)); - } + static class MobileState extends SignalController.State { + String networkName; + boolean noSim; + boolean dataConnected; + boolean isEmergency; + boolean airplaneMode; + int inetForNetwork; - // update QS - for (NetworkSignalChangedCallback cb : mSignalsChangedCallbacks) { - notifySignalsChangedCallbacks(cb); - } - - if (mLastPhoneSignalIconId != mPhoneSignalIconId - || mLastWifiIconId != mWifiIconId - || mLastInetCondition != mInetCondition - || mLastWimaxIconId != mWimaxIconId - || mLastDataTypeIconId != mDataTypeIconId - || mLastAirplaneMode != mAirplaneMode - || mLastLocale != mLocale - || mLastConnectedNetworkType != mConnectedNetworkType) - { - // NB: the mLast*s will be updated later - for (SignalCluster cluster : mSignalClusters) { - refreshSignalCluster(cluster); + @Override + public void copyFrom(State s) { + MobileState state = (MobileState) s; + noSim = state.noSim; + networkName = state.networkName; + dataConnected = state.dataConnected; + inetForNetwork = state.inetForNetwork; + isEmergency = state.isEmergency; + airplaneMode = state.airplaneMode; + super.copyFrom(s); } - } - if (mLastAirplaneMode != mAirplaneMode) { - mLastAirplaneMode = mAirplaneMode; - } + @Override + protected void toString(StringBuilder builder) { + builder.append("noSim=").append(noSim).append(','); + builder.append("networkName=").append(networkName).append(','); + builder.append("dataConnected=").append(dataConnected).append(','); + builder.append("inetForNetwork=").append(inetForNetwork).append(','); + builder.append("isEmergency=").append(isEmergency).append(','); + builder.append("airplaneMode=").append(airplaneMode).append(','); + super.toString(builder); + } - if (mLastLocale != mLocale) { - mLastLocale = mLocale; + @Override + public boolean equals(Object o) { + return super.equals(o) + && Objects.equals(((MobileState) o).networkName, networkName) + && ((MobileState) o).noSim == noSim + && ((MobileState) o).dataConnected == dataConnected + && ((MobileState) o).isEmergency == isEmergency + && ((MobileState) o).airplaneMode == airplaneMode + && ((MobileState) o).inetForNetwork == inetForNetwork; + } } + } - // the phone icon on phones - if (mLastPhoneSignalIconId != mPhoneSignalIconId) { - mLastPhoneSignalIconId = mPhoneSignalIconId; + /** + * Common base class for handling signal for both wifi and mobile data. + */ + static abstract class SignalController<T extends SignalController.State, + I extends SignalController.IconGroup> { + protected final String mTag; + protected final T mCurrentState; + protected final T mLastState; + protected final int mNetworkType; + protected final Context mContext; + // The owner of the SignalController (i.e. NetworkController will maintain the following + // lists and call notifyListeners whenever the list has changed to ensure everyone + // is aware of current state. + protected final List<NetworkSignalChangedCallback> mSignalsChangedCallbacks; + protected final List<SignalCluster> mSignalClusters; + protected final NetworkControllerImpl mNetworkController; + + // Save the previous HISTORY_SIZE states for logging. + private final State[] mHistory; + // Where to copy the next state into. + private int mHistoryIndex; + + public SignalController(String tag, Context context, int type, + List<NetworkSignalChangedCallback> signalCallbacks, + List<SignalCluster> signalClusters, NetworkControllerImpl networkController) { + mTag = TAG + "::" + tag; + mNetworkController = networkController; + mNetworkType = type; + mContext = context; + mSignalsChangedCallbacks = signalCallbacks; + mSignalClusters = signalClusters; + mCurrentState = cleanState(); + mLastState = cleanState(); + if (RECORD_HISTORY) { + mHistory = new State[HISTORY_SIZE]; + for (int i = 0; i < HISTORY_SIZE; i++) { + mHistory[i] = cleanState(); + } + } } - // the data icon on phones - if (mLastDataDirectionIconId != mDataDirectionIconId) { - mLastDataDirectionIconId = mDataDirectionIconId; + public T getState() { + return mCurrentState; } - // the wifi icon on phones - if (mLastWifiIconId != mWifiIconId) { - mLastWifiIconId = mWifiIconId; + public int getNetworkType() { + return mNetworkType; } - if (mLastInetCondition != mInetCondition) { - mLastInetCondition = mInetCondition; + public void setInetCondition(int inetCondition) { + mCurrentState.inetCondition = inetCondition; + notifyListenersIfNecessary(); } - if (mLastConnectedNetworkType != mConnectedNetworkType) { - mLastConnectedNetworkType = mConnectedNetworkType; + // @VisibleForDemoMode + /** + * Used at the end of demo mode to clear out any ugly state that it has created. + * Since we haven't had any callbacks, then isDirty will not have been triggered, + * so we can just take the last good state directly from there. + */ + void resetLastState() { + mCurrentState.copyFrom(mLastState); } - // the wimax icon on phones - if (mLastWimaxIconId != mWimaxIconId) { - mLastWimaxIconId = mWimaxIconId; - } - // the combined data signal icon - if (mLastCombinedSignalIconId != combinedSignalIconId) { - mLastCombinedSignalIconId = combinedSignalIconId; + /** + * Determines if the state of this signal controller has changed and + * needs to trigger callbacks related to it. + */ + public boolean isDirty() { + if (!mLastState.equals(mCurrentState)) { + if (DEBUG) { + Log.d(mTag, "Change in state from: " + mLastState + "\n" + + "\tto: " + mCurrentState); + } + return true; + } + return false; } - // the data network type overlay - if (mLastDataTypeIconId != mDataTypeIconId) { - mLastDataTypeIconId = mDataTypeIconId; + public void saveLastState() { + if (RECORD_HISTORY) { + recordLast(); + } + // Updates the current time. + mCurrentState.time = System.currentTimeMillis(); + mLastState.copyFrom(mCurrentState); + } + + /** + * Gets the signal icon for QS based on current state of connected, enabled, and level. + */ + public int getQsCurrentIconId() { + if (mCurrentState.connected) { + return getIcons().mQsIcons[mCurrentState.inetCondition][mCurrentState.level]; + } else if (mCurrentState.enabled) { + return getIcons().mQsDiscState; + } else { + return getIcons().mQsNullState; + } } - // the combinedLabel in the notification panel - if (!mLastCombinedLabel.equals(combinedLabel)) { - mLastCombinedLabel = combinedLabel; - N = mCombinedLabelViews.size(); - for (int i=0; i<N; i++) { - TextView v = mCombinedLabelViews.get(i); - v.setText(combinedLabel); + /** + * Gets the signal icon for SB based on current state of connected, enabled, and level. + */ + public int getCurrentIconId() { + if (mCurrentState.connected) { + return getIcons().mSbIcons[mCurrentState.inetCondition][mCurrentState.level]; + } else if (mCurrentState.enabled) { + return getIcons().mSbDiscState; + } else { + return getIcons().mSbNullState; } } - // wifi label - N = mWifiLabelViews.size(); - for (int i=0; i<N; i++) { - TextView v = mWifiLabelViews.get(i); - v.setText(wifiLabel); - if ("".equals(wifiLabel)) { - v.setVisibility(View.GONE); + /** + * Gets the content description for the signal based on current state of connected and + * level. + */ + public int getContentDescription() { + if (mCurrentState.connected) { + return getIcons().mContentDesc[mCurrentState.level]; } else { - v.setVisibility(View.VISIBLE); + return getIcons().mDiscContentDesc; } } - // mobile label - N = mMobileLabelViews.size(); - for (int i=0; i<N; i++) { - TextView v = mMobileLabelViews.get(i); - v.setText(mobileLabel); - if ("".equals(mobileLabel)) { - v.setVisibility(View.GONE); - } else { - v.setVisibility(View.VISIBLE); + protected void notifyListenersIfNecessary() { + if (isDirty()) { + saveLastState(); + notifyListeners(); + mNetworkController.refreshCarrierLabel(); } } - // e-call label - N = mEmergencyViews.size(); - for (int i=0; i<N; i++) { - StatusBarHeaderView v = mEmergencyViews.get(i); - v.setShowEmergencyCallsOnly(emergencyOnly); + /** + * Returns the resource if resId is not 0, and an empty string otherwise. + */ + protected String getStringIfExists(int resId) { + return resId != 0 ? mContext.getString(resId) : ""; } - } - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("NetworkController state:"); - pw.println(String.format(" %s network type %d (%s)", - mConnected?"CONNECTED":"DISCONNECTED", - mConnectedNetworkType, mConnectedNetworkTypeName)); - pw.println(" - telephony ------"); - pw.print(" hasVoiceCallingFeature()="); - pw.println(hasVoiceCallingFeature()); - pw.print(" hasService()="); - pw.println(hasService()); - pw.print(" mHspaDataDistinguishable="); - pw.println(mHspaDataDistinguishable); - pw.print(" mDataConnected="); - pw.println(mDataConnected); - pw.print(" mSimState="); - pw.println(mSimState); - pw.print(" mPhoneState="); - pw.println(mPhoneState); - pw.print(" mDataState="); - pw.println(mDataState); - pw.print(" mDataActivity="); - pw.println(mDataActivity); - pw.print(" mDataNetType="); - pw.print(mDataNetType); - pw.print("/"); - pw.println(TelephonyManager.getNetworkTypeName(mDataNetType)); - pw.print(" mServiceState="); - pw.println(mServiceState); - pw.print(" mSignalStrength="); - pw.println(mSignalStrength); - pw.print(" mLastSignalLevel="); - pw.println(mLastSignalLevel); - pw.print(" mNetworkName="); - pw.println(mNetworkName); - pw.print(" mNetworkNameDefault="); - pw.println(mNetworkNameDefault); - pw.print(" mNetworkNameSeparator="); - pw.println(mNetworkNameSeparator.replace("\n","\\n")); - pw.print(" mPhoneSignalIconId=0x"); - pw.print(Integer.toHexString(mPhoneSignalIconId)); - pw.print("/"); - pw.print(" mQSPhoneSignalIconId=0x"); - pw.print(Integer.toHexString(mQSPhoneSignalIconId)); - pw.print("/"); - pw.println(getResourceName(mPhoneSignalIconId)); - pw.print(" mDataDirectionIconId="); - pw.print(Integer.toHexString(mDataDirectionIconId)); - pw.print("/"); - pw.println(getResourceName(mDataDirectionIconId)); - pw.print(" mDataSignalIconId="); - pw.print(Integer.toHexString(mDataSignalIconId)); - pw.print("/"); - pw.println(getResourceName(mDataSignalIconId)); - pw.print(" mDataTypeIconId="); - pw.print(Integer.toHexString(mDataTypeIconId)); - pw.print("/"); - pw.println(getResourceName(mDataTypeIconId)); - pw.print(" mQSDataTypeIconId="); - pw.print(Integer.toHexString(mQSDataTypeIconId)); - pw.print("/"); - pw.println(getResourceName(mQSDataTypeIconId)); - - pw.println(" - wifi ------"); - pw.print(" mWifiEnabled="); - pw.println(mWifiEnabled); - pw.print(" mWifiConnected="); - pw.println(mWifiConnected); - pw.print(" mWifiRssi="); - pw.println(mWifiRssi); - pw.print(" mWifiLevel="); - pw.println(mWifiLevel); - pw.print(" mWifiSsid="); - pw.println(mWifiSsid); - pw.println(String.format(" mWifiIconId=0x%08x/%s", - mWifiIconId, getResourceName(mWifiIconId))); - pw.println(String.format(" mQSWifiIconId=0x%08x/%s", - mQSWifiIconId, getResourceName(mQSWifiIconId))); - pw.print(" mWifiActivity="); - pw.println(mWifiActivity); - - if (mWimaxSupported) { - pw.println(" - wimax ------"); - pw.print(" mIsWimaxEnabled="); pw.println(mIsWimaxEnabled); - pw.print(" mWimaxConnected="); pw.println(mWimaxConnected); - pw.print(" mWimaxIdle="); pw.println(mWimaxIdle); - pw.println(String.format(" mWimaxIconId=0x%08x/%s", - mWimaxIconId, getResourceName(mWimaxIconId))); - pw.println(String.format(" mWimaxSignal=%d", mWimaxSignal)); - pw.println(String.format(" mWimaxState=%d", mWimaxState)); - pw.println(String.format(" mWimaxExtraState=%d", mWimaxExtraState)); + protected I getIcons() { + return (I) mCurrentState.iconGroup; } - pw.println(" - Bluetooth ----"); - pw.print(" mBtReverseTethered="); - pw.println(mBluetoothTethered); + /** + * Saves the last state of any changes, so we can log the current + * and last value of any state data. + */ + protected void recordLast() { + mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)].copyFrom(mLastState); + } - pw.println(" - connectivity ------"); - pw.print(" mInetCondition="); - pw.println(mInetCondition); + public void dump(PrintWriter pw) { + pw.println(" - " + mTag + " -----"); + pw.println(" Current State: " + mCurrentState); + if (RECORD_HISTORY) { + // Count up the states that actually contain time stamps, and only display those. + int size = 0; + for (int i = 0; i < HISTORY_SIZE; i++) { + if (mHistory[i].time != 0) size++; + } + // Print out the previous states in ordered number. + for (int i = mHistoryIndex + HISTORY_SIZE - 1; + i >= mHistoryIndex + HISTORY_SIZE - size; i--) { + pw.println(" Previous State(" + (mHistoryIndex + HISTORY_SIZE - i) + ": " + + mHistory[i & (HISTORY_SIZE - 1)]); + } + } + } - pw.println(" - icons ------"); - pw.print(" mLastPhoneSignalIconId=0x"); - pw.print(Integer.toHexString(mLastPhoneSignalIconId)); - pw.print("/"); - pw.println(getResourceName(mLastPhoneSignalIconId)); - pw.print(" mLastDataDirectionIconId=0x"); - pw.print(Integer.toHexString(mLastDataDirectionIconId)); - pw.print("/"); - pw.println(getResourceName(mLastDataDirectionIconId)); - pw.print(" mLastWifiIconId=0x"); - pw.print(Integer.toHexString(mLastWifiIconId)); - pw.print("/"); - pw.println(getResourceName(mLastWifiIconId)); - pw.print(" mLastCombinedSignalIconId=0x"); - pw.print(Integer.toHexString(mLastCombinedSignalIconId)); - pw.print("/"); - pw.println(getResourceName(mLastCombinedSignalIconId)); - pw.print(" mLastDataTypeIconId=0x"); - pw.print(Integer.toHexString(mLastDataTypeIconId)); - pw.print("/"); - pw.println(getResourceName(mLastDataTypeIconId)); - pw.print(" mLastCombinedLabel="); - pw.print(mLastCombinedLabel); - pw.println(""); - } + /** + * Trigger callbacks based on current state. The callbacks should be completely + * based on current state, and only need to be called in the scenario where + * mCurrentState != mLastState. + */ + public abstract void notifyListeners(); + + /** + * Generate a blank T. + */ + public abstract T cleanState(); + + /* + * Holds icons for a given state. Arrays are generally indexed as inet + * state (full connectivity or not) first, and second dimension as + * signal strength. + */ + static class IconGroup { + final int[][] mSbIcons; + final int[][] mQsIcons; + final int[] mContentDesc; + final int mSbNullState; + final int mQsNullState; + final int mSbDiscState; + final int mQsDiscState; + final int mDiscContentDesc; + // For logging. + final String mName; + + public IconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc, + int sbNullState, int qsNullState, int sbDiscState, int qsDiscState, + int discContentDesc) { + mName = name; + mSbIcons = sbIcons; + mQsIcons = qsIcons; + mContentDesc = contentDesc; + mSbNullState = sbNullState; + mQsNullState = qsNullState; + mSbDiscState = sbDiscState; + mQsDiscState = qsDiscState; + mDiscContentDesc = discContentDesc; + } - private String getResourceName(int resId) { - if (resId != 0) { - final Resources res = mContext.getResources(); - try { - return res.getResourceName(resId); - } catch (android.content.res.Resources.NotFoundException ex) { - return "(unknown)"; + @Override + public String toString() { + return "IconGroup(" + mName + ")"; } - } else { - return "(null)"; } - } - private boolean mDemoMode; - private int mDemoInetCondition; - private int mDemoWifiLevel; - private int mDemoDataTypeIconId; - private int mDemoQSDataTypeIconId; - private int mDemoMobileLevel; - - @Override - public void dispatchDemoCommand(String command, Bundle args) { - if (!mDemoMode && command.equals(COMMAND_ENTER)) { - mDemoMode = true; - mDemoWifiLevel = mWifiLevel; - mDemoInetCondition = mInetCondition; - mDemoDataTypeIconId = mDataTypeIconId; - mDemoQSDataTypeIconId = mQSDataTypeIconId; - mDemoMobileLevel = mLastSignalLevel; - } else if (mDemoMode && command.equals(COMMAND_EXIT)) { - mDemoMode = false; - for (SignalCluster cluster : mSignalClusters) { - refreshSignalCluster(cluster); + static class State { + boolean connected; + boolean enabled; + boolean activityIn; + boolean activityOut; + int level; + IconGroup iconGroup; + int inetCondition; + int rssi; // Only for logging. + + // Not used for comparison, just used for logging. + long time; + + public void copyFrom(State state) { + connected = state.connected; + enabled = state.enabled; + level = state.level; + iconGroup = state.iconGroup; + inetCondition = state.inetCondition; + activityIn = state.activityIn; + activityOut = state.activityOut; + rssi = state.rssi; + time = state.time; } - refreshViews(); - } else if (mDemoMode && command.equals(COMMAND_NETWORK)) { - String airplane = args.getString("airplane"); - if (airplane != null) { - boolean show = airplane.equals("show"); - for (SignalCluster cluster : mSignalClusters) { - cluster.setIsAirplaneMode(show, TelephonyIcons.FLIGHT_MODE_ICON); + + @Override + public String toString() { + if (time != 0) { + StringBuilder builder = new StringBuilder(); + toString(builder); + return builder.toString(); + } else { + return "Empty " + getClass().getSimpleName(); } } - String fully = args.getString("fully"); - if (fully != null) { - mDemoInetCondition = Boolean.parseBoolean(fully) ? 1 : 0; - } - String wifi = args.getString("wifi"); - if (wifi != null) { - boolean show = wifi.equals("show"); - String level = args.getString("level"); - if (level != null) { - mDemoWifiLevel = level.equals("null") ? -1 - : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1); - } - int iconId = mDemoWifiLevel < 0 ? R.drawable.stat_sys_wifi_signal_null - : WifiIcons.WIFI_SIGNAL_STRENGTH[mDemoInetCondition][mDemoWifiLevel]; - for (SignalCluster cluster : mSignalClusters) { - cluster.setWifiIndicators( - show, - iconId, - "Demo"); - } - refreshViews(); + + protected void toString(StringBuilder builder) { + builder.append("connected=").append(connected).append(',') + .append("enabled=").append(enabled).append(',') + .append("level=").append(level).append(',') + .append("inetCondition=").append(inetCondition).append(',') + .append("iconGroup=").append(iconGroup).append(',') + .append("activityIn=").append(activityIn).append(',') + .append("activityOut=").append(activityOut).append(',') + .append("rssi=").append(rssi).append(',') + .append("lastModified=").append(DateFormat.format("MM-dd hh:mm:ss", time)); } - String mobile = args.getString("mobile"); - if (mobile != null) { - boolean show = mobile.equals("show"); - String datatype = args.getString("datatype"); - if (datatype != null) { - mDemoDataTypeIconId = - datatype.equals("1x") ? TelephonyIcons.ICON_1X : - datatype.equals("3g") ? TelephonyIcons.ICON_3G : - datatype.equals("4g") ? TelephonyIcons.ICON_4G : - datatype.equals("e") ? R.drawable.stat_sys_data_fully_connected_e : - datatype.equals("g") ? R.drawable.stat_sys_data_fully_connected_g : - datatype.equals("h") ? R.drawable.stat_sys_data_fully_connected_h : - datatype.equals("lte") ? TelephonyIcons.ICON_LTE : - datatype.equals("roam") ? TelephonyIcons.ROAMING_ICON : - 0; - mDemoQSDataTypeIconId = - datatype.equals("1x") ? TelephonyIcons.QS_ICON_1X : - datatype.equals("3g") ? TelephonyIcons.QS_ICON_3G : - datatype.equals("4g") ? TelephonyIcons.QS_ICON_4G : - datatype.equals("e") ? R.drawable.ic_qs_signal_e : - datatype.equals("g") ? R.drawable.ic_qs_signal_g : - datatype.equals("h") ? R.drawable.ic_qs_signal_h : - datatype.equals("lte") ? TelephonyIcons.QS_ICON_LTE : - datatype.equals("roam") ? R.drawable.ic_qs_signal_r : - 0; - } - int[][] icons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH; - String level = args.getString("level"); - if (level != null) { - mDemoMobileLevel = level.equals("null") ? -1 - : Math.min(Integer.parseInt(level), icons[0].length - 1); - } - int iconId = mDemoMobileLevel < 0 ? R.drawable.stat_sys_signal_null : - icons[mDemoInetCondition][mDemoMobileLevel]; - for (SignalCluster cluster : mSignalClusters) { - cluster.setMobileDataIndicators( - show, - iconId, - mDemoDataTypeIconId, - "Demo", - "Demo", - isTypeIconWide(mDemoDataTypeIconId)); + + @Override + public boolean equals(Object o) { + if (!o.getClass().equals(getClass())) { + return false; } - refreshViews(); + State other = (State) o; + return other.connected == connected + && other.enabled == enabled + && other.level == level + && other.inetCondition == inetCondition + && other.iconGroup == iconGroup + && other.activityIn == activityIn + && other.activityOut == activityOut + && other.rssi == rssi; } } } + + public interface SignalCluster { + void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription); + + void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon, + String contentDescription, String typeContentDescription, boolean isTypeIconWide); + + void setIsAirplaneMode(boolean is, int airplaneIcon, int contentDescription); + } + + public interface EmergencyListener { + void setEmergencyCallsOnly(boolean emergencyOnly); + } + + public interface CarrierLabelListener { + void setCarrierLabel(String label); + } + + @VisibleForTesting + static class Config { + boolean showAtLeastThreeGees = false; + boolean alwaysShowCdmaRssi = false; + boolean show4gForLte = false; + boolean hspaDataDistinguishable; + + static Config readConfig(Context context) { + Config config = new Config(); + Resources res = context.getResources(); + + config.showAtLeastThreeGees = res.getBoolean(R.bool.config_showMin3G); + config.alwaysShowCdmaRssi = + res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi); + config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE); + config.hspaDataDistinguishable = + res.getBoolean(R.bool.config_hspa_data_distinguishable); + return config; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java index 1f2b918..4091619 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java @@ -17,11 +17,16 @@ package com.android.systemui.statusbar.policy; import com.android.systemui.R; +import com.android.systemui.statusbar.policy.NetworkControllerImpl.MobileSignalController.MobileIconGroup; class TelephonyIcons { //***** Signal strength icons + static final int TELEPHONY_NUM_LEVELS = 5; + //GSM/UMTS + static final int TELEPHONY_NO_NETWORK = R.drawable.stat_sys_signal_null; + static final int[][] TELEPHONY_SIGNAL_STRENGTH = { { R.drawable.stat_sys_signal_0, R.drawable.stat_sys_signal_1, @@ -35,6 +40,8 @@ class TelephonyIcons { R.drawable.stat_sys_signal_4_fully } }; + static final int QS_TELEPHONY_NO_NETWORK = R.drawable.ic_qs_signal_no_signal; + static final int[][] QS_TELEPHONY_SIGNAL_STRENGTH = { { R.drawable.ic_qs_signal_0, R.drawable.ic_qs_signal_1, @@ -66,8 +73,6 @@ class TelephonyIcons { R.drawable.ic_qs_signal_r }; - static final int[][] DATA_SIGNAL_STRENGTH = TELEPHONY_SIGNAL_STRENGTH; - //***** Data connection icons //GSM/UMTS @@ -191,6 +196,9 @@ class TelephonyIcons { static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_airplane_mode; static final int ROAMING_ICON = R.drawable.stat_sys_data_fully_connected_roam; static final int ICON_LTE = R.drawable.stat_sys_data_fully_connected_lte; + static final int ICON_G = R.drawable.stat_sys_data_fully_connected_g; + static final int ICON_E = R.drawable.stat_sys_data_fully_connected_e; + static final int ICON_H = R.drawable.stat_sys_data_fully_connected_h; static final int ICON_3G = R.drawable.stat_sys_data_fully_connected_3g; static final int ICON_4G = R.drawable.stat_sys_data_fully_connected_4g; static final int ICON_1X = R.drawable.stat_sys_data_fully_connected_1x; @@ -199,5 +207,137 @@ class TelephonyIcons { static final int QS_ICON_3G = R.drawable.ic_qs_signal_3g; static final int QS_ICON_4G = R.drawable.ic_qs_signal_4g; static final int QS_ICON_1X = R.drawable.ic_qs_signal_1x; + + static final MobileIconGroup THREE_G = new MobileIconGroup( + "3G", + TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH, + TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH, + 0, 0, + TelephonyIcons.TELEPHONY_NO_NETWORK, + TelephonyIcons.QS_TELEPHONY_NO_NETWORK, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0], + R.string.accessibility_data_connection_3g, + TelephonyIcons.ICON_3G, + true, + TelephonyIcons.QS_DATA_3G + ); + + static final MobileIconGroup UNKNOWN = new MobileIconGroup( + "Unknown", + TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH, + TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH, + 0, 0, + TelephonyIcons.TELEPHONY_NO_NETWORK, + TelephonyIcons.QS_TELEPHONY_NO_NETWORK, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0], + 0, 0, false, new int[2] + ); + + static final MobileIconGroup E = new MobileIconGroup( + "E", + TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH, + TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH, + 0, 0, + TelephonyIcons.TELEPHONY_NO_NETWORK, + TelephonyIcons.QS_TELEPHONY_NO_NETWORK, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0], + R.string.accessibility_data_connection_edge, + TelephonyIcons.ICON_E, + false, + TelephonyIcons.QS_DATA_E + ); + + static final MobileIconGroup ONE_X = new MobileIconGroup( + "1X", + TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH, + TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH, + 0, 0, + TelephonyIcons.TELEPHONY_NO_NETWORK, + TelephonyIcons.QS_TELEPHONY_NO_NETWORK, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0], + R.string.accessibility_data_connection_cdma, + TelephonyIcons.ICON_1X, + true, + TelephonyIcons.QS_DATA_1X + ); + + static final MobileIconGroup G = new MobileIconGroup( + "G", + TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH, + TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH, + 0, 0, + TelephonyIcons.TELEPHONY_NO_NETWORK, + TelephonyIcons.QS_TELEPHONY_NO_NETWORK, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0], + R.string.accessibility_data_connection_gprs, + TelephonyIcons.ICON_G, + false, + TelephonyIcons.QS_DATA_G + ); + + static final MobileIconGroup H = new MobileIconGroup( + "H", + TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH, + TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH, + 0, 0, + TelephonyIcons.TELEPHONY_NO_NETWORK, + TelephonyIcons.QS_TELEPHONY_NO_NETWORK, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0], + R.string.accessibility_data_connection_3_5g, + TelephonyIcons.ICON_H, + false, + TelephonyIcons.QS_DATA_H + ); + + static final MobileIconGroup FOUR_G = new MobileIconGroup( + "4G", + TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH, + TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH, + 0, 0, + TelephonyIcons.TELEPHONY_NO_NETWORK, + TelephonyIcons.QS_TELEPHONY_NO_NETWORK, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0], + R.string.accessibility_data_connection_4g, + TelephonyIcons.ICON_4G, + true, + TelephonyIcons.QS_DATA_4G + ); + + static final MobileIconGroup LTE = new MobileIconGroup( + "LTE", + TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH, + TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH, + 0, 0, + TelephonyIcons.TELEPHONY_NO_NETWORK, + TelephonyIcons.QS_TELEPHONY_NO_NETWORK, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0], + R.string.accessibility_data_connection_lte, + TelephonyIcons.ICON_LTE, + true, + TelephonyIcons.QS_DATA_LTE + ); + + static final MobileIconGroup ROAMING = new MobileIconGroup( + "Roaming", + TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING, + TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH, + 0, 0, + TelephonyIcons.TELEPHONY_NO_NETWORK, + TelephonyIcons.QS_TELEPHONY_NO_NETWORK, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0], + R.string.accessibility_data_connection_roaming, + TelephonyIcons.ROAMING_ICON, + false, + TelephonyIcons.QS_DATA_R + ); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java index 49af979..c56646f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java @@ -45,5 +45,8 @@ class WifiIcons { R.drawable.ic_qs_wifi_full_4 } }; + static final int QS_WIFI_NO_NETWORK = R.drawable.ic_qs_wifi_no_network; + static final int WIFI_NO_NETWORK = R.drawable.stat_sys_wifi_signal_null; + static final int WIFI_LEVEL_COUNT = WIFI_SIGNAL_STRENGTH[0].length; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WimaxIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WimaxIcons.java deleted file mode 100644 index 4877828..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WimaxIcons.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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. - * 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.statusbar.policy; - -import com.android.systemui.statusbar.policy.TelephonyIcons; - -class WimaxIcons { - static final int[][] WIMAX_SIGNAL_STRENGTH = TelephonyIcons.DATA_SIGNAL_STRENGTH; - - static final int WIMAX_DISCONNECTED = WIMAX_SIGNAL_STRENGTH[0][0]; - - static final int WIMAX_IDLE = WIMAX_DISCONNECTED; // XXX: unclear if we need a different icon -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java index 3c93b19..3d4cda6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java @@ -33,6 +33,7 @@ public class AnimationFilter { boolean animateHideSensitive; boolean hasDelays; boolean hasGoToFullShadeEvent; + boolean hasDarkEvent; public AnimationFilter animateAlpha() { animateAlpha = true; @@ -98,6 +99,10 @@ public class AnimationFilter { NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE) { hasGoToFullShadeEvent = true; } + if (events.get(i).animationType == + NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_DARK) { + hasDarkEvent = true; + } } } @@ -126,5 +131,6 @@ public class AnimationFilter { animateHideSensitive = false; hasDelays = false; hasGoToFullShadeEvent = false; + hasDarkEvent = false; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index e63be97..c5f1161 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -2455,7 +2455,8 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_DARK new AnimationFilter() - .animateDark(), + .animateDark() + .hasDelays(), // ANIMATION_TYPE_GO_TO_FULL_SHADE new AnimationFilter() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java index 4611370..0b1ce8f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java @@ -154,7 +154,7 @@ public class StackScrollState { child.setDimmed(state.dimmed, false /* animate */); // apply dark - child.setDark(state.dark, false /* animate */); + child.setDark(state.dark, false /* animate */, 0 /* delay */); // apply hiding sensitive child.setHideSensitive( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index a56440c..05077bf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -46,6 +46,7 @@ public class StackStateAnimator { public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80; public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32; public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48; + public static final int ANIMATION_DELAY_PER_ELEMENT_DARK = 24; private static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2; private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag; @@ -161,11 +162,12 @@ public class StackStateAnimator { boolean scaleChanging = child.getScaleX() != viewState.scale; boolean alphaChanging = alpha != child.getAlpha(); boolean heightChanging = viewState.height != child.getActualHeight(); + boolean darkChanging = viewState.dark != child.isDark(); boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount(); boolean wasAdded = mNewAddChildren.contains(child); boolean hasDelays = mAnimationFilter.hasDelays; boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging || - alphaChanging || heightChanging || topInsetChanging; + alphaChanging || heightChanging || topInsetChanging || darkChanging; boolean noAnimation = wasAdded; long delay = 0; long duration = mCurrentLength; @@ -242,7 +244,7 @@ public class StackStateAnimator { && !noAnimation); // start dark animation - child.setDark(viewState.dark, mAnimationFilter.animateDark && !noAnimation); + child.setDark(viewState.dark, mAnimationFilter.animateDark && !noAnimation, delay); // apply speed bump state child.setBelowSpeedBump(viewState.belowSpeedBump); @@ -262,6 +264,9 @@ public class StackStateAnimator { private long calculateChildAnimationDelay(StackScrollState.ViewState viewState, StackScrollState finalState) { + if (mAnimationFilter.hasDarkEvent) { + return calculateDelayDark(viewState); + } if (mAnimationFilter.hasGoToFullShadeEvent) { return calculateDelayGoToFullShade(viewState); } @@ -309,6 +314,10 @@ public class StackStateAnimator { return minDelay; } + private long calculateDelayDark(StackScrollState.ViewState viewState) { + return viewState.notGoneIndex * ANIMATION_DELAY_PER_ELEMENT_DARK; + } + private long calculateDelayGoToFullShade(StackScrollState.ViewState viewState) { float index = viewState.notGoneIndex; index = (float) Math.pow(index, 0.7f); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java index 31adc95..e3f8f3d 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java @@ -21,4 +21,5 @@ import com.android.systemui.statusbar.policy.ZenModeController; public interface VolumeComponent extends DemoMode { ZenModeController getZenController(); + void dismissNow(); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java index f12053c..351911c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java @@ -793,7 +793,7 @@ public class VolumePanel extends Handler implements DemoMode { sc.icon.setAlpha(mDisabledAlpha); sc.icon.setClickable(false); } else if (fixedVolume || - (sc.streamType != mAudioManager.getMasterStreamType() && muted) || + (sc.streamType != mAudioManager.getMasterStreamType() && !isRinger && muted) || (sSafetyWarning != null)) { sc.seekbarView.setEnabled(false); } else { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java index 5232a17..7102c2a 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java @@ -178,7 +178,7 @@ public class VolumeUI extends SystemUI { @Override public void dismiss() throws RemoteException { - mPanel.postDismiss(0); + dismissNow(); } @Override @@ -190,6 +190,11 @@ public class VolumeUI extends SystemUI { public void dispatchDemoCommand(String command, Bundle args) { mPanel.dispatchDemoCommand(command, args); } + + @Override + public void dismissNow() { + mPanel.postDismiss(0); + } } private final class RemoteVolumeController extends IRemoteVolumeController.Stub { diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk index 8b87522..5a90324 100644 --- a/packages/SystemUI/tests/Android.mk +++ b/packages/SystemUI/tests/Android.mk @@ -17,14 +17,20 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests -LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages com.android.systemui:com.android.keyguard +LOCAL_SRC_FILES := $(call all-java-files-under, src) \ + $(call all-java-files-under, ../src) \ + src/com/android/systemui/EventLogTags.logtags + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \ + frameworks/base/packages/SystemUI/res \ + frameworks/base/packages/Keyguard/res LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common LOCAL_PACKAGE_NAME := SystemUITests -LOCAL_INSTRUMENTATION_FOR := SystemUI -LOCAL_STATIC_JAVA_LIBRARIES := mockito-target +LOCAL_STATIC_JAVA_LIBRARIES := mockito-target Keyguard # sign this with platform cert, so this test is allowed to inject key events into # UI it doesn't own. This is necessary to allow screenshots to be taken diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index 1d319cf..e52806d 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -25,7 +25,7 @@ </application> <instrumentation android:name="android.test.InstrumentationTestRunner" - android:targetPackage="com.android.systemui" + android:targetPackage="com.android.systemui.tests" android:label="Tests for SystemUI"> </instrumentation> </manifest> diff --git a/packages/SystemUI/tests/src/com/android/systemui/EventLogTags.logtags b/packages/SystemUI/tests/src/com/android/systemui/EventLogTags.logtags new file mode 120000 index 0000000..2f243d7 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/EventLogTags.logtags @@ -0,0 +1 @@ +../../../../../src/com/android/systemui/EventLogTags.logtags
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotStubActivity.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotStubActivity.java index 2935373..784d035 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotStubActivity.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotStubActivity.java @@ -15,7 +15,7 @@ */ package com.android.systemui.screenshot; -import com.android.systemui.tests.R; +import com.android.systemui.R; import android.app.Activity; import android.os.Bundle; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index 97605ea..49fe1e3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -17,6 +17,7 @@ import android.util.Log; import com.android.internal.telephony.cdma.EriInfo; import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; +import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config; import com.android.systemui.statusbar.policy.NetworkControllerImpl.SignalCluster; import org.mockito.ArgumentCaptor; @@ -44,6 +45,7 @@ public class NetworkControllerBaseTest extends AndroidTestCase { protected ConnectivityManager mMockCm; protected WifiManager mMockWm; protected TelephonyManager mMockTm; + protected Config mConfig; @Override protected void setUp() throws Exception { @@ -59,15 +61,19 @@ public class NetworkControllerBaseTest extends AndroidTestCase { mSignalStrength = mock(SignalStrength.class); mServiceState = mock(ServiceState.class); - mSignalCluster = mock(SignalCluster.class); - mNetworkSignalChangedCallback = mock(NetworkSignalChangedCallback.class); - mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm); + mConfig = new Config(); + mConfig.hspaDataDistinguishable = true; + mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, + mConfig, mock(AccessPointControllerImpl.class), + mock(MobileDataControllerImpl.class)); setupNetworkController(); } protected void setupNetworkController() { - mPhoneStateListener = mNetworkController.mPhoneStateListener; + mPhoneStateListener = mNetworkController.mMobileSignalController.mPhoneStateListener; + mSignalCluster = mock(SignalCluster.class); + mNetworkSignalChangedCallback = mock(NetworkSignalChangedCallback.class); mNetworkController.addSignalCluster(mSignalCluster); mNetworkController.addNetworkSignalChangedCallback(mNetworkSignalChangedCallback); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java index ed76ae5..bb2ff7c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java @@ -1,5 +1,7 @@ package com.android.systemui.statusbar.policy; +import static org.mockito.Mockito.mock; + import android.net.ConnectivityManager; import android.telephony.ServiceState; import android.telephony.SignalStrength; @@ -15,7 +17,9 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { // Turn off mobile network support. Mockito.when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false); // Create a new NetworkController as this is currently handled in constructor. - mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm); + mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, + mConfig, mock(AccessPointControllerImpl.class), + mock(MobileDataControllerImpl.class)); setupNetworkController(); verifyLastMobileDataIndicators(false, 0, 0); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java index 4ffdff2..7f0a8f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java @@ -6,8 +6,6 @@ import android.net.NetworkInfo; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import com.android.systemui.R; - import org.mockito.ArgumentCaptor; import org.mockito.Mockito; @@ -16,14 +14,10 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { private static final int MIN_RSSI = -100; private static final int MAX_RSSI = -55; - // TODO: Move this into WifiIcons, remove all R.drawable from NetworkControllerImpl. - private static final int NULL_SIGNAL = R.drawable.stat_sys_wifi_signal_null; - private static final int QS_NO_NET = R.drawable.ic_qs_wifi_no_network; - public void testWifiIcon() { String testSsid = "Test SSID"; setWifiEnabled(true); - verifyLastWifiIcon(false, NULL_SIGNAL); + verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK); setWifiState(true, testSsid); verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]); @@ -42,10 +36,10 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { String testSsid = "Test SSID"; setWifiEnabled(false); - verifyLastQsWifiIcon(false, false, 0, null); + verifyLastQsWifiIcon(false, false, WifiIcons.QS_WIFI_NO_NETWORK, null); setWifiEnabled(true); - verifyLastQsWifiIcon(true, false, QS_NO_NET, null); + verifyLastQsWifiIcon(true, false, WifiIcons.QS_WIFI_NO_NETWORK, null); setWifiState(true, testSsid); for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) { @@ -118,8 +112,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { protected void setWifiActivity(int activity) { // TODO: Not this, because this variable probably isn't sticking around. - mNetworkController.mWifiActivity = activity; - mNetworkController.refreshViews(); + mNetworkController.mWifiSignalController.setActivity(activity); } protected void setWifiLevel(int level) { |