Merge "Custom message in AoD"

This commit is contained in:
TreeHugger Robot
2017-11-07 17:24:27 +00:00
committed by Android (Google) Code Review
8 changed files with 475 additions and 68 deletions

View File

@@ -328,6 +328,7 @@ applications that come with the platform
<privapp-permissions package="com.android.systemui">
<permission name="android.permission.BATTERY_STATS"/>
<permission name="android.permission.BIND_APPWIDGET"/>
<permission name="android.permission.BIND_SLICE" />
<permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
<permission name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/>

View File

@@ -120,6 +120,7 @@
<uses-permission android:name="android.permission.TRUST_LISTENER" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" />
<uses-permission android:name="android.permission.BIND_SLICE" />
<!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked -->
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
@@ -222,7 +223,7 @@
-->
<service android:name="SystemUIService"
android:exported="true"
/>
/>
<!-- Recents depends on every user having their own SystemUI process, so on user switch,
ensure that the process is created by starting this service.
@@ -568,6 +569,11 @@
android:resource="@xml/fileprovider" />
</provider>
<provider android:name=".keyguard.KeyguardSliceProvider"
android:authorities="com.android.systemui.keyguard"
android:exported="true">
</provider>
<receiver
android:name=".statusbar.KeyboardShortcutsReceiver">
<intent-filter>

View File

@@ -18,34 +18,37 @@
-->
<!-- This is a view that shows general status information in Keyguard. -->
<LinearLayout
<com.android.keyguard.KeyguardSliceView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<com.android.systemui.statusbar.policy.DateView
android:id="@+id/date_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?attr/wallpaperTextColor"
style="@style/widget_label"
android:letterSpacing="0.05"
android:gravity="center"
/>
<TextView android:id="@+id/alarm_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="6dp"
android:drawableStart="@drawable/ic_access_alarms_big"
android:drawableTint="?attr/wallpaperTextColorSecondary"
android:drawableTintMode="src_in"
android:textColor="?attr/wallpaperTextColorSecondary"
android:letterSpacing="0.05"
style="@style/widget_label"
android:layout_marginStart="6dp"
android:gravity="center"
android:visibility="gone"
/>
</LinearLayout>
android:layout_marginTop="@dimen/date_owner_info_margin"
android:layout_gravity="center_horizontal"
android:paddingTop="4dp"
android:clipToPadding="false"
android:orientation="vertical"
android:layout_centerHorizontal="true">
<TextView android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:fadingEdge="horizontal"
android:gravity="center"
android:textSize="22sp"
android:textColor="?attr/wallpaperTextColor"
/>
<TextView android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:gravity="center"
android:visibility="gone"
android:textSize="16sp"
android:textColor="?attr/wallpaperTextColor"
android:layout_marginTop="4dp"
android:ellipsize="end"
/>
</com.android.keyguard.KeyguardSliceView>

View File

