blob: 037a8d3146d2e6ad125d3d030b48b622884da55d [file] [log] [blame]
/*
* 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.keyguard;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.widget.TextView;
import com.android.systemui.statusbar.policy.ConfigurationController;
import java.lang.ref.WeakReference;
import javax.inject.Inject;
import javax.inject.Named;
/***
* Manages a number of views inside of the given layout. See below for a list of widgets.
*/
public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay,
ConfigurationController.ConfigurationListener {
/** Handler token posted with accessibility announcement runnables. */
private static final Object ANNOUNCE_TOKEN = new Object();
/**
* Delay before speaking an accessibility announcement. Used to prevent
* lift-to-type from interrupting itself.
*/
private static final long ANNOUNCEMENT_DELAY = 250;
private static final int DEFAULT_COLOR = -1;
private final Handler mHandler;
private final ConfigurationController mConfigurationController;
private ColorStateList mDefaultColorState;
private CharSequence mMessage;
private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
private boolean mBouncerVisible;
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
public void onFinishedGoingToSleep(int why) {
setSelected(false);
}
public void onStartedWakingUp() {
setSelected(true);
}
@Override
public void onKeyguardBouncerChanged(boolean bouncer) {
mBouncerVisible = bouncer;
update();
}
};
public KeyguardMessageArea(Context context) {
super(context, null);
throw new IllegalStateException("This constructor should never be invoked");
}
@Inject
public KeyguardMessageArea(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
ConfigurationController configurationController) {
this(context, attrs, KeyguardUpdateMonitor.getInstance(context), configurationController);
}
public KeyguardMessageArea(Context context, AttributeSet attrs, KeyguardUpdateMonitor monitor,
ConfigurationController configurationController) {
super(context, attrs);
setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
monitor.registerCallback(mInfoCallback);
mHandler = new Handler(Looper.myLooper());
mConfigurationController = configurationController;
onThemeChanged();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mConfigurationController.addCallback(this);
onThemeChanged();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mConfigurationController.removeCallback(this);
}
@Override
public void setNextMessageColor(ColorStateList colorState) {
mNextMessageColorState = colorState;
}
@Override
public void onThemeChanged() {
TypedArray array = mContext.obtainStyledAttributes(new int[] {
R.attr.wallpaperTextColor
});
ColorStateList newTextColors = ColorStateList.valueOf(array.getColor(0, Color.RED));
array.recycle();
mDefaultColorState = newTextColors;
update();
}
@Override
public void onDensityOrFontScaleChanged() {
TypedArray array = mContext.obtainStyledAttributes(R.style.Keyguard_TextView, new int[] {
android.R.attr.textSize
});
setTextSize(TypedValue.COMPLEX_UNIT_PX, array.getDimensionPixelSize(0, 0));
array.recycle();
}
@Override
public void setMessage(CharSequence msg) {
if (!TextUtils.isEmpty(msg)) {
securityMessageChanged(msg);
} else {
clearMessage();
}
}
@Override
public void setMessage(int resId) {
CharSequence message = null;
if (resId != 0) {
message = getContext().getResources().getText(resId);
}
setMessage(message);
}
@Override
public void formatMessage(int resId, Object... formatArgs) {
CharSequence message = null;
if (resId != 0) {
message = getContext().getString(resId, formatArgs);
}
setMessage(message);
}
public static KeyguardMessageArea findSecurityMessageDisplay(View v) {
KeyguardMessageArea messageArea = v.findViewById(R.id.keyguard_message_area);
if (messageArea == null) {
messageArea = v.getRootView().findViewById(R.id.keyguard_message_area);
}
if (messageArea == null) {
throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
}
return messageArea;
}
@Override
protected void onFinishInflate() {
boolean shouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
setSelected(shouldMarquee); // This is required to ensure marquee works
}
private void securityMessageChanged(CharSequence message) {
mMessage = message;
update();
mHandler.removeCallbacksAndMessages(ANNOUNCE_TOKEN);
mHandler.postAtTime(new AnnounceRunnable(this, getText()), ANNOUNCE_TOKEN,
(SystemClock.uptimeMillis() + ANNOUNCEMENT_DELAY));
}
private void clearMessage() {
mMessage = null;
update();
}
private void update() {
CharSequence status = mMessage;
setVisibility(TextUtils.isEmpty(status) || !mBouncerVisible ? INVISIBLE : VISIBLE);
setText(status);
ColorStateList colorState = mDefaultColorState;
if (mNextMessageColorState.getDefaultColor() != DEFAULT_COLOR) {
colorState = mNextMessageColorState;
mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
}
setTextColor(colorState);
}
/**
* Runnable used to delay accessibility announcements.
*/
private static class AnnounceRunnable implements Runnable {
private final WeakReference<View> mHost;
private final CharSequence mTextToAnnounce;
AnnounceRunnable(View host, CharSequence textToAnnounce) {
mHost = new WeakReference<View>(host);
mTextToAnnounce = textToAnnounce;
}
@Override
public void run() {
final View host = mHost.get();
if (host != null) {
host.announceForAccessibility(mTextToAnnounce);
}
}
}
}