166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi/* 266ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * Copyright (C) 2015 The Android Open Source Project 366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * 466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * Licensed under the Apache License, Version 2.0 (the "License"); 566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * you may not use this file except in compliance with the License. 666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * You may obtain a copy of the License at 766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * 866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * http://www.apache.org/licenses/LICENSE-2.0 966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * 1066ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * Unless required by applicable law or agreed to in writing, software 1166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * distributed under the License is distributed on an "AS IS" BASIS, 1266ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * See the License for the specific language governing permissions and 1466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * limitations under the License 1566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi */ 1666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 1766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggipackage com.android.systemui.statusbar.phone; 1866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 195443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggiimport android.animation.ArgbEvaluator; 2024bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggiimport android.animation.ValueAnimator; 2166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggiimport android.content.Context; 22dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggiimport android.content.res.ColorStateList; 2355e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chenimport android.content.res.Resources; 24dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggiimport android.graphics.Color; 2586905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggiimport android.graphics.Rect; 2607473cea3a6007d5e72b25d3445437a175735fa9Jason Monkimport android.graphics.drawable.Icon; 2766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggiimport android.os.Bundle; 2833ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggiimport android.os.Handler; 2924bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggiimport android.os.SystemClock; 3007473cea3a6007d5e72b25d3445437a175735fa9Jason Monkimport android.os.UserHandle; 313b230077dc53c1f83aa6e9b70585148d8193dd82Jason Monkimport android.text.TextUtils; 323b230077dc53c1f83aa6e9b70585148d8193dd82Jason Monkimport android.util.ArraySet; 3355e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chenimport android.util.TypedValue; 346d6672ffd1b6d918e64d951f8f1fd1cc441b363fJason Monkimport android.view.Gravity; 3566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggiimport android.view.View; 3666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggiimport android.view.ViewGroup; 37dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggiimport android.widget.ImageView; 3866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggiimport android.widget.LinearLayout; 3966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggiimport android.widget.TextView; 4066ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggiimport com.android.internal.statusbar.StatusBarIcon; 41dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggiimport com.android.systemui.BatteryMeterView; 4266ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggiimport com.android.systemui.FontSizeUtils; 43c0d7058b14c24cd07912f5629c26b39b7b4673d5Winsonimport com.android.systemui.Interpolators; 4466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggiimport com.android.systemui.R; 45eb04a9992ea995eaf35a9afbdab060af7bd8e775Xiaohui Chenimport com.android.systemui.SystemUIFactory; 4666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggiimport com.android.systemui.statusbar.NotificationData; 47dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggiimport com.android.systemui.statusbar.SignalClusterView; 4866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggiimport com.android.systemui.statusbar.StatusBarIconView; 495e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monkimport com.android.systemui.tuner.TunerService; 505e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monkimport com.android.systemui.tuner.TunerService.Tunable; 5166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 5266ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggiimport java.io.PrintWriter; 5366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggiimport java.util.ArrayList; 5466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 5566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi/** 5666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * Controls everything regarding the icons in the status bar and on Keyguard, including, but not 5766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * limited to: notification icons, signal cluster, additional status icons, and clock in the status 5866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * bar. 5966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi */ 6007473cea3a6007d5e72b25d3445437a175735fa9Jason Monkpublic class StatusBarIconController extends StatusBarIconList implements Tunable { 6166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 6233ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi public static final long DEFAULT_TINT_ANIMATION_DURATION = 120; 633b230077dc53c1f83aa6e9b70585148d8193dd82Jason Monk public static final String ICON_BLACKLIST = "icon_blacklist"; 6486905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi public static final int DEFAULT_ICON_TINT = Color.WHITE; 653b230077dc53c1f83aa6e9b70585148d8193dd82Jason Monk 6666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi private Context mContext; 6766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi private PhoneStatusBar mPhoneStatusBar; 6866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi private DemoStatusIcons mDemoStatusIcons; 6966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 7066ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi private LinearLayout mSystemIconArea; 7166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi private LinearLayout mStatusIcons; 72dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi private SignalClusterView mSignalCluster; 7366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi private LinearLayout mStatusIconsKeyguard; 7483092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen 7583092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen private NotificationIconAreaController mNotificationIconAreaController; 7683092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen private View mNotificationIconAreaInner; 7783092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen 78dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi private BatteryMeterView mBatteryMeterView; 793e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek private BatteryMeterView mBatteryMeterViewKeyguard; 8066ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi private TextView mClock; 8166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 8266ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi private int mIconSize; 8366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi private int mIconHPadding; 8466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 8586905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi private int mIconTint = DEFAULT_ICON_TINT; 865443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi private float mDarkIntensity; 8786905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi private final Rect mTintArea = new Rect(); 8886905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi private static final Rect sTmpRect = new Rect(); 8986905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi private static final int[] sTmpInt2 = new int[2]; 90dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi 9124bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi private boolean mTransitionPending; 9224bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi private boolean mTintChangePending; 935443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi private float mPendingDarkIntensity; 9424bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi private ValueAnimator mTintAnimator; 9524bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi 965443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi private int mDarkModeIconColorSingleTone; 975443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi private int mLightModeIconColorSingleTone; 985443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi 9933ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi private final Handler mHandler; 10033ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi private boolean mTransitionDeferring; 10133ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi private long mTransitionDeferringStartTime; 10233ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi private long mTransitionDeferringDuration; 10333ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi 1045e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk private final ArraySet<String> mIconBlacklist = new ArraySet<>(); 1053b230077dc53c1f83aa6e9b70585148d8193dd82Jason Monk 10633ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi private final Runnable mTransitionDeferringDoneRunnable = new Runnable() { 10733ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi @Override 10833ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi public void run() { 10933ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi mTransitionDeferring = false; 11033ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi } 11133ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi }; 11233ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi 11366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi public StatusBarIconController(Context context, View statusBar, View keyguardStatusBar, 11466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi PhoneStatusBar phoneStatusBar) { 115c6fe61c59c5a3a6ae691256c9afdde3820e3dd9eJason Monk super(context.getResources().getStringArray( 116c6fe61c59c5a3a6ae691256c9afdde3820e3dd9eJason Monk com.android.internal.R.array.config_statusBarIcons)); 11766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi mContext = context; 11866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi mPhoneStatusBar = phoneStatusBar; 11966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi mSystemIconArea = (LinearLayout) statusBar.findViewById(R.id.system_icon_area); 12066ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi mStatusIcons = (LinearLayout) statusBar.findViewById(R.id.statusIcons); 121dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi mSignalCluster = (SignalClusterView) statusBar.findViewById(R.id.signal_cluster); 12283092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen 123eb04a9992ea995eaf35a9afbdab060af7bd8e775Xiaohui Chen mNotificationIconAreaController = SystemUIFactory.getInstance() 124eb04a9992ea995eaf35a9afbdab060af7bd8e775Xiaohui Chen .createNotificationIconAreaController(context, phoneStatusBar); 12583092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen mNotificationIconAreaInner = 12683092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen mNotificationIconAreaController.getNotificationInnerAreaView(); 12783092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen 12883092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen ViewGroup notificationIconArea = 12983092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen (ViewGroup) statusBar.findViewById(R.id.notification_icon_area); 13083092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen notificationIconArea.addView(mNotificationIconAreaInner); 13183092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen 13266ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi mStatusIconsKeyguard = (LinearLayout) keyguardStatusBar.findViewById(R.id.statusIcons); 13355e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen 134dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi mBatteryMeterView = (BatteryMeterView) statusBar.findViewById(R.id.battery); 1353e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek mBatteryMeterViewKeyguard = (BatteryMeterView) keyguardStatusBar.findViewById(R.id.battery); 1363e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek scaleBatteryMeterViews(context); 13755e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen 13866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi mClock = (TextView) statusBar.findViewById(R.id.clock); 1395443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi mDarkModeIconColorSingleTone = context.getColor(R.color.dark_mode_icon_color_single_tone); 1405443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi mLightModeIconColorSingleTone = context.getColor(R.color.light_mode_icon_color_single_tone); 14133ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi mHandler = new Handler(); 1423e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek loadDimens(); 1433b230077dc53c1f83aa6e9b70585148d8193dd82Jason Monk 1445e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk TunerService.get(mContext).addTunable(this, ICON_BLACKLIST); 14566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 14666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 1473e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek public void setSignalCluster(SignalClusterView signalCluster) { 1483e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek mSignalCluster = signalCluster; 1493e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek } 1503e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek 15155e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen /** 1523e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek * Looks up the scale factor for status bar icons and scales the battery view by that amount. 15355e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen */ 1543e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek private void scaleBatteryMeterViews(Context context) { 15555e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen Resources res = context.getResources(); 15655e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen TypedValue typedValue = new TypedValue(); 15755e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen 15855e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen res.getValue(R.dimen.status_bar_icon_scale_factor, typedValue, true); 15955e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen float iconScaleFactor = typedValue.getFloat(); 16055e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen 1613e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek int batteryHeight = res.getDimensionPixelSize(R.dimen.status_bar_battery_icon_height); 1623e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek int batteryWidth = res.getDimensionPixelSize(R.dimen.status_bar_battery_icon_width); 1633e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek int marginBottom = res.getDimensionPixelSize(R.dimen.battery_margin_bottom); 16455e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen 16555e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen LinearLayout.LayoutParams scaledLayoutParams = new LinearLayout.LayoutParams( 16655e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen (int) (batteryWidth * iconScaleFactor), (int) (batteryHeight * iconScaleFactor)); 1673e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek scaledLayoutParams.setMarginsRelative(0, 0, 0, marginBottom); 16855e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen 16955e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen mBatteryMeterView.setLayoutParams(scaledLayoutParams); 1703e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek mBatteryMeterViewKeyguard.setLayoutParams(scaledLayoutParams); 17155e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen } 17255e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen 1735e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk @Override 1745e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk public void onTuningChanged(String key, String newValue) { 1755e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk if (!ICON_BLACKLIST.equals(key)) { 1765e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk return; 1775e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk } 1785e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk mIconBlacklist.clear(); 1795e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk mIconBlacklist.addAll(getIconBlacklist(newValue)); 1805e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk ArrayList<StatusBarIconView> views = new ArrayList<StatusBarIconView>(); 1815e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk // Get all the current views. 1825e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk for (int i = 0; i < mStatusIcons.getChildCount(); i++) { 1835e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk views.add((StatusBarIconView) mStatusIcons.getChildAt(i)); 1845e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk } 1855e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk // Remove all the icons. 1865e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk for (int i = views.size() - 1; i >= 0; i--) { 18707473cea3a6007d5e72b25d3445437a175735fa9Jason Monk removeIcon(views.get(i).getSlot()); 1885e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk } 1895e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk // Add them all back 1905e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk for (int i = 0; i < views.size(); i++) { 19107473cea3a6007d5e72b25d3445437a175735fa9Jason Monk setIcon(views.get(i).getSlot(), views.get(i).getStatusBarIcon()); 1925e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk } 19307473cea3a6007d5e72b25d3445437a175735fa9Jason Monk } 1943e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek private void loadDimens() { 19566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi mIconSize = mContext.getResources().getDimensionPixelSize( 19666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi com.android.internal.R.dimen.status_bar_icon_size); 19766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi mIconHPadding = mContext.getResources().getDimensionPixelSize( 19866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi R.dimen.status_bar_icon_padding); 1993e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek } 2003e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek 20107473cea3a6007d5e72b25d3445437a175735fa9Jason Monk private void addSystemIcon(int index, StatusBarIcon icon) { 20207473cea3a6007d5e72b25d3445437a175735fa9Jason Monk String slot = getSlot(index); 20307473cea3a6007d5e72b25d3445437a175735fa9Jason Monk int viewIndex = getViewIndex(index); 2043b230077dc53c1f83aa6e9b70585148d8193dd82Jason Monk boolean blocked = mIconBlacklist.contains(slot); 2053b230077dc53c1f83aa6e9b70585148d8193dd82Jason Monk StatusBarIconView view = new StatusBarIconView(mContext, slot, null, blocked); 20666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi view.set(icon); 20755e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen 20855e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 20955e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); 21055e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen lp.setMargins(mIconHPadding, 0, mIconHPadding, 0); 21155e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen mStatusIcons.addView(view, viewIndex, lp); 21255e8e1ee604475b05c96934a4c53688504d1b9c5Anthony Chen 2133b230077dc53c1f83aa6e9b70585148d8193dd82Jason Monk view = new StatusBarIconView(mContext, slot, null, blocked); 21466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi view.set(icon); 21566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi mStatusIconsKeyguard.addView(view, viewIndex, new LinearLayout.LayoutParams( 21666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize)); 217dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi applyIconTint(); 21866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 21966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 22007473cea3a6007d5e72b25d3445437a175735fa9Jason Monk public void setIcon(String slot, int resourceId, CharSequence contentDescription) { 22107473cea3a6007d5e72b25d3445437a175735fa9Jason Monk int index = getSlotIndex(slot); 22207473cea3a6007d5e72b25d3445437a175735fa9Jason Monk StatusBarIcon icon = getIcon(index); 22307473cea3a6007d5e72b25d3445437a175735fa9Jason Monk if (icon == null) { 22407473cea3a6007d5e72b25d3445437a175735fa9Jason Monk icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(), 22507473cea3a6007d5e72b25d3445437a175735fa9Jason Monk Icon.createWithResource(mContext, resourceId), 0, 0, contentDescription); 22607473cea3a6007d5e72b25d3445437a175735fa9Jason Monk setIcon(slot, icon); 22707473cea3a6007d5e72b25d3445437a175735fa9Jason Monk } else { 22807473cea3a6007d5e72b25d3445437a175735fa9Jason Monk icon.icon = Icon.createWithResource(mContext, resourceId); 22907473cea3a6007d5e72b25d3445437a175735fa9Jason Monk icon.contentDescription = contentDescription; 23007473cea3a6007d5e72b25d3445437a175735fa9Jason Monk handleSet(index, icon); 23107473cea3a6007d5e72b25d3445437a175735fa9Jason Monk } 23207473cea3a6007d5e72b25d3445437a175735fa9Jason Monk } 23307473cea3a6007d5e72b25d3445437a175735fa9Jason Monk 23466c89c15a0baabf001e69498dbc09903f72cc63dJason Monk public void setExternalIcon(String slot) { 23566c89c15a0baabf001e69498dbc09903f72cc63dJason Monk int viewIndex = getViewIndex(getSlotIndex(slot)); 2366d6672ffd1b6d918e64d951f8f1fd1cc441b363fJason Monk int height = mContext.getResources().getDimensionPixelSize( 2376d6672ffd1b6d918e64d951f8f1fd1cc441b363fJason Monk R.dimen.status_bar_icon_drawing_size); 23866c89c15a0baabf001e69498dbc09903f72cc63dJason Monk ImageView imageView = (ImageView) mStatusIcons.getChildAt(viewIndex); 23966c89c15a0baabf001e69498dbc09903f72cc63dJason Monk imageView.setScaleType(ImageView.ScaleType.FIT_CENTER); 24066c89c15a0baabf001e69498dbc09903f72cc63dJason Monk imageView.setAdjustViewBounds(true); 2416d6672ffd1b6d918e64d951f8f1fd1cc441b363fJason Monk setHeightAndCenter(imageView, height); 24266c89c15a0baabf001e69498dbc09903f72cc63dJason Monk imageView = (ImageView) mStatusIconsKeyguard.getChildAt(viewIndex); 24366c89c15a0baabf001e69498dbc09903f72cc63dJason Monk imageView.setScaleType(ImageView.ScaleType.FIT_CENTER); 24466c89c15a0baabf001e69498dbc09903f72cc63dJason Monk imageView.setAdjustViewBounds(true); 2456d6672ffd1b6d918e64d951f8f1fd1cc441b363fJason Monk setHeightAndCenter(imageView, height); 2466d6672ffd1b6d918e64d951f8f1fd1cc441b363fJason Monk } 2476d6672ffd1b6d918e64d951f8f1fd1cc441b363fJason Monk 2486d6672ffd1b6d918e64d951f8f1fd1cc441b363fJason Monk private void setHeightAndCenter(ImageView imageView, int height) { 2496d6672ffd1b6d918e64d951f8f1fd1cc441b363fJason Monk ViewGroup.LayoutParams params = imageView.getLayoutParams(); 2506d6672ffd1b6d918e64d951f8f1fd1cc441b363fJason Monk params.height = height; 2516d6672ffd1b6d918e64d951f8f1fd1cc441b363fJason Monk if (params instanceof LinearLayout.LayoutParams) { 2526d6672ffd1b6d918e64d951f8f1fd1cc441b363fJason Monk ((LinearLayout.LayoutParams) params).gravity = Gravity.CENTER_VERTICAL; 2536d6672ffd1b6d918e64d951f8f1fd1cc441b363fJason Monk } 2546d6672ffd1b6d918e64d951f8f1fd1cc441b363fJason Monk imageView.setLayoutParams(params); 25566c89c15a0baabf001e69498dbc09903f72cc63dJason Monk } 25666c89c15a0baabf001e69498dbc09903f72cc63dJason Monk 25707473cea3a6007d5e72b25d3445437a175735fa9Jason Monk public void setIcon(String slot, StatusBarIcon icon) { 25807473cea3a6007d5e72b25d3445437a175735fa9Jason Monk setIcon(getSlotIndex(slot), icon); 25907473cea3a6007d5e72b25d3445437a175735fa9Jason Monk } 26007473cea3a6007d5e72b25d3445437a175735fa9Jason Monk 26107473cea3a6007d5e72b25d3445437a175735fa9Jason Monk public void removeIcon(String slot) { 26207473cea3a6007d5e72b25d3445437a175735fa9Jason Monk int index = getSlotIndex(slot); 26366c89c15a0baabf001e69498dbc09903f72cc63dJason Monk removeIcon(index); 26407473cea3a6007d5e72b25d3445437a175735fa9Jason Monk } 26507473cea3a6007d5e72b25d3445437a175735fa9Jason Monk 26607473cea3a6007d5e72b25d3445437a175735fa9Jason Monk public void setIconVisibility(String slot, boolean visibility) { 26707473cea3a6007d5e72b25d3445437a175735fa9Jason Monk int index = getSlotIndex(slot); 26807473cea3a6007d5e72b25d3445437a175735fa9Jason Monk StatusBarIcon icon = getIcon(index); 26907473cea3a6007d5e72b25d3445437a175735fa9Jason Monk if (icon == null || icon.visible == visibility) { 27007473cea3a6007d5e72b25d3445437a175735fa9Jason Monk return; 27107473cea3a6007d5e72b25d3445437a175735fa9Jason Monk } 27207473cea3a6007d5e72b25d3445437a175735fa9Jason Monk icon.visible = visibility; 27307473cea3a6007d5e72b25d3445437a175735fa9Jason Monk handleSet(index, icon); 27407473cea3a6007d5e72b25d3445437a175735fa9Jason Monk } 27507473cea3a6007d5e72b25d3445437a175735fa9Jason Monk 27607473cea3a6007d5e72b25d3445437a175735fa9Jason Monk @Override 27766c89c15a0baabf001e69498dbc09903f72cc63dJason Monk public void removeIcon(int index) { 27866c89c15a0baabf001e69498dbc09903f72cc63dJason Monk if (getIcon(index) == null) { 27966c89c15a0baabf001e69498dbc09903f72cc63dJason Monk return; 28066c89c15a0baabf001e69498dbc09903f72cc63dJason Monk } 28166c89c15a0baabf001e69498dbc09903f72cc63dJason Monk super.removeIcon(index); 28266c89c15a0baabf001e69498dbc09903f72cc63dJason Monk int viewIndex = getViewIndex(index); 28366c89c15a0baabf001e69498dbc09903f72cc63dJason Monk mStatusIcons.removeViewAt(viewIndex); 28466c89c15a0baabf001e69498dbc09903f72cc63dJason Monk mStatusIconsKeyguard.removeViewAt(viewIndex); 28566c89c15a0baabf001e69498dbc09903f72cc63dJason Monk } 28666c89c15a0baabf001e69498dbc09903f72cc63dJason Monk 28766c89c15a0baabf001e69498dbc09903f72cc63dJason Monk @Override 28807473cea3a6007d5e72b25d3445437a175735fa9Jason Monk public void setIcon(int index, StatusBarIcon icon) { 28907473cea3a6007d5e72b25d3445437a175735fa9Jason Monk if (icon == null) { 29066c89c15a0baabf001e69498dbc09903f72cc63dJason Monk removeIcon(index); 29107473cea3a6007d5e72b25d3445437a175735fa9Jason Monk return; 29207473cea3a6007d5e72b25d3445437a175735fa9Jason Monk } 29366c89c15a0baabf001e69498dbc09903f72cc63dJason Monk boolean isNew = getIcon(index) == null; 29466c89c15a0baabf001e69498dbc09903f72cc63dJason Monk super.setIcon(index, icon); 29507473cea3a6007d5e72b25d3445437a175735fa9Jason Monk if (isNew) { 29607473cea3a6007d5e72b25d3445437a175735fa9Jason Monk addSystemIcon(index, icon); 29707473cea3a6007d5e72b25d3445437a175735fa9Jason Monk } else { 29807473cea3a6007d5e72b25d3445437a175735fa9Jason Monk handleSet(index, icon); 29907473cea3a6007d5e72b25d3445437a175735fa9Jason Monk } 30007473cea3a6007d5e72b25d3445437a175735fa9Jason Monk } 30107473cea3a6007d5e72b25d3445437a175735fa9Jason Monk 30207473cea3a6007d5e72b25d3445437a175735fa9Jason Monk private void handleSet(int index, StatusBarIcon icon) { 30307473cea3a6007d5e72b25d3445437a175735fa9Jason Monk int viewIndex = getViewIndex(index); 30466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi StatusBarIconView view = (StatusBarIconView) mStatusIcons.getChildAt(viewIndex); 30566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi view.set(icon); 30666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi view = (StatusBarIconView) mStatusIconsKeyguard.getChildAt(viewIndex); 30766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi view.set(icon); 308dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi applyIconTint(); 30966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 31066ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 31166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi public void updateNotificationIcons(NotificationData notificationData) { 31283092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen mNotificationIconAreaController.updateNotificationIcons(notificationData); 31366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 31466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 31566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi public void hideSystemIconArea(boolean animate) { 31666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi animateHide(mSystemIconArea, animate); 31766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 31866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 31966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi public void showSystemIconArea(boolean animate) { 32066ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi animateShow(mSystemIconArea, animate); 32166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 32266ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 32366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi public void hideNotificationIconArea(boolean animate) { 32483092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen animateHide(mNotificationIconAreaInner, animate); 32566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 32666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 32766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi public void showNotificationIconArea(boolean animate) { 32883092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen animateShow(mNotificationIconAreaInner, animate); 32966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 33066ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 33166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi public void setClockVisibility(boolean visible) { 33266ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi mClock.setVisibility(visible ? View.VISIBLE : View.GONE); 33366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 33466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 33566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi public void dump(PrintWriter pw) { 33666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi int N = mStatusIcons.getChildCount(); 337f3a1f2c7b0f684438bb5a34dad4600a07a1e0413Dan Sandler pw.println(" icon views: " + N); 33866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi for (int i=0; i<N; i++) { 33966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i); 34066ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi pw.println(" [" + i + "] icon=" + ic); 34166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 342f3a1f2c7b0f684438bb5a34dad4600a07a1e0413Dan Sandler super.dump(pw); 34366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 34466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 34566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi public void dispatchDemoCommand(String command, Bundle args) { 34666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi if (mDemoStatusIcons == null) { 34766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize); 34866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 34966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi mDemoStatusIcons.dispatchDemoCommand(command, args); 35066ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 35166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 35266ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi /** 35366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * Hides a view. 35466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi */ 35566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi private void animateHide(final View v, boolean animate) { 35666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi v.animate().cancel(); 35766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi if (!animate) { 35866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi v.setAlpha(0f); 35966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi v.setVisibility(View.INVISIBLE); 36066ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi return; 36166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 36266ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi v.animate() 36366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi .alpha(0f) 36466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi .setDuration(160) 36566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi .setStartDelay(0) 366c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek .setInterpolator(Interpolators.ALPHA_OUT) 36766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi .withEndAction(new Runnable() { 36866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi @Override 36966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi public void run() { 37066ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi v.setVisibility(View.INVISIBLE); 37166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 37266ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi }); 37366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 37466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 37566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi /** 37666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi * Shows a view, and synchronizes the animation with Keyguard exit animations, if applicable. 37766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi */ 37866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi private void animateShow(View v, boolean animate) { 37966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi v.animate().cancel(); 38066ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi v.setVisibility(View.VISIBLE); 38166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi if (!animate) { 38266ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi v.setAlpha(1f); 38366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi return; 38466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 38566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi v.animate() 38666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi .alpha(1f) 38766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi .setDuration(320) 388c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek .setInterpolator(Interpolators.ALPHA_IN) 38966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi .setStartDelay(50) 39066ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 39166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi // We need to clean up any pending end action from animateHide if we call 39266ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi // both hide and show in the same frame before the animation actually gets started. 39366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi // cancel() doesn't really remove the end action. 39466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi .withEndAction(null); 39566ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi 39666ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi // Synchronize the motion with the Keyguard fading if necessary. 39766ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi if (mPhoneStatusBar.isKeyguardFadingAway()) { 39866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi v.animate() 39966ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi .setDuration(mPhoneStatusBar.getKeyguardFadingAwayDuration()) 400c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) 40166ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi .setStartDelay(mPhoneStatusBar.getKeyguardFadingAwayDelay()) 40266ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi .start(); 40366ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 40466ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi } 405dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi 40686905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi /** 40786905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi * Sets the dark area so {@link #setIconsDark} only affects the icons in the specified area. 40886905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi * 40986905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi * @param darkArea the area in which icons should change it's tint, in logical screen 41086905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi * coordinates 41186905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi */ 41286905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi public void setIconsDarkArea(Rect darkArea) { 41386905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi if (darkArea == null && mTintArea.isEmpty()) { 41486905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi return; 41586905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi } 416ec1d075e5fab3e2d4320c05da6e6bd73d119dcaeJorim Jaggi if (darkArea == null) { 417ec1d075e5fab3e2d4320c05da6e6bd73d119dcaeJorim Jaggi mTintArea.setEmpty(); 418ec1d075e5fab3e2d4320c05da6e6bd73d119dcaeJorim Jaggi } else { 419ec1d075e5fab3e2d4320c05da6e6bd73d119dcaeJorim Jaggi mTintArea.set(darkArea); 420ec1d075e5fab3e2d4320c05da6e6bd73d119dcaeJorim Jaggi } 42186905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi applyIconTint(); 42286905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi mNotificationIconAreaController.setTintArea(darkArea); 42386905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi } 42486905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi 425d94d3a2aa5cd1f177f61f73733217c952e356c43Jorim Jaggi public void setIconsDark(boolean dark, boolean animate) { 426d94d3a2aa5cd1f177f61f73733217c952e356c43Jorim Jaggi if (!animate) { 427d94d3a2aa5cd1f177f61f73733217c952e356c43Jorim Jaggi setIconTintInternal(dark ? 1.0f : 0.0f); 428d94d3a2aa5cd1f177f61f73733217c952e356c43Jorim Jaggi } else if (mTransitionPending) { 4295443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi deferIconTintChange(dark ? 1.0f : 0.0f); 43033ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi } else if (mTransitionDeferring) { 4315443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi animateIconTint(dark ? 1.0f : 0.0f, 43233ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()), 43333ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi mTransitionDeferringDuration); 43424bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi } else { 4355443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION); 43624bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi } 43724bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi } 43824bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi 4395443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi private void animateIconTint(float targetDarkIntensity, long delay, 4405443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi long duration) { 44124bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi if (mTintAnimator != null) { 44224bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi mTintAnimator.cancel(); 44324bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi } 4445443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi if (mDarkIntensity == targetDarkIntensity) { 44524bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi return; 44624bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi } 4475443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi mTintAnimator = ValueAnimator.ofFloat(mDarkIntensity, targetDarkIntensity); 44824bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi mTintAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 44924bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi @Override 45024bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi public void onAnimationUpdate(ValueAnimator animation) { 4515443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi setIconTintInternal((Float) animation.getAnimatedValue()); 45224bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi } 45324bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi }); 45424bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi mTintAnimator.setDuration(duration); 45524bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi mTintAnimator.setStartDelay(delay); 456c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek mTintAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 45724bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi mTintAnimator.start(); 45824bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi } 4595443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi 4605443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi private void setIconTintInternal(float darkIntensity) { 4615443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi mDarkIntensity = darkIntensity; 4625443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi mIconTint = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, 4635443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi mLightModeIconColorSingleTone, mDarkModeIconColorSingleTone); 46483092c6cbb03b840a4b723d30486b491206b4bcdAnthony Chen mNotificationIconAreaController.setIconTint(mIconTint); 465dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi applyIconTint(); 466dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi } 467dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi 4685443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi private void deferIconTintChange(float darkIntensity) { 4695443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi if (mTintChangePending && darkIntensity == mPendingDarkIntensity) { 47024bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi return; 47124bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi } 47224bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi mTintChangePending = true; 4735443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi mPendingDarkIntensity = darkIntensity; 47424bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi } 47524bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi 47686905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi /** 47786905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi * @return the tint to apply to {@param view} depending on the desired tint {@param color} and 47886905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi * the screen {@param tintArea} in which to apply that tint 47986905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi */ 48086905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi public static int getTint(Rect tintArea, View view, int color) { 48186905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi if (isInArea(tintArea, view)) { 48286905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi return color; 48386905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi } else { 48486905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi return DEFAULT_ICON_TINT; 48586905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi } 48686905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi } 48786905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi 48886905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi /** 48986905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi * @return the dark intensity to apply to {@param view} depending on the desired dark 49086905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi * {@param intensity} and the screen {@param tintArea} in which to apply that intensity 49186905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi */ 49286905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi public static float getDarkIntensity(Rect tintArea, View view, float intensity) { 49386905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi if (isInArea(tintArea, view)) { 49486905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi return intensity; 49586905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi } else { 49686905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi return 0f; 49786905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi } 49886905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi } 49986905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi 50086905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi /** 50186905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi * @return true if more than half of the {@param view} area are in {@param area}, false 50286905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi * otherwise 50386905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi */ 50486905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi private static boolean isInArea(Rect area, View view) { 50586905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi if (area.isEmpty()) { 50686905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi return true; 50786905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi } 50886905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi sTmpRect.set(area); 50986905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi view.getLocationOnScreen(sTmpInt2); 51086905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi int left = sTmpInt2[0]; 51186905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi 51286905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi int intersectStart = Math.max(left, area.left); 51386905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi int intersectEnd = Math.min(left + view.getWidth(), area.right); 51486905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi int intersectAmount = Math.max(0, intersectEnd - intersectStart); 51586905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi 51686905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi boolean coversFullStatusBar = area.top <= 0; 51786905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi boolean majorityOfWidth = 2 * intersectAmount > view.getWidth(); 51886905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi return majorityOfWidth && coversFullStatusBar; 51986905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi } 52086905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi 521dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi private void applyIconTint() { 522dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi for (int i = 0; i < mStatusIcons.getChildCount(); i++) { 523dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi StatusBarIconView v = (StatusBarIconView) mStatusIcons.getChildAt(i); 52486905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi v.setImageTintList(ColorStateList.valueOf(getTint(mTintArea, v, mIconTint))); 525dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi } 52686905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi mSignalCluster.setIconTint(mIconTint, mDarkIntensity, mTintArea); 52786905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi mBatteryMeterView.setDarkIntensity( 52886905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi isInArea(mTintArea, mBatteryMeterView) ? mDarkIntensity : 0); 52986905582411c5c77a3e7641589cf206c6e5770f5Jorim Jaggi mClock.setTextColor(getTint(mTintArea, mClock, mIconTint)); 530dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi } 531dd0107a0dbe3084af8f5da005a4d96f8657f58a1Jorim Jaggi 53224bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi public void appTransitionPending() { 53324bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi mTransitionPending = true; 53424bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi } 53524bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi 53624bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi public void appTransitionCancelled() { 53724bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi if (mTransitionPending && mTintChangePending) { 53824bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi mTintChangePending = false; 5395443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi animateIconTint(mPendingDarkIntensity, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION); 54024bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi } 54124bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi mTransitionPending = false; 54224bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi } 54324bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi 54424bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi public void appTransitionStarting(long startTime, long duration) { 54524bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi if (mTransitionPending && mTintChangePending) { 54624bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi mTintChangePending = false; 5475443cc563cf4581a8ea9ada3b27fac7107d72099Jorim Jaggi animateIconTint(mPendingDarkIntensity, 54824bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi Math.max(0, startTime - SystemClock.uptimeMillis()), 54924bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi duration); 55033ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi 55133ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi } else if (mTransitionPending) { 55233ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi 55333ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi // If we don't have a pending tint change yet, the change might come in the future until 55433ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi // startTime is reached. 55533ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi mTransitionDeferring = true; 55633ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi mTransitionDeferringStartTime = startTime; 55733ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi mTransitionDeferringDuration = duration; 55833ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi mHandler.removeCallbacks(mTransitionDeferringDoneRunnable); 55933ae80e35a1b170309999c9b33b310ca91eb3f05Jorim Jaggi mHandler.postAtTime(mTransitionDeferringDoneRunnable, startTime); 56024bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi } 56124bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi mTransitionPending = false; 56224bec7ce7adc3c0d693cfadcb4b28f5fc41555aeJorim Jaggi } 5633b230077dc53c1f83aa6e9b70585148d8193dd82Jason Monk 5645e745172d93fcd4a90e0c8b216941a10a9994de8Jason Monk public static ArraySet<String> getIconBlacklist(String blackListStr) { 5653b230077dc53c1f83aa6e9b70585148d8193dd82Jason Monk ArraySet<String> ret = new ArraySet<String>(); 5663e189877aba0285d79ce55349afae60457d95d6fJason Monk if (blackListStr == null) { 5673e189877aba0285d79ce55349afae60457d95d6fJason Monk blackListStr = "rotate,headset"; 5683e189877aba0285d79ce55349afae60457d95d6fJason Monk } 5693e189877aba0285d79ce55349afae60457d95d6fJason Monk String[] blacklist = blackListStr.split(","); 5703e189877aba0285d79ce55349afae60457d95d6fJason Monk for (String slot : blacklist) { 5713e189877aba0285d79ce55349afae60457d95d6fJason Monk if (!TextUtils.isEmpty(slot)) { 5723e189877aba0285d79ce55349afae60457d95d6fJason Monk ret.add(slot); 5733b230077dc53c1f83aa6e9b70585148d8193dd82Jason Monk } 5743b230077dc53c1f83aa6e9b70585148d8193dd82Jason Monk } 5753b230077dc53c1f83aa6e9b70585148d8193dd82Jason Monk return ret; 5763b230077dc53c1f83aa6e9b70585148d8193dd82Jason Monk } 5773e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek 5783e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek public void onDensityOrFontScaleChanged() { 5793e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek loadDimens(); 5803e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext); 5813e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek updateClock(); 5823e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek for (int i = 0; i < mStatusIcons.getChildCount(); i++) { 5833e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek View child = mStatusIcons.getChildAt(i); 5843e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 5853e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); 5863e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek lp.setMargins(mIconHPadding, 0, mIconHPadding, 0); 5873e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek child.setLayoutParams(lp); 5883e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek } 5893e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek for (int i = 0; i < mStatusIconsKeyguard.getChildCount(); i++) { 5903e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek View child = mStatusIconsKeyguard.getChildAt(i); 5913e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 5923e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); 5933e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek child.setLayoutParams(lp); 5943e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek } 5953e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek scaleBatteryMeterViews(mContext); 5963e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek } 5973e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek 5983e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek private void updateClock() { 5993e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek FontSizeUtils.updateFontSize(mClock, R.dimen.status_bar_clock_size); 6003e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek mClock.setPaddingRelative( 6013e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek mContext.getResources().getDimensionPixelSize( 6023e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek R.dimen.status_bar_clock_starting_padding), 6033e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek 0, 6043e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek mContext.getResources().getDimensionPixelSize( 6053e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek R.dimen.status_bar_clock_end_padding), 6063e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek 0); 6073e7592d0cbbde5b0b85d98b82d991ee5d2aab8a9Selim Cinek } 60866ac133971f4e2f80cd7cfff89cc6f8a3f7e899fJorim Jaggi} 609