KeyguardMessageArea.java revision c345146a3a321644f653fce0fb79f288a0221fb0
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    static final String SEPARATOR = "  ";
47
48    // are we showing battery information?
49    boolean mShowingBatteryInfo = false;
50
51    // is the bouncer up?
52    boolean mShowingBouncer = false;
53
54    // last known plugged in state
55    boolean mPluggedIn = false;
56
57    // last known battery level
58    int mBatteryLevel = 100;
59
60    KeyguardUpdateMonitor mUpdateMonitor;
61
62    // Timeout before we reset the message to show charging/owner info
63    long mTimeout = SECURITY_MESSAGE_DURATION;
64
65    // Shadowed text values
66    protected boolean mBatteryCharged;
67    protected boolean mBatteryIsLow;
68
69    private Handler mHandler;
70
71    CharSequence mMessage;
72    boolean mShowingMessage;
73    Runnable mClearMessageRunnable = new Runnable() {
74        @Override
75        public void run() {
76            mMessage = null;
77            mShowingMessage = false;
78            if (mShowingBouncer) {
79                hideMessage(FADE_DURATION, true);
80            } else {
81                update();
82            }
83        }
84    };
85
86    public static class Helper implements SecurityMessageDisplay {
87        KeyguardMessageArea mMessageArea;
88        Helper(View v) {
89            mMessageArea = (KeyguardMessageArea) v.findViewById(R.id.keyguard_message_area);
90            if (mMessageArea == null) {
91                throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
92            }
93        }
94
95        public void setMessage(CharSequence msg, boolean important) {
96            if (!TextUtils.isEmpty(msg) && important) {
97                mMessageArea.mMessage = msg;
98                mMessageArea.securityMessageChanged();
99            }
100        }
101
102        public void setMessage(int resId, boolean important) {
103            if (resId != 0 && important) {
104                mMessageArea.mMessage = mMessageArea.getContext().getResources().getText(resId);
105                mMessageArea.securityMessageChanged();
106            }
107        }
108
109        public void setMessage(int resId, boolean important, Object... formatArgs) {
110            if (resId != 0 && important) {
111                mMessageArea.mMessage = mMessageArea.getContext().getString(resId, formatArgs);
112                mMessageArea.securityMessageChanged();
113            }
114        }
115
116        @Override
117        public void showBouncer(int duration) {
118            mMessageArea.hideMessage(duration, false);
119            mMessageArea.mShowingBouncer = true;
120        }
121
122        @Override
123        public void hideBouncer(int duration) {
124            mMessageArea.showMessage(duration);
125            mMessageArea.mShowingBouncer = false;
126        }
127
128        @Override
129        public void setTimeout(int timeoutMs) {
130            mMessageArea.mTimeout = timeoutMs;
131        }
132    }
133
134    private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
135        @Override
136        public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
137            mShowingBatteryInfo = status.isPluggedIn() || status.isBatteryLow();
138            mPluggedIn = status.isPluggedIn();
139            mBatteryLevel = status.level;
140            mBatteryCharged = status.isCharged();
141            mBatteryIsLow = status.isBatteryLow();
142            update();
143        }
144    };
145
146    public KeyguardMessageArea(Context context) {
147        this(context, null);
148    }
149
150    public KeyguardMessageArea(Context context, AttributeSet attrs) {
151        super(context, attrs);
152
153        // This is required to ensure marquee works
154        setSelected(true);
155
156        // Registering this callback immediately updates the battery state, among other things.
157        mUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
158        mUpdateMonitor.registerCallback(mInfoCallback);
159        mHandler = new Handler(Looper.myLooper());
160
161        update();
162    }
163
164    public void securityMessageChanged() {
165        setAlpha(1f);
166        mShowingMessage = true;
167        update();
168        mHandler.removeCallbacks(mClearMessageRunnable);
169        if (mTimeout > 0) {
170            mHandler.postDelayed(mClearMessageRunnable, mTimeout);
171        }
172        announceForAccessibility(getText());
173    }
174
175    /**
176     * Update the status lines based on these rules:
177     * AlarmStatus: Alarm state always gets it's own line.
178     * Status1 is shared between help, battery status and generic unlock instructions,
179     * prioritized in that order.
180     * @param showStatusLines status lines are shown if true
181     */
182    void update() {
183        MutableInt icon = new MutableInt(0);
184        CharSequence status = concat(getChargeInfo(icon), getOwnerInfo(), getCurrentMessage());
185        setCompoundDrawablesWithIntrinsicBounds(icon.value, 0, 0, 0);
186        setText(status);
187    }
188
189
190    private CharSequence concat(Object... args) {
191        StringBuilder b = new StringBuilder();
192        for (int i = 0; i < args.length; i++) {
193            final Object arg = args[i];
194            if (arg instanceof CharSequence) {
195                b.append((CharSequence)args[i]);
196                b.append(SEPARATOR);
197            } else if (arg instanceof String) {
198                b.append((String)args[i]);
199                b.append(SEPARATOR);
200            }
201        }
202        return b.toString();
203    }
204
205
206    CharSequence getCurrentMessage() {
207        return mShowingMessage ? mMessage : null;
208    }
209
210    String getOwnerInfo() {
211        ContentResolver res = getContext().getContentResolver();
212        final boolean ownerInfoEnabled = Settings.Secure.getIntForUser(res,
213                Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
214        return ownerInfoEnabled && !mShowingMessage ?
215                Settings.Secure.getStringForUser(res, Settings.Secure.LOCK_SCREEN_OWNER_INFO,
216                        UserHandle.USER_CURRENT) : null;
217    }
218
219    private CharSequence getChargeInfo(MutableInt icon) {
220        CharSequence string = null;
221        if (mShowingBatteryInfo && !mShowingMessage) {
222            // Battery status
223            if (mPluggedIn) {
224                // Charging, charged or waiting to charge.
225                string = getContext().getString(mBatteryCharged ?
226                        com.android.internal.R.string.lockscreen_charged
227                        :com.android.internal.R.string.lockscreen_plugged_in, mBatteryLevel);
228                icon.value = CHARGING_ICON;
229            } else if (mBatteryIsLow) {
230                // Battery is low
231                string = getContext().getString(
232                        com.android.internal.R.string.lockscreen_low_battery);
233                icon.value = BATTERY_LOW_ICON;
234            }
235        }
236        return string;
237    }
238
239    private void hideMessage(int duration, boolean thenUpdate) {
240        Animator anim = ObjectAnimator.ofFloat(this, "alpha", 0f);
241        anim.setDuration(duration);
242        if (thenUpdate) {
243            anim.addListener(new AnimatorListenerAdapter() {
244                    @Override
245                        public void onAnimationEnd(Animator animation) {
246                        update();
247                    }
248                });
249        }
250        anim.start();
251    }
252
253    private void showMessage(int duration) {
254        Animator anim = ObjectAnimator.ofFloat(this, "alpha", 1f);
255        anim.setDuration(duration);
256        anim.start();
257    }
258}
259