@@ -0,0 +1,160 @@
/*
* Copyright (C) 2017 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 android.app.PendingIntent;
import android.app.slice.Slice;
import android.app.slice.SliceItem;
import android.app.slice.SliceQuery;
import android.content.Context;
import android.database.ContentObserver;
import android.graphics.Color;
import android.net.Uri;
import android.os.Handler;
import android.util.AttributeSet;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.internal.graphics.ColorUtils;
import com.android.systemui.R;
import com.android.systemui.keyguard.KeyguardSliceProvider;
/**
* View visible under the clock on the lock screen and AoD.
*/
public class KeyguardSliceView extends LinearLayout {
private final Uri mKeyguardSliceUri;
private TextView mTitle;
private TextView mText;
private Slice mSlice;
private PendingIntent mSliceAction;
private int mTextColor;
private float mDarkAmount = 0;
private final ContentObserver mObserver;
public KeyguardSliceView(Context context) {
this(context, null, 0);
}
public KeyguardSliceView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public KeyguardSliceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mObserver = new KeyguardSliceObserver(new Handler());
mKeyguardSliceUri = Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI);;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mTitle = findViewById(R.id.title);
mText = findViewById(R.id.text);
mTextColor = mTitle.getCurrentTextColor();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// Set initial content
showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri));
// Make sure we always have the most current slice
getContext().getContentResolver().registerContentObserver(mKeyguardSliceUri,
false /* notifyDescendants */, mObserver);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getContext().getContentResolver().unregisterContentObserver(mObserver);
}
private void showSlice(Slice slice) {
// Items will be wrapped into an action when they have tap targets.
SliceItem actionSlice = SliceQuery.find(slice, SliceItem.TYPE_ACTION);
if (actionSlice != null) {
mSlice = actionSlice.getSlice();
mSliceAction = actionSlice.getAction();
} else {
mSlice = slice;
mSliceAction = null;
}
if (mSlice == null) {
setVisibility(GONE);
return;
}
SliceItem title = SliceQuery.find(mSlice, SliceItem.TYPE_TEXT, Slice.HINT_TITLE, null);
if (title == null) {
mTitle.setVisibility(GONE);
} else {
mTitle.setVisibility(VISIBLE);
mTitle.setText(title.getText());
}
SliceItem text = SliceQuery.find(mSlice, SliceItem.TYPE_TEXT, null, Slice.HINT_TITLE);
if (text == null) {
mText.setVisibility(GONE);
} else {
mText.setVisibility(VISIBLE);
mText.setText(text.getText());
}
final int visibility = title == null && text == null ? GONE : VISIBLE;
if (visibility != getVisibility()) {
setVisibility(visibility);
}
}
public void setDark(float darkAmount) {
mDarkAmount = darkAmount;
updateTextColors();
}
public void setTextColor(int textColor) {
mTextColor = textColor;
}
private void updateTextColors() {
final int blendedColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
mTitle.setTextColor(blendedColor);
mText.setTextColor(blendedColor);
}
private class KeyguardSliceObserver extends ContentObserver {
KeyguardSliceObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
this.onChange(selfChange, null);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri));
}
}
}

View File

