KeyguardMessageArea.java revision c0ae9e67ebe6f1298800feaed1b43e867139a904
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.policy.impl.keyguard;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.os.Handler;
25import android.os.Looper;
26import android.os.UserHandle;
27import android.provider.Settings;
28import android.text.TextUtils;
29import android.util.AttributeSet;
30import android.view.View;
31import android.widget.TextView;
32
33import libcore.util.MutableInt;
34
35import com.android.internal.R;
36
37/***
38 * Manages a number of views inside of the given layout. See below for a list of widgets.
39 */
40class KeyguardMessageArea extends TextView {
41    static final int CHARGING_ICON = 0; //R.drawable.ic_lock_idle_charging;
42    static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
43
44    static final int SECURITY_MESSAGE_DURATION = 5000;
45    protected static final int FADE_DURATION = 750;
46
47    // are we showing battery information?
48    boolean mShowingBatteryInfo = false;
49
50    // is the bouncer up?
51    boolean mShowingBouncer = false;
52
53    // last known plugged in state
54    boolean mPluggedIn = false;
55
56    // last known battery level
57    int mBatteryLevel = 100;
58
59    KeyguardUpdateMonitor mUpdateMonitor;
60
61    // Timeout before we reset the message to show charging/owner info
62    long mTimeout = SECURITY_MESSAGE_DURATION;
63
64    // Shadowed text values
65    protected boolean mBatteryCharged;
66    protected boolean mBatteryIsLow;
67
68    private Handler mHandler;
69
70    CharSequence mMessage;
71    boolean mShowingMessage;
72    Runnable mClearMessageRunnable = new Runnable() {
73        @Override
74        public void run() {
75            mMessage = null;
76            mShowingMessage = false;
77            if (mShowingBouncer) {
78                hideMessage(FADE_DURATION, true);
79            } else {
80                update();
81            }
82        }
83    };
84
85    public static class Helper implements SecurityMessageDisplay {
86        KeyguardMessageArea mMessageArea;
87        Helper(View v) {
88            mMessageArea = (KeyguardMessageArea) v.findViewById(R.id.keyguard_message_area);
89            if (mMessageArea == null) {
90                throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
91            }
92        }
93
94        public void setMessage(CharSequence msg, boolean important) {
95            if (!TextUtils.isEmpty(msg) && important) {
96                mMessageArea.mMessage = msg;
97                mMessageArea.securityMessageChanged();
98            }
99        }
100
101        public void setMessage(int resId, boolean important) {
102            if (resId != 0 && important) {
103                mMessageArea.mMessage = mMessageArea.getContext().getResources().getText(resId);
104                mMessageArea.securityMessageChanged();
105            }
106        }
107
108        public void setMessage(int resId, boolean important, Object... formatArgs) {
109            if (resId != 0 && important) {
110                mMessageArea.mMessage = mMessageArea.getContext().getString(resId, formatArgs);
111                mMessageArea.securityMessageChanged();
112            }
113        }
114
115        @Override
116        public void showBouncer(int duration) {
117            mMessageArea.hideMessage(duration, false);
118            mMessageArea.mShowingBouncer = true;
119        }
120
121        @Override
122        public void hideBouncer(int duration) {
123            mMessageArea.showMessage(duration);
124            mMessageArea.mShowingBouncer = false;
125        }
126
127        @Override
128        public void setTimeout(int timeoutMs) {
129            mMessageArea.mTimeout = timeoutMs;
130        }
131    }
132
133    private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
134        @Override
135        public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
136            mShowingBatteryInfo = status.isPluggedIn() || status.isBatteryLow();
137            mPluggedIn = status.isPluggedIn();
138            mBatteryLevel = status.level;
139            mBatteryCharged = status.isCharged();
140            mBatteryIsLow = status.isBatteryLow();
141            update();
142        }
143    };
144
145    private CharSequence mSeparator;
146
147    public KeyguardMessageArea(Context context) {
148        this(context, null);
149    }
150
151    public KeyguardMessageArea(Context context, AttributeSet attrs) {
152        super(context, attrs);
153
154        // This is required to ensure marquee works
155        setSelected(true);
156
157        // Registering this callback immediately updates the battery state, among other things.
158        mUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
159        mUpdateMonitor.registerCallback(mInfoCallback);
160        mHandler = new Handler(Looper.myLooper());
161
162        mSeparator = getResources().getString(R.string.kg_text_message_separator);
163
164        update();
165    }
166
167    public void securityMessageChanged() {
168        setAlpha(1f);
169        mShowingMessage = true;
170        update();
171        mHandler.removeCallbacks(mClearMessageRunnable);
172        if (mTimeout > 0) {
173            mHandler.postDelayed(mClearMessageRunnable, mTimeout);
174        }
175        announceForAccessibility(getText());
176    }
177
178    /**
179     * Update the status lines based on these rules:
180     * AlarmStatus: Alarm state always gets it's own line.
181     * Status1 is shared between help, battery status and generic unlock instructions,
182     * prioritized in that order.
183     * @param showStatusLines status lines are shown if true
184     */
185    void update() {
186        MutableInt icon = new MutableInt(0);
187        CharSequence status = concat(getChargeInfo(icon), getOwnerInfo(), getCurrentMessage());
188        setCompoundDrawablesWithIntrinsicBounds(icon.value, 0, 0, 0);
189        setText(status);
190    }
191
192    private CharSequence concat(CharSequence... args) {
193        StringBuilder b = new StringBuilder();
194        if (!TextUtils.isEmpty(args[0])) {
195            b.append(args[0]);
196        }
197        for (int i = 1; i < args.length; i++) {
198            CharSequence text = args[i];
199            if (!TextUtils.isEmpty(text)) {
200                if (b.length() > 0) {
201                    b.append(mSeparator);
202                }
203                b.append(text);
204            }
205        }
206        return b.toString();
207    }
208
209    CharSequence getCurrentMessage() {
210        return mShowingMessage ? mMessage : null;
211    }
212
213    String getOwnerInfo() {
214        ContentResolver res = getContext().getContentResolver();
215        final boolean ownerInfoEnabled = Settings.Secure.getIntForUser(res,
216                Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
217        return ownerInfoEnabled && !mShowingMessage ?
218                Settings.Secure.getStringForUser(res, Settings.Secure.LOCK_SCREEN_OWNER_INFO,
219                        UserHandle.USER_CURRENT) : null;
220    }
221
222    private CharSequence getChargeInfo(MutableInt icon) {
223        CharSequence string = null;
224        if (mShowingBatteryInfo && !mShowingMessage) {
225            // Battery status
226            if (mPluggedIn) {
227                // Charging, charged or waiting to charge.
228                string = getContext().getString(mBatteryCharged ?
229                        com.android.internal.R.string.lockscreen_charged
230                        :com.android.internal.R.string.lockscreen_plugged_in, mBatteryLevel);
231                icon.value = CHARGING_ICON;
232            } else if (mBatteryIsLow) {
233                // Battery is low
234                string = getContext().getString(
235                        com.android.internal.R.string.lockscreen_low_battery);
236                icon.value = BATTERY_LOW_ICON;
237            }
238        }
239        return string;
240    }
241
242    private void hideMessage(int duration, boolean thenUpdate) {
243        if (duration > 0) {
244            Animator anim = ObjectAnimator.ofFloat(this, "alpha", 0f);
245            anim.setDuration(duration);
246            if (thenUpdate) {
247                anim.addListener(new AnimatorListenerAdapter() {
248                        @Override
249                            public void onAnimationEnd(Animator animation) {
250                            update();
251                        }
252                });
253            }
254            anim.start();
255        } else {
256            setAlpha(0f);
257            if (thenUpdate) {
258                update();
259            }
260        }
261    }
262
263    private void showMessage(int duration) {
264        if (duration > 0) {
265            Animator anim = ObjectAnimator.ofFloat(this, "alpha", 1f);
266            anim.setDuration(duration);
267            anim.start();
268        } else {
269            setAlpha(1f);
270        }
271    }
272}
273