@@ -19,11 +19,9 @@ package com.android.keyguard;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
@@ -42,8 +40,8 @@ import android.widget.TextView;
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.Utils;
import com.android.systemui.ChargingView;
import com.android.systemui.statusbar.policy.DateView;
import java.util.Locale;
@@ -55,13 +53,11 @@ public class KeyguardStatusView extends GridLayout {
private final LockPatternUtils mLockPatternUtils;
private final AlarmManager mAlarmManager;
private TextView mAlarmStatusView;
private DateView mDateView;
private TextClock mClockView;
private TextView mOwnerInfo;
private ViewGroup mClockContainer;
private ChargingView mBatteryDoze;
private View mKeyguardStatusArea;
private KeyguardSliceView mKeyguardSlice;
private Runnable mPendingMarqueeStart;
private Handler mHandler;
@@ -69,8 +65,6 @@ public class KeyguardStatusView extends GridLayout {
private boolean mPulsing;
private float mDarkAmount = 0;
private int mTextColor;
private int mDateTextColor;
private int mAlarmTextColor;
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@@ -141,7 +135,6 @@ public class KeyguardStatusView extends GridLayout {
private void setEnableMarqueeImpl(boolean enabled) {
if (DEBUG) Log.v(TAG, (enabled ? "Enable" : "Disable") + " transport text marquee");
if (mAlarmStatusView != null) mAlarmStatusView.setSelected(enabled);
if (mOwnerInfo != null) mOwnerInfo.setSelected(enabled);
}
@@ -149,8 +142,6 @@ public class KeyguardStatusView extends GridLayout {
protected void onFinishInflate() {
super.onFinishInflate();
mClockContainer = findViewById(R.id.keyguard_clock_container);
mAlarmStatusView = findViewById(R.id.alarm_status);
mDateView = findViewById(R.id.date_view);
mClockView = findViewById(R.id.clock_view);
mClockView.setShowCurrentUserTime(true);
if (KeyguardClockAccessibilityDelegate.isNeeded(mContext)) {
@@ -158,11 +149,9 @@ public class KeyguardStatusView extends GridLayout {
}
mOwnerInfo = findViewById(R.id.owner_info);
mBatteryDoze = findViewById(R.id.battery_doze);
mKeyguardStatusArea = findViewById(R.id.keyguard_status_area);
mVisibleInDoze = new View[]{mBatteryDoze, mClockView, mKeyguardStatusArea};
mKeyguardSlice = findViewById(R.id.keyguard_status_area);
mVisibleInDoze = new View[]{mBatteryDoze, mClockView, mKeyguardSlice};
mTextColor = mClockView.getCurrentTextColor();
mDateTextColor = mDateView.getCurrentTextColor();
mAlarmTextColor = mAlarmStatusView.getCurrentTextColor();
boolean shouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
setEnableMarquee(shouldMarquee);
@@ -184,8 +173,6 @@ public class KeyguardStatusView extends GridLayout {
layoutParams.bottomMargin = getResources().getDimensionPixelSize(
R.dimen.bottom_text_spacing_digital);
mClockView.setLayoutParams(layoutParams);
mDateView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimensionPixelSize(R.dimen.widget_label_font_size));
if (mOwnerInfo != null) {
mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimensionPixelSize(R.dimen.widget_label_font_size));
@@ -193,8 +180,6 @@ public class KeyguardStatusView extends GridLayout {
}
public void refreshTime() {
mDateView.setDatePattern(Patterns.dateViewSkel);
mClockView.setFormat12Hour(Patterns.clockView12);
mClockView.setFormat24Hour(Patterns.clockView24);
}
@@ -205,23 +190,11 @@ public class KeyguardStatusView extends GridLayout {
Patterns.update(mContext, nextAlarm != null);
refreshTime();
refreshAlarmStatus(nextAlarm);
}
void refreshAlarmStatus(AlarmManager.AlarmClockInfo nextAlarm) {
if (nextAlarm != null) {
String alarm = formatNextAlarm(mContext, nextAlarm);
mAlarmStatusView.setText(alarm);
mAlarmStatusView.setContentDescription(
getResources().getString(R.string.keyguard_accessibility_next_alarm, alarm));
mAlarmStatusView.setVisibility(View.VISIBLE);
} else {
mAlarmStatusView.setVisibility(View.GONE);
}
}
public int getClockBottom() {
return mKeyguardStatusArea.getBottom();
return mKeyguardSlice.getVisibility() == VISIBLE ? mKeyguardSlice.getBottom()
: mClockView.getBottom();
}
public float getClockTextSize() {
@@ -341,11 +314,8 @@ public class KeyguardStatusView extends GridLayout {
updateDozeVisibleViews();
mBatteryDoze.setDark(dark);
mKeyguardSlice.setDark(darkAmount);
mClockView.setTextColor(ColorUtils.blendARGB(mTextColor, Color.WHITE, darkAmount));
mDateView.setTextColor(ColorUtils.blendARGB(mDateTextColor, Color.WHITE, darkAmount));
int blendedAlarmColor = ColorUtils.blendARGB(mAlarmTextColor, Color.WHITE, darkAmount);
mAlarmStatusView.setTextColor(blendedAlarmColor);
mAlarmStatusView.setCompoundDrawableTintList(ColorStateList.valueOf(blendedAlarmColor));
}
public void setPulsing(boolean pulsing) {

View File

@@ -0,0 +1,159 @@
/*
* Copyright (C) 2017 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.keyguard;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.icu.text.DateFormat;
import android.icu.text.DisplayContext;
import android.net.Uri;
import android.os.Handler;
import android.app.slice.Slice;
import android.app.slice.SliceProvider;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import java.util.Date;
import java.util.Locale;
/**
* Simple Slice provider that shows the current date.
*/
public class KeyguardSliceProvider extends SliceProvider {
public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main";
private final Date mCurrentTime = new Date();
protected final Uri mSliceUri;
private final Handler mHandler;
private String mDatePattern;
private DateFormat mDateFormat;
private String mLastText;
private boolean mRegistered;
private boolean mRegisteredEveryMinute;
/**
* Receiver responsible for time ticking and updating the date format.
*/
@VisibleForTesting
final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (Intent.ACTION_TIME_TICK.equals(action)
|| Intent.ACTION_DATE_CHANGED.equals(action)
|| Intent.ACTION_TIME_CHANGED.equals(action)
|| Intent.ACTION_TIMEZONE_CHANGED.equals(action)
|| Intent.ACTION_LOCALE_CHANGED.equals(action)) {
if (Intent.ACTION_LOCALE_CHANGED.equals(action)
|| Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
// need to get a fresh date format
mHandler.post(KeyguardSliceProvider.this::cleanDateFormat);
}
mHandler.post(KeyguardSliceProvider.this::updateClock);
}
}
};
public KeyguardSliceProvider() {
this(new Handler());
}
@VisibleForTesting
KeyguardSliceProvider(Handler handler) {
mHandler = handler;
mSliceUri = Uri.parse(KEYGUARD_SLICE_URI);
}
@Override
public Slice onBindSlice(Uri sliceUri) {
return new Slice.Builder(sliceUri).addText(mLastText, Slice.HINT_TITLE).build();
}
@Override
public boolean onCreate() {
mDatePattern = getContext().getString(R.string.system_ui_date_pattern);
registerClockUpdate(false /* everyMinute */);
updateClock();
return true;
}
protected void registerClockUpdate(boolean everyMinute) {
if (mRegistered) {
if (mRegisteredEveryMinute == everyMinute) {
return;
} else {
unregisterClockUpdate();
}
}
IntentFilter filter = new IntentFilter();
if (everyMinute) {
filter.addAction(Intent.ACTION_TIME_TICK);
}
filter.addAction(Intent.ACTION_DATE_CHANGED);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/,
null /* scheduler */);
mRegistered = true;
mRegisteredEveryMinute = everyMinute;
}
protected void unregisterClockUpdate() {
if (!mRegistered) {
return;
}
getContext().unregisterReceiver(mIntentReceiver);
mRegistered = false;
}
@VisibleForTesting
boolean isRegistered() {
return mRegistered;
}
protected void updateClock() {
final String text = getFormattedDate();
if (!text.equals(mLastText)) {
mLastText = text;
getContext().getContentResolver().notifyChange(mSliceUri, null /* observer */);
}
}
protected String getFormattedDate() {
if (mDateFormat == null) {
final Locale l = Locale.getDefault();
DateFormat format = DateFormat.getInstanceForSkeleton(mDatePattern, l);
format.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE);
mDateFormat = format;
}
mCurrentTime.setTime(System.currentTimeMillis());
return mDateFormat.format(mCurrentTime);
}
@VisibleForTesting
void cleanDateFormat() {
mDateFormat = null;
}
}

View File

@@ -7113,7 +7113,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
}
private void updateLockscreenNotificationSetting() {
protected void updateLockscreenNotificationSetting() {
final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
1,

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2017 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.keyguard;
import android.app.slice.Slice;
import android.app.slice.SliceItem;
import android.app.slice.SliceQuery;
import android.content.Intent;
import android.net.Uri;
import android.os.Handler;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import com.android.systemui.SysuiTestCase;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
public class KeyguardSliceProviderTest extends SysuiTestCase {
private TestableKeyguardSliceProvider mProvider;
@Before
public void setup() {
mProvider = new TestableKeyguardSliceProvider();
mProvider.attachInfo(getContext(), null);
}
@Test
public void registersClockUpdate() {
Assert.assertTrue("registerClockUpdate should have been called during initialization.",
mProvider.isRegistered());
}
@Test
public void unregisterClockUpdate() {
mProvider.unregisterClockUpdate();
Assert.assertFalse("Clock updates should have been unregistered.",
mProvider.isRegistered());
}
@Test
public void returnsValidSlice() {
Slice slice = mProvider.onBindSlice(Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI));
SliceItem text = SliceQuery.find(slice, SliceItem.TYPE_TEXT, Slice.HINT_TITLE,
null /* nonHints */);
Assert.assertNotNull("Slice must provide a title.", text);
}
@Test
public void cleansDateFormat() {
mProvider.mIntentReceiver.onReceive(getContext(), new Intent(Intent.ACTION_TIMEZONE_CHANGED));
TestableLooper.get(this).processAllMessages();
Assert.assertEquals("Date format should have been cleaned.", 1 /* expected */,
mProvider.mCleanDateFormatInvokations);
}
@Test
public void updatesClock() {
mProvider.mIntentReceiver.onReceive(getContext(), new Intent(Intent.ACTION_TIME_TICK));
TestableLooper.get(this).processAllMessages();
Assert.assertEquals("Clock should have been updated.", 2 /* expected */,
mProvider.mUpdateClockInvokations);
}
private class TestableKeyguardSliceProvider extends KeyguardSliceProvider {
int mCleanDateFormatInvokations;
int mUpdateClockInvokations;
TestableKeyguardSliceProvider() {
super(new Handler(TestableLooper.get(KeyguardSliceProviderTest.this).getLooper()));
}
@Override
void cleanDateFormat() {
super.cleanDateFormat();
mCleanDateFormatInvokations++;
}
@Override
protected void updateClock() {
super.updateClock();
mUpdateClockInvokations++;
}
}
}