Merge "Remove deprecated clock components" into udc-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
f114d7ee35
@@ -1068,15 +1068,6 @@
|
||||
android:exported="true">
|
||||
</provider>
|
||||
|
||||
<!-- Provides list and realistic previews of clock faces for the picker app. -->
|
||||
<provider
|
||||
android:name="com.android.keyguard.clock.ClockOptionsProvider"
|
||||
android:authorities="com.android.keyguard.clock"
|
||||
android:enabled="false"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
</provider>
|
||||
|
||||
<receiver
|
||||
android:name=".statusbar.KeyboardShortcutsReceiver"
|
||||
android:exported="true">
|
||||
|
||||
@@ -40,11 +40,6 @@
|
||||
android:enabled="false"
|
||||
tools:replace="android:authorities"
|
||||
tools:node="remove" />
|
||||
<provider android:name="com.android.keyguard.clock.ClockOptionsProvider"
|
||||
android:authorities="com.android.systemui.test.keyguard.clock.disabled"
|
||||
android:enabled="false"
|
||||
tools:replace="android:authorities"
|
||||
tools:node="remove" />
|
||||
<provider android:name="com.android.systemui.people.PeopleProvider"
|
||||
android:authorities="com.android.systemui.test.people.disabled"
|
||||
android:enabled="false"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Clock Plugins
|
||||
|
||||
The clock appearing on the lock screen and always on display (AOD) can be
|
||||
customized via the ClockProviderPlugin plugin interface.
|
||||
The clock appearing on the lock screen and always on display (AOD) can be customized via the
|
||||
ClockProviderPlugin plugin interface. The ClockPlugin interface has been removed.
|
||||
|
||||
## Lock screen integration
|
||||
The lockscreen code has two main components, a [clock customization library](../customization), and
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.plugins;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Plugin used to replace main clock in keyguard.
|
||||
* @deprecated Migrating to ClockProviderPlugin
|
||||
*/
|
||||
@Deprecated
|
||||
@ProvidesInterface(action = ClockPlugin.ACTION, version = ClockPlugin.VERSION)
|
||||
public interface ClockPlugin extends Plugin {
|
||||
|
||||
String ACTION = "com.android.systemui.action.PLUGIN_CLOCK";
|
||||
int VERSION = 5;
|
||||
|
||||
/**
|
||||
* Get the name of the clock face.
|
||||
*
|
||||
* This name should not be translated.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Get the title of the clock face to be shown in the picker app.
|
||||
*/
|
||||
String getTitle();
|
||||
|
||||
/**
|
||||
* Get thumbnail of clock face to be shown in the picker app.
|
||||
*/
|
||||
Bitmap getThumbnail();
|
||||
|
||||
/**
|
||||
* Get preview images of clock face to be shown in the picker app.
|
||||
*
|
||||
* Preview image should be realistic and show what the clock face will look like on AOD and lock
|
||||
* screen.
|
||||
*
|
||||
* @param width width of the preview image, should be the same as device width in pixels.
|
||||
* @param height height of the preview image, should be the same as device height in pixels.
|
||||
*/
|
||||
Bitmap getPreview(int width, int height);
|
||||
|
||||
/**
|
||||
* Get clock view.
|
||||
* @return clock view from plugin.
|
||||
*/
|
||||
View getView();
|
||||
|
||||
/**
|
||||
* Get clock view for a large clock that appears behind NSSL.
|
||||
*/
|
||||
default View getBigClockView() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred Y position of the clock.
|
||||
*
|
||||
* @param totalHeight Height of the parent container.
|
||||
* @return preferred Y position.
|
||||
*/
|
||||
int getPreferredY(int totalHeight);
|
||||
|
||||
/**
|
||||
* Allows the plugin to clean up resources when no longer needed.
|
||||
*
|
||||
* Called when the view previously created by {@link ClockPlugin#getView()} has been detached
|
||||
* from the view hierarchy.
|
||||
*/
|
||||
void onDestroyView();
|
||||
|
||||
/**
|
||||
* Set clock paint style.
|
||||
* @param style The new style to set in the paint.
|
||||
*/
|
||||
void setStyle(Style style);
|
||||
|
||||
/**
|
||||
* Set clock text color.
|
||||
* @param color A color value.
|
||||
*/
|
||||
void setTextColor(int color);
|
||||
|
||||
/**
|
||||
* Sets the color palette for the clock face.
|
||||
* @param supportsDarkText Whether dark text can be displayed.
|
||||
* @param colors Colors that should be used on the clock face, ordered from darker to lighter.
|
||||
*/
|
||||
default void setColorPalette(boolean supportsDarkText, int[] colors) {}
|
||||
|
||||
/**
|
||||
* Set the amount (ratio) that the device has transitioned to doze.
|
||||
* @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
|
||||
*/
|
||||
default void setDarkAmount(float darkAmount) {}
|
||||
|
||||
/**
|
||||
* Notifies that time tick alarm from doze service fired.
|
||||
*
|
||||
* Implement this method instead of registering a broadcast listener for TIME_TICK.
|
||||
*/
|
||||
default void onTimeTick() {}
|
||||
|
||||
/**
|
||||
* Notifies that the time zone has changed.
|
||||
*
|
||||
* Implement this method instead of registering a broadcast listener for TIME_ZONE_CHANGED.
|
||||
*/
|
||||
default void onTimeZoneChanged(TimeZone timeZone) {}
|
||||
|
||||
/**
|
||||
* Notifies that the time format has changed.
|
||||
*
|
||||
* @param timeFormat "12" for 12-hour format, "24" for 24-hour format
|
||||
*/
|
||||
default void onTimeFormatChanged(String timeFormat) {}
|
||||
|
||||
/**
|
||||
* Indicates whether the keyguard status area (date) should be shown below
|
||||
* the clock.
|
||||
*/
|
||||
default boolean shouldShowStatusArea() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextClock;
|
||||
|
||||
import com.android.internal.colorextraction.ColorExtractor;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||
import com.android.systemui.plugins.ClockPlugin;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Controller for Stretch clock that can appear on lock screen and AOD.
|
||||
*/
|
||||
public class AnalogClockController implements ClockPlugin {
|
||||
|
||||
/**
|
||||
* Resources used to get title and thumbnail.
|
||||
*/
|
||||
private final Resources mResources;
|
||||
|
||||
/**
|
||||
* LayoutInflater used to inflate custom clock views.
|
||||
*/
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
|
||||
/**
|
||||
* Extracts accent color from wallpaper.
|
||||
*/
|
||||
private final SysuiColorExtractor mColorExtractor;
|
||||
|
||||
/**
|
||||
* Computes preferred position of clock.
|
||||
*/
|
||||
private final SmallClockPosition mClockPosition;
|
||||
|
||||
/**
|
||||
* Renders preview from clock view.
|
||||
*/
|
||||
private final ViewPreviewer mRenderer = new ViewPreviewer();
|
||||
|
||||
/**
|
||||
* Custom clock shown on AOD screen and behind stack scroller on lock.
|
||||
*/
|
||||
private ClockLayout mBigClockView;
|
||||
private ImageClock mAnalogClock;
|
||||
|
||||
/**
|
||||
* Small clock shown on lock screen above stack scroller.
|
||||
*/
|
||||
private View mView;
|
||||
private TextClock mLockClock;
|
||||
|
||||
/**
|
||||
* Helper to extract colors from wallpaper palette for clock face.
|
||||
*/
|
||||
private final ClockPalette mPalette = new ClockPalette();
|
||||
|
||||
/**
|
||||
* Create a BubbleClockController instance.
|
||||
*
|
||||
* @param res Resources contains title and thumbnail.
|
||||
* @param inflater Inflater used to inflate custom clock views.
|
||||
* @param colorExtractor Extracts accent color from wallpaper.
|
||||
*/
|
||||
public AnalogClockController(Resources res, LayoutInflater inflater,
|
||||
SysuiColorExtractor colorExtractor) {
|
||||
mResources = res;
|
||||
mLayoutInflater = inflater;
|
||||
mColorExtractor = colorExtractor;
|
||||
mClockPosition = new SmallClockPosition(inflater.getContext());
|
||||
}
|
||||
|
||||
private void createViews() {
|
||||
mBigClockView = (ClockLayout) mLayoutInflater.inflate(R.layout.analog_clock, null);
|
||||
mAnalogClock = mBigClockView.findViewById(R.id.analog_clock);
|
||||
|
||||
mView = mLayoutInflater.inflate(R.layout.digital_clock, null);
|
||||
mLockClock = mView.findViewById(R.id.lock_screen_clock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
mBigClockView = null;
|
||||
mAnalogClock = null;
|
||||
mView = null;
|
||||
mLockClock = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "analog";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return mResources.getString(R.string.clock_title_analog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap getThumbnail() {
|
||||
return BitmapFactory.decodeResource(mResources, R.drawable.analog_thumbnail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap getPreview(int width, int height) {
|
||||
|
||||
// Use the big clock view for the preview
|
||||
View view = getBigClockView();
|
||||
|
||||
// Initialize state of plugin before generating preview.
|
||||
setDarkAmount(1f);
|
||||
setTextColor(Color.WHITE);
|
||||
ColorExtractor.GradientColors colors = mColorExtractor.getColors(
|
||||
WallpaperManager.FLAG_LOCK);
|
||||
setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
|
||||
onTimeTick();
|
||||
|
||||
return mRenderer.createPreview(view, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView() {
|
||||
if (mView == null) {
|
||||
createViews();
|
||||
}
|
||||
return mView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getBigClockView() {
|
||||
if (mBigClockView == null) {
|
||||
createViews();
|
||||
}
|
||||
return mBigClockView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPreferredY(int totalHeight) {
|
||||
return mClockPosition.getPreferredY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStyle(Style style) {}
|
||||
|
||||
@Override
|
||||
public void setTextColor(int color) {
|
||||
updateColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorPalette(boolean supportsDarkText, int[] colorPalette) {
|
||||
mPalette.setColorPalette(supportsDarkText, colorPalette);
|
||||
updateColor();
|
||||
}
|
||||
|
||||
private void updateColor() {
|
||||
final int primary = mPalette.getPrimaryColor();
|
||||
final int secondary = mPalette.getSecondaryColor();
|
||||
mLockClock.setTextColor(secondary);
|
||||
mAnalogClock.setClockColors(primary, secondary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeTick() {
|
||||
mAnalogClock.onTimeChanged();
|
||||
mBigClockView.onTimeChanged();
|
||||
mLockClock.refreshTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDarkAmount(float darkAmount) {
|
||||
mPalette.setDarkAmount(darkAmount);
|
||||
mClockPosition.setDarkAmount(darkAmount);
|
||||
mBigClockView.setDarkAmount(darkAmount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeZoneChanged(TimeZone timeZone) {
|
||||
mAnalogClock.onTimeZoneChanged(timeZone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldShowStatusArea() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextClock;
|
||||
|
||||
import com.android.internal.colorextraction.ColorExtractor;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||
import com.android.systemui.plugins.ClockPlugin;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Controller for Bubble clock that can appear on lock screen and AOD.
|
||||
*/
|
||||
public class BubbleClockController implements ClockPlugin {
|
||||
|
||||
/**
|
||||
* Resources used to get title and thumbnail.
|
||||
*/
|
||||
private final Resources mResources;
|
||||
|
||||
/**
|
||||
* LayoutInflater used to inflate custom clock views.
|
||||
*/
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
|
||||
/**
|
||||
* Extracts accent color from wallpaper.
|
||||
*/
|
||||
private final SysuiColorExtractor mColorExtractor;
|
||||
|
||||
/**
|
||||
* Computes preferred position of clock.
|
||||
*/
|
||||
private final SmallClockPosition mClockPosition;
|
||||
|
||||
/**
|
||||
* Renders preview from clock view.
|
||||
*/
|
||||
private final ViewPreviewer mRenderer = new ViewPreviewer();
|
||||
|
||||
/**
|
||||
* Custom clock shown on AOD screen and behind stack scroller on lock.
|
||||
*/
|
||||
private ClockLayout mView;
|
||||
private ImageClock mAnalogClock;
|
||||
|
||||
/**
|
||||
* Small clock shown on lock screen above stack scroller.
|
||||
*/
|
||||
private View mLockClockContainer;
|
||||
private TextClock mLockClock;
|
||||
|
||||
/**
|
||||
* Helper to extract colors from wallpaper palette for clock face.
|
||||
*/
|
||||
private final ClockPalette mPalette = new ClockPalette();
|
||||
|
||||
/**
|
||||
* Create a BubbleClockController instance.
|
||||
*
|
||||
* @param res Resources contains title and thumbnail.
|
||||
* @param inflater Inflater used to inflate custom clock views.
|
||||
* @param colorExtractor Extracts accent color from wallpaper.
|
||||
*/
|
||||
public BubbleClockController(Resources res, LayoutInflater inflater,
|
||||
SysuiColorExtractor colorExtractor) {
|
||||
mResources = res;
|
||||
mLayoutInflater = inflater;
|
||||
mColorExtractor = colorExtractor;
|
||||
mClockPosition = new SmallClockPosition(inflater.getContext());
|
||||
}
|
||||
|
||||
private void createViews() {
|
||||
mView = (ClockLayout) mLayoutInflater.inflate(R.layout.bubble_clock, null);
|
||||
mAnalogClock = (ImageClock) mView.findViewById(R.id.analog_clock);
|
||||
|
||||
mLockClockContainer = mLayoutInflater.inflate(R.layout.digital_clock, null);
|
||||
mLockClock = (TextClock) mLockClockContainer.findViewById(R.id.lock_screen_clock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
mView = null;
|
||||
mAnalogClock = null;
|
||||
mLockClockContainer = null;
|
||||
mLockClock = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "bubble";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return mResources.getString(R.string.clock_title_bubble);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap getThumbnail() {
|
||||
return BitmapFactory.decodeResource(mResources, R.drawable.bubble_thumbnail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap getPreview(int width, int height) {
|
||||
|
||||
// Use the big clock view for the preview
|
||||
View view = getBigClockView();
|
||||
|
||||
// Initialize state of plugin before generating preview.
|
||||
setDarkAmount(1f);
|
||||
setTextColor(Color.WHITE);
|
||||
ColorExtractor.GradientColors colors = mColorExtractor.getColors(
|
||||
WallpaperManager.FLAG_LOCK);
|
||||
setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
|
||||
onTimeTick();
|
||||
|
||||
return mRenderer.createPreview(view, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView() {
|
||||
if (mLockClockContainer == null) {
|
||||
createViews();
|
||||
}
|
||||
return mLockClockContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getBigClockView() {
|
||||
if (mView == null) {
|
||||
createViews();
|
||||
}
|
||||
return mView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPreferredY(int totalHeight) {
|
||||
return mClockPosition.getPreferredY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStyle(Style style) {}
|
||||
|
||||
@Override
|
||||
public void setTextColor(int color) {
|
||||
updateColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorPalette(boolean supportsDarkText, int[] colorPalette) {
|
||||
mPalette.setColorPalette(supportsDarkText, colorPalette);
|
||||
updateColor();
|
||||
}
|
||||
|
||||
private void updateColor() {
|
||||
final int primary = mPalette.getPrimaryColor();
|
||||
final int secondary = mPalette.getSecondaryColor();
|
||||
mLockClock.setTextColor(secondary);
|
||||
mAnalogClock.setClockColors(primary, secondary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDarkAmount(float darkAmount) {
|
||||
mPalette.setDarkAmount(darkAmount);
|
||||
mClockPosition.setDarkAmount(darkAmount);
|
||||
mView.setDarkAmount(darkAmount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeTick() {
|
||||
mAnalogClock.onTimeChanged();
|
||||
mView.onTimeChanged();
|
||||
mLockClock.refreshTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeZoneChanged(TimeZone timeZone) {
|
||||
mAnalogClock.onTimeZoneChanged(timeZone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldShowStatusArea() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Metadata about an available clock face.
|
||||
*/
|
||||
final class ClockInfo {
|
||||
|
||||
private final String mName;
|
||||
private final Supplier<String> mTitle;
|
||||
private final String mId;
|
||||
private final Supplier<Bitmap> mThumbnail;
|
||||
private final Supplier<Bitmap> mPreview;
|
||||
|
||||
private ClockInfo(String name, Supplier<String> title, String id,
|
||||
Supplier<Bitmap> thumbnail, Supplier<Bitmap> preview) {
|
||||
mName = name;
|
||||
mTitle = title;
|
||||
mId = id;
|
||||
mThumbnail = thumbnail;
|
||||
mPreview = preview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the non-internationalized name for the clock face.
|
||||
*/
|
||||
String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name (title) of the clock face to be shown in the picker app.
|
||||
*/
|
||||
String getTitle() {
|
||||
return mTitle.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID of the clock face, used by the picker to set the current selection.
|
||||
*/
|
||||
String getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a thumbnail image of the clock.
|
||||
*/
|
||||
Bitmap getThumbnail() {
|
||||
return mThumbnail.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a potentially realistic preview image of the clock face.
|
||||
*/
|
||||
Bitmap getPreview() {
|
||||
return mPreview.get();
|
||||
}
|
||||
|
||||
static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
static class Builder {
|
||||
private String mName;
|
||||
private Supplier<String> mTitle;
|
||||
private String mId;
|
||||
private Supplier<Bitmap> mThumbnail;
|
||||
private Supplier<Bitmap> mPreview;
|
||||
|
||||
public ClockInfo build() {
|
||||
return new ClockInfo(mName, mTitle, mId, mThumbnail, mPreview);
|
||||
}
|
||||
|
||||
public Builder setName(String name) {
|
||||
mName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTitle(Supplier<String> title) {
|
||||
mTitle = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setId(String id) {
|
||||
mId = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setThumbnail(Supplier<Bitmap> thumbnail) {
|
||||
mThumbnail = thumbnail;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPreview(Supplier<Bitmap> preview) {
|
||||
mPreview = preview;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.clock;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
/**
|
||||
* Dagger Module for clock package.
|
||||
*
|
||||
* @deprecated Migrate to ClockRegistry
|
||||
*/
|
||||
@Module
|
||||
@Deprecated
|
||||
public abstract class ClockInfoModule {
|
||||
|
||||
/** */
|
||||
@Provides
|
||||
public static List<ClockInfo> provideClockInfoList(ClockManager clockManager) {
|
||||
return clockManager.getClockInfos();
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.MathUtils;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* Positions clock faces (analog, digital, typographic) and handles pixel shifting
|
||||
* to prevent screen burn-in.
|
||||
*/
|
||||
public class ClockLayout extends FrameLayout {
|
||||
|
||||
private static final int ANALOG_CLOCK_SHIFT_FACTOR = 3;
|
||||
/**
|
||||
* Clock face views.
|
||||
*/
|
||||
private View mAnalogClock;
|
||||
|
||||
/**
|
||||
* Pixel shifting amplitudes used to prevent screen burn-in.
|
||||
*/
|
||||
private int mBurnInPreventionOffsetX;
|
||||
private int mBurnInPreventionOffsetY;
|
||||
|
||||
private float mDarkAmount;
|
||||
|
||||
public ClockLayout(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ClockLayout(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ClockLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mAnalogClock = findViewById(R.id.analog_clock);
|
||||
|
||||
// Get pixel shifting X, Y amplitudes from resources.
|
||||
Resources resources = getResources();
|
||||
mBurnInPreventionOffsetX = resources.getDimensionPixelSize(
|
||||
R.dimen.burn_in_prevention_offset_x);
|
||||
mBurnInPreventionOffsetY = resources.getDimensionPixelSize(
|
||||
R.dimen.burn_in_prevention_offset_y);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
positionChildren();
|
||||
}
|
||||
|
||||
void onTimeChanged() {
|
||||
positionChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link com.android.systemui.plugins.ClockPlugin#setDarkAmount(float)}.
|
||||
*/
|
||||
void setDarkAmount(float darkAmount) {
|
||||
mDarkAmount = darkAmount;
|
||||
positionChildren();
|
||||
}
|
||||
|
||||
private void positionChildren() {
|
||||
final float offsetX = MathUtils.lerp(0f,
|
||||
getBurnInOffset(mBurnInPreventionOffsetX * 2, true) - mBurnInPreventionOffsetX,
|
||||
mDarkAmount);
|
||||
final float offsetY = MathUtils.lerp(0f,
|
||||
getBurnInOffset(mBurnInPreventionOffsetY * 2, false)
|
||||
- 0.5f * mBurnInPreventionOffsetY,
|
||||
mDarkAmount);
|
||||
|
||||
// Put the analog clock in the middle of the screen.
|
||||
if (mAnalogClock != null) {
|
||||
mAnalogClock.setX(Math.max(0f, 0.5f * (getWidth() - mAnalogClock.getWidth()))
|
||||
+ ANALOG_CLOCK_SHIFT_FACTOR * offsetX);
|
||||
mAnalogClock.setY(Math.max(0f, 0.5f * (getHeight() - mAnalogClock.getHeight()))
|
||||
+ ANALOG_CLOCK_SHIFT_FACTOR * offsetY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,392 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.dock.DockManager;
|
||||
import com.android.systemui.dock.DockManager.DockEventListener;
|
||||
import com.android.systemui.plugins.ClockPlugin;
|
||||
import com.android.systemui.plugins.PluginListener;
|
||||
import com.android.systemui.plugins.PluginManager;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Manages custom clock faces for AOD and lock screen.
|
||||
*
|
||||
* @deprecated Migrate to ClockRegistry
|
||||
*/
|
||||
@SysUISingleton
|
||||
@Deprecated
|
||||
public final class ClockManager {
|
||||
|
||||
private static final String TAG = "ClockOptsProvider";
|
||||
|
||||
private final AvailableClocks mPreviewClocks;
|
||||
private final List<Supplier<ClockPlugin>> mBuiltinClocks = new ArrayList<>();
|
||||
|
||||
private final Context mContext;
|
||||
private final ContentResolver mContentResolver;
|
||||
private final SettingsWrapper mSettingsWrapper;
|
||||
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
|
||||
private final UserTracker mUserTracker;
|
||||
private final Executor mMainExecutor;
|
||||
|
||||
/**
|
||||
* Observe settings changes to know when to switch the clock face.
|
||||
*/
|
||||
private final ContentObserver mContentObserver =
|
||||
new ContentObserver(mMainHandler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Collection<Uri> uris,
|
||||
int flags, int userId) {
|
||||
if (Objects.equals(userId,
|
||||
mUserTracker.getUserId())) {
|
||||
reload();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Observe user changes and react by potentially loading the custom clock for the new user.
|
||||
*/
|
||||
private final UserTracker.Callback mUserChangedCallback =
|
||||
new UserTracker.Callback() {
|
||||
@Override
|
||||
public void onUserChanged(int newUser, @NonNull Context userContext) {
|
||||
reload();
|
||||
}
|
||||
};
|
||||
|
||||
private final PluginManager mPluginManager;
|
||||
@Nullable private final DockManager mDockManager;
|
||||
|
||||
/**
|
||||
* Observe changes to dock state to know when to switch the clock face.
|
||||
*/
|
||||
private final DockEventListener mDockEventListener =
|
||||
new DockEventListener() {
|
||||
@Override
|
||||
public void onEvent(int event) {
|
||||
mIsDocked = (event == DockManager.STATE_DOCKED
|
||||
|| event == DockManager.STATE_DOCKED_HIDE);
|
||||
reload();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face
|
||||
* to show.
|
||||
*/
|
||||
private boolean mIsDocked;
|
||||
|
||||
/**
|
||||
* Listeners for onClockChanged event.
|
||||
*
|
||||
* Each listener must receive a separate clock plugin instance. Otherwise, there could be
|
||||
* problems like attempting to attach a view that already has a parent. To deal with this issue,
|
||||
* each listener is associated with a collection of available clocks. When onClockChanged is
|
||||
* fired the current clock plugin instance is retrieved from that listeners available clocks.
|
||||
*/
|
||||
private final Map<ClockChangedListener, AvailableClocks> mListeners = new ArrayMap<>();
|
||||
|
||||
private final int mWidth;
|
||||
private final int mHeight;
|
||||
|
||||
@Inject
|
||||
public ClockManager(Context context, LayoutInflater layoutInflater,
|
||||
PluginManager pluginManager, SysuiColorExtractor colorExtractor,
|
||||
@Nullable DockManager dockManager, UserTracker userTracker,
|
||||
@Main Executor mainExecutor) {
|
||||
this(context, layoutInflater, pluginManager, colorExtractor,
|
||||
context.getContentResolver(), userTracker, mainExecutor,
|
||||
new SettingsWrapper(context.getContentResolver()), dockManager);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ClockManager(Context context, LayoutInflater layoutInflater,
|
||||
PluginManager pluginManager, SysuiColorExtractor colorExtractor,
|
||||
ContentResolver contentResolver, UserTracker userTracker, Executor mainExecutor,
|
||||
SettingsWrapper settingsWrapper, DockManager dockManager) {
|
||||
mContext = context;
|
||||
mPluginManager = pluginManager;
|
||||
mContentResolver = contentResolver;
|
||||
mSettingsWrapper = settingsWrapper;
|
||||
mUserTracker = userTracker;
|
||||
mMainExecutor = mainExecutor;
|
||||
mDockManager = dockManager;
|
||||
mPreviewClocks = new AvailableClocks();
|
||||
|
||||
Resources res = context.getResources();
|
||||
|
||||
addBuiltinClock(() -> new DefaultClockController(res, layoutInflater, colorExtractor));
|
||||
|
||||
// Store the size of the display for generation of clock preview.
|
||||
DisplayMetrics dm = res.getDisplayMetrics();
|
||||
mWidth = dm.widthPixels;
|
||||
mHeight = dm.heightPixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add listener to be notified when clock implementation should change.
|
||||
*/
|
||||
public void addOnClockChangedListener(ClockChangedListener listener) {
|
||||
if (mListeners.isEmpty()) {
|
||||
register();
|
||||
}
|
||||
AvailableClocks availableClocks = new AvailableClocks();
|
||||
for (int i = 0; i < mBuiltinClocks.size(); i++) {
|
||||
availableClocks.addClockPlugin(mBuiltinClocks.get(i).get());
|
||||
}
|
||||
mListeners.put(listener, availableClocks);
|
||||
mPluginManager.addPluginListener(availableClocks, ClockPlugin.class, true);
|
||||
reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove listener added with {@link addOnClockChangedListener}.
|
||||
*/
|
||||
public void removeOnClockChangedListener(ClockChangedListener listener) {
|
||||
AvailableClocks availableClocks = mListeners.remove(listener);
|
||||
mPluginManager.removePluginListener(availableClocks);
|
||||
if (mListeners.isEmpty()) {
|
||||
unregister();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about available clock faces.
|
||||
*/
|
||||
List<ClockInfo> getClockInfos() {
|
||||
return mPreviewClocks.getInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current clock.
|
||||
* @return current custom clock or null for default.
|
||||
*/
|
||||
@Nullable
|
||||
ClockPlugin getCurrentClock() {
|
||||
return mPreviewClocks.getCurrentClock();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean isDocked() {
|
||||
return mIsDocked;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ContentObserver getContentObserver() {
|
||||
return mContentObserver;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void addBuiltinClock(Supplier<ClockPlugin> pluginSupplier) {
|
||||
ClockPlugin plugin = pluginSupplier.get();
|
||||
mPreviewClocks.addClockPlugin(plugin);
|
||||
mBuiltinClocks.add(pluginSupplier);
|
||||
}
|
||||
|
||||
private void register() {
|
||||
mPluginManager.addPluginListener(mPreviewClocks, ClockPlugin.class, true);
|
||||
mContentResolver.registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
|
||||
false, mContentObserver, UserHandle.USER_ALL);
|
||||
mContentResolver.registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE),
|
||||
false, mContentObserver, UserHandle.USER_ALL);
|
||||
mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
|
||||
if (mDockManager != null) {
|
||||
mDockManager.addListener(mDockEventListener);
|
||||
}
|
||||
}
|
||||
|
||||
private void unregister() {
|
||||
mPluginManager.removePluginListener(mPreviewClocks);
|
||||
mContentResolver.unregisterContentObserver(mContentObserver);
|
||||
mUserTracker.removeCallback(mUserChangedCallback);
|
||||
if (mDockManager != null) {
|
||||
mDockManager.removeListener(mDockEventListener);
|
||||
}
|
||||
}
|
||||
|
||||
private void reload() {
|
||||
mPreviewClocks.reloadCurrentClock();
|
||||
mListeners.forEach((listener, clocks) -> {
|
||||
clocks.reloadCurrentClock();
|
||||
final ClockPlugin clock = clocks.getCurrentClock();
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
listener.onClockChanged(clock instanceof DefaultClockController ? null : clock);
|
||||
} else {
|
||||
mMainHandler.post(() -> listener.onClockChanged(
|
||||
clock instanceof DefaultClockController ? null : clock));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for events that should cause the custom clock face to change.
|
||||
*/
|
||||
public interface ClockChangedListener {
|
||||
/**
|
||||
* Called when custom clock should change.
|
||||
*
|
||||
* @param clock Custom clock face to use. A null value indicates the default clock face.
|
||||
*/
|
||||
void onClockChanged(ClockPlugin clock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection of available clocks.
|
||||
*/
|
||||
private final class AvailableClocks implements PluginListener<ClockPlugin> {
|
||||
|
||||
/**
|
||||
* Map from expected value stored in settings to plugin for custom clock face.
|
||||
*/
|
||||
private final Map<String, ClockPlugin> mClocks = new ArrayMap<>();
|
||||
|
||||
/**
|
||||
* Metadata about available clocks, such as name and preview images.
|
||||
*/
|
||||
private final List<ClockInfo> mClockInfo = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Active ClockPlugin.
|
||||
*/
|
||||
@Nullable private ClockPlugin mCurrentClock;
|
||||
|
||||
@Override
|
||||
public void onPluginConnected(ClockPlugin plugin, Context pluginContext) {
|
||||
addClockPlugin(plugin);
|
||||
reloadIfNeeded(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPluginDisconnected(ClockPlugin plugin) {
|
||||
removeClockPlugin(plugin);
|
||||
reloadIfNeeded(plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current clock.
|
||||
* @return current custom clock or null for default.
|
||||
*/
|
||||
@Nullable
|
||||
ClockPlugin getCurrentClock() {
|
||||
return mCurrentClock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about available clock faces.
|
||||
*/
|
||||
List<ClockInfo> getInfo() {
|
||||
return mClockInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a clock plugin to the collection of available clocks.
|
||||
*
|
||||
* @param plugin The plugin to add.
|
||||
*/
|
||||
void addClockPlugin(ClockPlugin plugin) {
|
||||
final String id = plugin.getClass().getName();
|
||||
mClocks.put(plugin.getClass().getName(), plugin);
|
||||
mClockInfo.add(ClockInfo.builder()
|
||||
.setName(plugin.getName())
|
||||
.setTitle(plugin::getTitle)
|
||||
.setId(id)
|
||||
.setThumbnail(plugin::getThumbnail)
|
||||
.setPreview(() -> plugin.getPreview(mWidth, mHeight))
|
||||
.build());
|
||||
}
|
||||
|
||||
private void removeClockPlugin(ClockPlugin plugin) {
|
||||
final String id = plugin.getClass().getName();
|
||||
mClocks.remove(id);
|
||||
for (int i = 0; i < mClockInfo.size(); i++) {
|
||||
if (id.equals(mClockInfo.get(i).getId())) {
|
||||
mClockInfo.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadIfNeeded(ClockPlugin plugin) {
|
||||
final boolean wasCurrentClock = plugin == mCurrentClock;
|
||||
reloadCurrentClock();
|
||||
final boolean isCurrentClock = plugin == mCurrentClock;
|
||||
if (wasCurrentClock || isCurrentClock) {
|
||||
ClockManager.this.reload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current clock.
|
||||
*/
|
||||
void reloadCurrentClock() {
|
||||
mCurrentClock = getClockPlugin();
|
||||
}
|
||||
|
||||
private ClockPlugin getClockPlugin() {
|
||||
ClockPlugin plugin = null;
|
||||
if (ClockManager.this.isDocked()) {
|
||||
final String name = mSettingsWrapper.getDockedClockFace(
|
||||
mUserTracker.getUserId());
|
||||
if (name != null) {
|
||||
plugin = mClocks.get(name);
|
||||
if (plugin != null) {
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
final String name = mSettingsWrapper.getLockScreenCustomClockFace(
|
||||
mUserTracker.getUserId());
|
||||
if (name != null) {
|
||||
plugin = mClocks.get(name);
|
||||
}
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
/**
|
||||
* Exposes custom clock face options and provides realistic preview images.
|
||||
*
|
||||
* APIs:
|
||||
*
|
||||
* /list_options: List the available clock faces, which has the following columns
|
||||
* name: name of the clock face
|
||||
* title: title of the clock face
|
||||
* id: value used to set the clock face
|
||||
* thumbnail: uri of the thumbnail image, should be /thumbnail/{name}
|
||||
* preview: uri of the preview image, should be /preview/{name}
|
||||
*
|
||||
* /thumbnail/{id}: Opens a file stream for the thumbnail image for clock face {id}.
|
||||
*
|
||||
* /preview/{id}: Opens a file stream for the preview image for clock face {id}.
|
||||
*/
|
||||
public final class ClockOptionsProvider extends ContentProvider {
|
||||
|
||||
private static final String TAG = "ClockOptionsProvider";
|
||||
private static final String KEY_LIST_OPTIONS = "/list_options";
|
||||
private static final String KEY_PREVIEW = "preview";
|
||||
private static final String KEY_THUMBNAIL = "thumbnail";
|
||||
private static final String COLUMN_NAME = "name";
|
||||
private static final String COLUMN_TITLE = "title";
|
||||
private static final String COLUMN_ID = "id";
|
||||
private static final String COLUMN_THUMBNAIL = "thumbnail";
|
||||
private static final String COLUMN_PREVIEW = "preview";
|
||||
private static final String MIME_TYPE_PNG = "image/png";
|
||||
private static final String CONTENT_SCHEME = "content";
|
||||
private static final String AUTHORITY = "com.android.keyguard.clock";
|
||||
|
||||
@Inject
|
||||
public Provider<List<ClockInfo>> mClockInfosProvider;
|
||||
|
||||
@VisibleForTesting
|
||||
ClockOptionsProvider(Provider<List<ClockInfo>> clockInfosProvider) {
|
||||
mClockInfosProvider = clockInfosProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
List<String> segments = uri.getPathSegments();
|
||||
if (segments.size() > 0 && (KEY_PREVIEW.equals(segments.get(0))
|
||||
|| KEY_THUMBNAIL.equals(segments.get(0)))) {
|
||||
return MIME_TYPE_PNG;
|
||||
}
|
||||
return "vnd.android.cursor.dir/clock_faces";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) {
|
||||
if (!KEY_LIST_OPTIONS.equals(uri.getPath())) {
|
||||
return null;
|
||||
}
|
||||
MatrixCursor cursor = new MatrixCursor(new String[] {
|
||||
COLUMN_NAME, COLUMN_TITLE, COLUMN_ID, COLUMN_THUMBNAIL, COLUMN_PREVIEW});
|
||||
List<ClockInfo> clocks = mClockInfosProvider.get();
|
||||
for (int i = 0; i < clocks.size(); i++) {
|
||||
ClockInfo clock = clocks.get(i);
|
||||
cursor.newRow()
|
||||
.add(COLUMN_NAME, clock.getName())
|
||||
.add(COLUMN_TITLE, clock.getTitle())
|
||||
.add(COLUMN_ID, clock.getId())
|
||||
.add(COLUMN_THUMBNAIL, createThumbnailUri(clock))
|
||||
.add(COLUMN_PREVIEW, createPreviewUri(clock));
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues initialValues) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
|
||||
List<String> segments = uri.getPathSegments();
|
||||
if (segments.size() != 2 || !(KEY_PREVIEW.equals(segments.get(0))
|
||||
|| KEY_THUMBNAIL.equals(segments.get(0)))) {
|
||||
throw new FileNotFoundException("Invalid preview url");
|
||||
}
|
||||
String id = segments.get(1);
|
||||
if (TextUtils.isEmpty(id)) {
|
||||
throw new FileNotFoundException("Invalid preview url, missing id");
|
||||
}
|
||||
ClockInfo clock = null;
|
||||
List<ClockInfo> clocks = mClockInfosProvider.get();
|
||||
for (int i = 0; i < clocks.size(); i++) {
|
||||
if (id.equals(clocks.get(i).getId())) {
|
||||
clock = clocks.get(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (clock == null) {
|
||||
throw new FileNotFoundException("Invalid preview url, id not found");
|
||||
}
|
||||
return openPipeHelper(uri, MIME_TYPE_PNG, null, KEY_PREVIEW.equals(segments.get(0))
|
||||
? clock.getPreview() : clock.getThumbnail(), new MyWriter());
|
||||
}
|
||||
|
||||
private Uri createThumbnailUri(ClockInfo clock) {
|
||||
return new Uri.Builder()
|
||||
.scheme(CONTENT_SCHEME)
|
||||
.authority(AUTHORITY)
|
||||
.appendPath(KEY_THUMBNAIL)
|
||||
.appendPath(clock.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
private Uri createPreviewUri(ClockInfo clock) {
|
||||
return new Uri.Builder()
|
||||
.scheme(CONTENT_SCHEME)
|
||||
.authority(AUTHORITY)
|
||||
.appendPath(KEY_PREVIEW)
|
||||
.appendPath(clock.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
private static class MyWriter implements ContentProvider.PipeDataWriter<Bitmap> {
|
||||
@Override
|
||||
public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
|
||||
Bundle opts, Bitmap bitmap) {
|
||||
try (AutoCloseOutputStream os = new AutoCloseOutputStream(output)) {
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "fail to write to pipe", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock
|
||||
|
||||
import android.graphics.Color
|
||||
import android.util.MathUtils
|
||||
|
||||
private const val PRIMARY_INDEX = 5
|
||||
private const val SECONDARY_DARK_INDEX = 8
|
||||
private const val SECONDARY_LIGHT_INDEX = 2
|
||||
|
||||
/**
|
||||
* A helper class to extract colors from a clock face.
|
||||
*/
|
||||
class ClockPalette {
|
||||
|
||||
private var darkAmount: Float = 0f
|
||||
private var accentPrimary: Int = Color.WHITE
|
||||
private var accentSecondaryLight: Int = Color.WHITE
|
||||
private var accentSecondaryDark: Int = Color.BLACK
|
||||
private val lightHSV: FloatArray = FloatArray(3)
|
||||
private val darkHSV: FloatArray = FloatArray(3)
|
||||
private val hsv: FloatArray = FloatArray(3)
|
||||
|
||||
/** Returns a color from the palette as an RGB packed int. */
|
||||
fun getPrimaryColor(): Int {
|
||||
return accentPrimary
|
||||
}
|
||||
|
||||
/** Returns either a light or dark color from the palette as an RGB packed int. */
|
||||
fun getSecondaryColor(): Int {
|
||||
Color.colorToHSV(accentSecondaryLight, lightHSV)
|
||||
Color.colorToHSV(accentSecondaryDark, darkHSV)
|
||||
for (i in 0..2) {
|
||||
hsv[i] = MathUtils.lerp(darkHSV[i], lightHSV[i], darkAmount)
|
||||
}
|
||||
return Color.HSVToColor(hsv)
|
||||
}
|
||||
|
||||
/** See {@link ClockPlugin#setColorPalette}. */
|
||||
fun setColorPalette(supportsDarkText: Boolean, colorPalette: IntArray?) {
|
||||
if (colorPalette == null || colorPalette.isEmpty()) {
|
||||
accentPrimary = Color.WHITE
|
||||
accentSecondaryLight = Color.WHITE
|
||||
accentSecondaryDark = if (supportsDarkText) Color.BLACK else Color.WHITE
|
||||
return
|
||||
}
|
||||
val length = colorPalette.size
|
||||
accentPrimary = colorPalette[Math.max(0, length - PRIMARY_INDEX)]
|
||||
accentSecondaryLight = colorPalette[Math.max(0, length - SECONDARY_LIGHT_INDEX)]
|
||||
accentSecondaryDark = colorPalette[Math.max(0,
|
||||
length - if (supportsDarkText) SECONDARY_DARK_INDEX else SECONDARY_LIGHT_INDEX)]
|
||||
}
|
||||
|
||||
/** See {@link ClockPlugin#setDarkAmount}. */
|
||||
fun setDarkAmount(darkAmount: Float) {
|
||||
this.darkAmount = darkAmount
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Controls transition to dark state by cross fading between views.
|
||||
*/
|
||||
final class CrossFadeDarkController {
|
||||
|
||||
private final View mFadeInView;
|
||||
private final View mFadeOutView;
|
||||
|
||||
/**
|
||||
* Creates a new controller that fades between views.
|
||||
*
|
||||
* @param fadeInView View to fade in when transitioning to AOD.
|
||||
* @param fadeOutView View to fade out when transitioning to AOD.
|
||||
*/
|
||||
CrossFadeDarkController(View fadeInView, View fadeOutView) {
|
||||
mFadeInView = fadeInView;
|
||||
mFadeOutView = fadeOutView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the amount the system has transitioned to the dark state.
|
||||
*
|
||||
* @param darkAmount Amount of transition to dark state: 1f for AOD and 0f for lock screen.
|
||||
*/
|
||||
void setDarkAmount(float darkAmount) {
|
||||
mFadeInView.setAlpha(Math.max(0f, 2f * darkAmount - 1f));
|
||||
if (darkAmount == 0f) {
|
||||
mFadeInView.setVisibility(View.GONE);
|
||||
} else {
|
||||
if (mFadeInView.getVisibility() == View.GONE) {
|
||||
mFadeInView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
mFadeOutView.setAlpha(Math.max(0f, 1f - 2f * darkAmount));
|
||||
if (darkAmount == 1f) {
|
||||
mFadeOutView.setVisibility(View.GONE);
|
||||
} else {
|
||||
if (mFadeOutView.getVisibility() == View.GONE) {
|
||||
mFadeOutView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.colorextraction.ColorExtractor;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||
import com.android.systemui.plugins.ClockPlugin;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Plugin for the default clock face used only to provide a preview.
|
||||
*/
|
||||
public class DefaultClockController implements ClockPlugin {
|
||||
|
||||
/**
|
||||
* Resources used to get title and thumbnail.
|
||||
*/
|
||||
private final Resources mResources;
|
||||
|
||||
/**
|
||||
* LayoutInflater used to inflate custom clock views.
|
||||
*/
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
|
||||
/**
|
||||
* Extracts accent color from wallpaper.
|
||||
*/
|
||||
private final SysuiColorExtractor mColorExtractor;
|
||||
|
||||
/**
|
||||
* Renders preview from clock view.
|
||||
*/
|
||||
private final ViewPreviewer mRenderer = new ViewPreviewer();
|
||||
|
||||
/**
|
||||
* Root view of preview.
|
||||
*/
|
||||
private View mView;
|
||||
|
||||
/**
|
||||
* Text clock in preview view hierarchy.
|
||||
*/
|
||||
private TextView mTextTime;
|
||||
|
||||
/**
|
||||
* Date showing below time in preview view hierarchy.
|
||||
*/
|
||||
private TextView mTextDate;
|
||||
|
||||
/**
|
||||
* Create a DefaultClockController instance.
|
||||
*
|
||||
* @param res Resources contains title and thumbnail.
|
||||
* @param inflater Inflater used to inflate custom clock views.
|
||||
* @param colorExtractor Extracts accent color from wallpaper.
|
||||
*/
|
||||
public DefaultClockController(Resources res, LayoutInflater inflater,
|
||||
SysuiColorExtractor colorExtractor) {
|
||||
mResources = res;
|
||||
mLayoutInflater = inflater;
|
||||
mColorExtractor = colorExtractor;
|
||||
}
|
||||
|
||||
private void createViews() {
|
||||
mView = mLayoutInflater.inflate(R.layout.default_clock_preview, null);
|
||||
mTextTime = mView.findViewById(R.id.time);
|
||||
mTextDate = mView.findViewById(R.id.date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
mView = null;
|
||||
mTextTime = null;
|
||||
mTextDate = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return mResources.getString(R.string.clock_title_default);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap getThumbnail() {
|
||||
return BitmapFactory.decodeResource(mResources, R.drawable.default_thumbnail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap getPreview(int width, int height) {
|
||||
|
||||
// Use the big clock view for the preview
|
||||
View view = getBigClockView();
|
||||
|
||||
// Initialize state of plugin before generating preview.
|
||||
setDarkAmount(1f);
|
||||
setTextColor(Color.WHITE);
|
||||
ColorExtractor.GradientColors colors = mColorExtractor.getColors(
|
||||
WallpaperManager.FLAG_LOCK);
|
||||
setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
|
||||
onTimeTick();
|
||||
|
||||
return mRenderer.createPreview(view, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getBigClockView() {
|
||||
if (mView == null) {
|
||||
createViews();
|
||||
}
|
||||
return mView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPreferredY(int totalHeight) {
|
||||
return totalHeight / 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStyle(Style style) {}
|
||||
|
||||
@Override
|
||||
public void setTextColor(int color) {
|
||||
mTextTime.setTextColor(color);
|
||||
mTextDate.setTextColor(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorPalette(boolean supportsDarkText, int[] colorPalette) {}
|
||||
|
||||
@Override
|
||||
public void onTimeTick() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDarkAmount(float darkAmount) {}
|
||||
|
||||
@Override
|
||||
public void onTimeZoneChanged(TimeZone timeZone) {}
|
||||
|
||||
@Override
|
||||
public boolean shouldShowStatusArea() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.format.DateFormat;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Clock composed of two images that rotate with the time.
|
||||
*
|
||||
* The images are the clock hands. ImageClock expects two child ImageViews
|
||||
* with ids hour_hand and minute_hand.
|
||||
*/
|
||||
public class ImageClock extends FrameLayout {
|
||||
|
||||
private ImageView mHourHand;
|
||||
private ImageView mMinuteHand;
|
||||
private final Calendar mTime = Calendar.getInstance(TimeZone.getDefault());
|
||||
private String mDescFormat;
|
||||
private TimeZone mTimeZone;
|
||||
|
||||
public ImageClock(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ImageClock(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ImageClock(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call when the time changes to update the rotation of the clock hands.
|
||||
*/
|
||||
public void onTimeChanged() {
|
||||
mTime.setTimeInMillis(System.currentTimeMillis());
|
||||
final float hourAngle = mTime.get(Calendar.HOUR) * 30f + mTime.get(Calendar.MINUTE) * 0.5f;
|
||||
mHourHand.setRotation(hourAngle);
|
||||
final float minuteAngle = mTime.get(Calendar.MINUTE) * 6f;
|
||||
mMinuteHand.setRotation(minuteAngle);
|
||||
setContentDescription(DateFormat.format(mDescFormat, mTime));
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call when the time zone has changed to update clock hands.
|
||||
*
|
||||
* @param timeZone The updated time zone that will be used.
|
||||
*/
|
||||
public void onTimeZoneChanged(TimeZone timeZone) {
|
||||
mTimeZone = timeZone;
|
||||
mTime.setTimeZone(timeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the colors to use on the clock face.
|
||||
* @param dark Darker color obtained from color palette.
|
||||
* @param light Lighter color obtained from color palette.
|
||||
*/
|
||||
public void setClockColors(int dark, int light) {
|
||||
mHourHand.setColorFilter(dark);
|
||||
mMinuteHand.setColorFilter(light);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mHourHand = findViewById(R.id.hour_hand);
|
||||
mMinuteHand = findViewById(R.id.minute_hand);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
mTime.setTimeZone(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
|
||||
onTimeChanged();
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.ContentResolver;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Wrapper around Settings used for testing.
|
||||
*/
|
||||
public class SettingsWrapper {
|
||||
|
||||
private static final String TAG = "ClockFaceSettings";
|
||||
private static final String CUSTOM_CLOCK_FACE = Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE;
|
||||
private static final String DOCKED_CLOCK_FACE = Settings.Secure.DOCKED_CLOCK_FACE;
|
||||
private static final String CLOCK_FIELD = "clock";
|
||||
|
||||
private final ContentResolver mContentResolver;
|
||||
private final Migration mMigration;
|
||||
|
||||
SettingsWrapper(ContentResolver contentResolver) {
|
||||
this(contentResolver, new Migrator(contentResolver));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
SettingsWrapper(ContentResolver contentResolver, Migration migration) {
|
||||
mContentResolver = contentResolver;
|
||||
mMigration = migration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value stored in settings for the custom clock face.
|
||||
*
|
||||
* @param userId ID of the user.
|
||||
*/
|
||||
String getLockScreenCustomClockFace(int userId) {
|
||||
return decode(
|
||||
Settings.Secure.getStringForUser(mContentResolver, CUSTOM_CLOCK_FACE, userId),
|
||||
userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value stored in settings for the clock face to use when docked.
|
||||
*
|
||||
* @param userId ID of the user.
|
||||
*/
|
||||
String getDockedClockFace(int userId) {
|
||||
return Settings.Secure.getStringForUser(mContentResolver, DOCKED_CLOCK_FACE, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the string stored in settings, which should be formatted as JSON.
|
||||
* @param value String stored in settings. If value is not JSON, then the settings is
|
||||
* overwritten with JSON containing the prior value.
|
||||
* @return ID of the clock face to show on AOD and lock screen. If value is not JSON, the value
|
||||
* is returned.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
String decode(@Nullable String value, int userId) {
|
||||
if (value == null) {
|
||||
return value;
|
||||
}
|
||||
JSONObject json;
|
||||
try {
|
||||
json = new JSONObject(value);
|
||||
} catch (JSONException ex) {
|
||||
Log.e(TAG, "Settings value is not valid JSON", ex);
|
||||
// The settings value isn't JSON since it didn't parse so migrate the value to JSON.
|
||||
// TODO(b/135674383): Remove this migration path in the following release.
|
||||
mMigration.migrate(value, userId);
|
||||
return value;
|
||||
}
|
||||
try {
|
||||
return json.getString(CLOCK_FIELD);
|
||||
} catch (JSONException ex) {
|
||||
Log.e(TAG, "JSON object does not contain clock field.", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
interface Migration {
|
||||
void migrate(String value, int userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@link Migration} that writes valid JSON back to Settings.
|
||||
*/
|
||||
private static final class Migrator implements Migration {
|
||||
|
||||
private final ContentResolver mContentResolver;
|
||||
|
||||
Migrator(ContentResolver contentResolver) {
|
||||
mContentResolver = contentResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate settings values that don't parse by converting to JSON format.
|
||||
*
|
||||
* Values in settings must be JSON to be backed up and restored. To help users maintain
|
||||
* their current settings, convert existing values into the JSON format.
|
||||
*
|
||||
* TODO(b/135674383): Remove this migration code in the following release.
|
||||
*/
|
||||
@Override
|
||||
public void migrate(String value, int userId) {
|
||||
try {
|
||||
JSONObject json = new JSONObject();
|
||||
json.put(CLOCK_FIELD, value);
|
||||
Settings.Secure.putStringForUser(mContentResolver, CUSTOM_CLOCK_FACE,
|
||||
json.toString(),
|
||||
userId);
|
||||
} catch (JSONException ex) {
|
||||
Log.e(TAG, "Failed migrating settings value to JSON format", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.MathUtils;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.policy.SystemBarUtils;
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* Computes preferred position of clock by considering height of status bar and lock icon.
|
||||
*/
|
||||
class SmallClockPosition {
|
||||
|
||||
/**
|
||||
* Dimensions used to determine preferred clock position.
|
||||
*/
|
||||
private final int mStatusBarHeight;
|
||||
private final int mKeyguardLockPadding;
|
||||
private final int mKeyguardLockHeight;
|
||||
private final int mBurnInOffsetY;
|
||||
|
||||
/**
|
||||
* Amount of transition between AOD and lock screen.
|
||||
*/
|
||||
private float mDarkAmount;
|
||||
|
||||
SmallClockPosition(Context context) {
|
||||
this(SystemBarUtils.getStatusBarHeight(context),
|
||||
context.getResources().getDimensionPixelSize(R.dimen.keyguard_lock_padding),
|
||||
context.getResources().getDimensionPixelSize(R.dimen.keyguard_lock_height),
|
||||
context.getResources().getDimensionPixelSize(R.dimen.burn_in_prevention_offset_y)
|
||||
);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
SmallClockPosition(int statusBarHeight, int lockPadding, int lockHeight, int burnInY) {
|
||||
mStatusBarHeight = statusBarHeight;
|
||||
mKeyguardLockPadding = lockPadding;
|
||||
mKeyguardLockHeight = lockHeight;
|
||||
mBurnInOffsetY = burnInY;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link ClockPlugin#setDarkAmount}.
|
||||
*/
|
||||
void setDarkAmount(float darkAmount) {
|
||||
mDarkAmount = darkAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the preferred Y position accounting for status bar and lock icon heights.
|
||||
*/
|
||||
int getPreferredY() {
|
||||
// On AOD, clock needs to appear below the status bar with enough room for pixel shifting
|
||||
int aodY = mStatusBarHeight + mKeyguardLockHeight + 2 * mKeyguardLockPadding
|
||||
+ mBurnInOffsetY;
|
||||
// On lock screen, clock needs to appear below the lock icon
|
||||
int lockY = mStatusBarHeight + mKeyguardLockHeight + 2 * mKeyguardLockPadding;
|
||||
return (int) MathUtils.lerp(lockY, aodY, mDarkAmount);
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
/**
|
||||
* Creates a preview image ({@link Bitmap}) of a {@link View} for a custom clock face.
|
||||
*/
|
||||
final class ViewPreviewer {
|
||||
|
||||
private static final String TAG = "ViewPreviewer";
|
||||
|
||||
/**
|
||||
* Handler used to run {@link View#draw(Canvas)} on the main thread.
|
||||
*/
|
||||
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
/**
|
||||
* Generate a realistic preview of a clock face.
|
||||
*
|
||||
* @param view view is used to generate preview image.
|
||||
* @param width width of the preview image, should be the same as device width in pixels.
|
||||
* @param height height of the preview image, should be the same as device height in pixels.
|
||||
* @return bitmap of view.
|
||||
*/
|
||||
@Nullable
|
||||
Bitmap createPreview(View view, int width, int height) {
|
||||
if (view == null) {
|
||||
return null;
|
||||
}
|
||||
FutureTask<Bitmap> task = new FutureTask<>(new Callable<Bitmap>() {
|
||||
@Override
|
||||
public Bitmap call() {
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
// Draw clock view hierarchy to canvas.
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
canvas.drawColor(Color.BLACK);
|
||||
dispatchVisibilityAggregated(view, true);
|
||||
view.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
|
||||
View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
|
||||
view.layout(0, 0, width, height);
|
||||
view.draw(canvas);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
});
|
||||
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
task.run();
|
||||
} else {
|
||||
mMainHandler.post(task);
|
||||
}
|
||||
|
||||
try {
|
||||
return task.get();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error completing task", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchVisibilityAggregated(View view, boolean isVisible) {
|
||||
// Similar to View.dispatchVisibilityAggregated implementation.
|
||||
final boolean thisVisible = view.getVisibility() == View.VISIBLE;
|
||||
if (thisVisible || !isVisible) {
|
||||
view.onVisibilityAggregated(isVisible);
|
||||
}
|
||||
|
||||
if (view instanceof ViewGroup) {
|
||||
isVisible = thisVisible && isVisible;
|
||||
ViewGroup vg = (ViewGroup) view;
|
||||
int count = vg.getChildCount();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
dispatchVisibilityAggregated(vg.getChildAt(i), isVisible);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,6 @@ import com.android.internal.statusbar.IStatusBarService;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.keyguard.KeyguardSecurityModel;
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.keyguard.clock.ClockManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
|
||||
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
|
||||
@@ -323,7 +322,6 @@ public class Dependency {
|
||||
@Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail;
|
||||
@Inject @Main Lazy<Executor> mMainExecutor;
|
||||
@Inject @Background Lazy<Executor> mBackgroundExecutor;
|
||||
@Inject Lazy<ClockManager> mClockManager;
|
||||
@Inject Lazy<ActivityManagerWrapper> mActivityManagerWrapper;
|
||||
@Inject Lazy<DevicePolicyManagerWrapper> mDevicePolicyManagerWrapper;
|
||||
@Inject Lazy<PackageManagerWrapper> mPackageManagerWrapper;
|
||||
@@ -517,7 +515,6 @@ public class Dependency {
|
||||
mProviders.put(SmartReplyController.class, mSmartReplyController::get);
|
||||
mProviders.put(RemoteInputQuickSettingsDisabler.class,
|
||||
mRemoteInputQuickSettingsDisabler::get);
|
||||
mProviders.put(ClockManager.class, mClockManager::get);
|
||||
mProviders.put(PrivacyItemController.class, mPrivacyItemController::get);
|
||||
mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get);
|
||||
mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get);
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.systemui.dagger;
|
||||
|
||||
import com.android.keyguard.clock.ClockOptionsProvider;
|
||||
import com.android.systemui.BootCompleteCacheImpl;
|
||||
import com.android.systemui.CoreStartable;
|
||||
import com.android.systemui.Dependency;
|
||||
@@ -249,11 +248,6 @@ public interface SysUIComponent {
|
||||
*/
|
||||
void inject(KeyguardSliceProvider keyguardSliceProvider);
|
||||
|
||||
/**
|
||||
* Member injection into the supplied argument.
|
||||
*/
|
||||
void inject(ClockOptionsProvider clockOptionsProvider);
|
||||
|
||||
/**
|
||||
* Member injection into the supplied argument.
|
||||
*/
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.service.dreams.IDreamManager;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.statusbar.IStatusBarService;
|
||||
import com.android.keyguard.clock.ClockInfoModule;
|
||||
import com.android.keyguard.dagger.ClockRegistryModule;
|
||||
import com.android.keyguard.dagger.KeyguardBouncerComponent;
|
||||
import com.android.systemui.BootCompleteCache;
|
||||
@@ -160,7 +159,6 @@ import javax.inject.Named;
|
||||
BiometricsModule.class,
|
||||
BouncerViewModule.class,
|
||||
ClipboardOverlayModule.class,
|
||||
ClockInfoModule.class,
|
||||
ClockRegistryModule.class,
|
||||
CommonRepositoryModule.class,
|
||||
ConnectivityModule.class,
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper
|
||||
public final class AnalogClockControllerTest extends SysuiTestCase {
|
||||
|
||||
private AnalogClockController mClockController;
|
||||
@Mock SysuiColorExtractor mMockColorExtractor;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
Resources res = getContext().getResources();
|
||||
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
|
||||
mClockController = new AnalogClockController(res, layoutInflater,
|
||||
mMockColorExtractor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setDarkAmount_AOD() {
|
||||
ViewGroup smallClockFrame = (ViewGroup) mClockController.getView();
|
||||
View smallClock = smallClockFrame.getChildAt(0);
|
||||
// WHEN dark amount is set to AOD
|
||||
mClockController.setDarkAmount(1f);
|
||||
// THEN small clock should be shown.
|
||||
assertThat(smallClock.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setColorPalette_setDigitalClock() {
|
||||
ViewGroup smallClock = (ViewGroup) mClockController.getView();
|
||||
// WHEN color palette is set
|
||||
mClockController.setColorPalette(true, new int[]{Color.RED});
|
||||
// THEN child of small clock should have text color set.
|
||||
TextView digitalClock = (TextView) smallClock.getChildAt(0);
|
||||
assertThat(digitalClock.getCurrentTextColor()).isEqualTo(Color.RED);
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper
|
||||
public final class BubbleClockControllerTest extends SysuiTestCase {
|
||||
|
||||
private BubbleClockController mClockController;
|
||||
@Mock SysuiColorExtractor mMockColorExtractor;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
Resources res = getContext().getResources();
|
||||
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
|
||||
mClockController = new BubbleClockController(res, layoutInflater, mMockColorExtractor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setDarkAmount_AOD() {
|
||||
ViewGroup smallClockFrame = (ViewGroup) mClockController.getView();
|
||||
View smallClock = smallClockFrame.getChildAt(0);
|
||||
// WHEN dark amount is set to AOD
|
||||
mClockController.setDarkAmount(1f);
|
||||
// THEN small clock should not be shown.
|
||||
assertThat(smallClock.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setColorPalette_setDigitalClock() {
|
||||
ViewGroup smallClock = (ViewGroup) mClockController.getView();
|
||||
// WHEN text color is set
|
||||
mClockController.setColorPalette(true, new int[]{Color.RED});
|
||||
// THEN child of small clock should have text color set.
|
||||
TextView digitalClock = (TextView) smallClock.getChildAt(0);
|
||||
assertThat(digitalClock.getCurrentTextColor()).isEqualTo(Color.RED);
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper
|
||||
public final class ClockInfoTest extends SysuiTestCase {
|
||||
|
||||
@Mock
|
||||
private Supplier<Bitmap> mMockSupplier;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetName() {
|
||||
final String name = "name";
|
||||
ClockInfo info = ClockInfo.builder().setName(name).build();
|
||||
assertThat(info.getName()).isEqualTo(name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTitle() {
|
||||
final String title = "title";
|
||||
ClockInfo info = ClockInfo.builder().setTitle(() -> title).build();
|
||||
assertThat(info.getTitle()).isEqualTo(title);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetId() {
|
||||
final String id = "id";
|
||||
ClockInfo info = ClockInfo.builder().setId(id).build();
|
||||
assertThat(info.getId()).isEqualTo(id);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetThumbnail() {
|
||||
ClockInfo info = ClockInfo.builder().setThumbnail(mMockSupplier).build();
|
||||
info.getThumbnail();
|
||||
verify(mMockSupplier).get();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPreview() {
|
||||
ClockInfo info = ClockInfo.builder().setPreview(mMockSupplier).build();
|
||||
info.getPreview();
|
||||
verify(mMockSupplier).get();
|
||||
}
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||
import com.android.systemui.dock.DockManager;
|
||||
import com.android.systemui.dock.DockManagerFake;
|
||||
import com.android.systemui.plugins.ClockPlugin;
|
||||
import com.android.systemui.plugins.PluginManager;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
import com.android.systemui.util.time.FakeSystemClock;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
// Need to run tests on main looper to allow for onClockChanged operation to happen synchronously.
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
public final class ClockManagerTest extends SysuiTestCase {
|
||||
|
||||
private static final String BUBBLE_CLOCK = BubbleClockController.class.getName();
|
||||
private static final Class<?> BUBBLE_CLOCK_CLASS = BubbleClockController.class;
|
||||
private static final int MAIN_USER_ID = 0;
|
||||
private static final int SECONDARY_USER_ID = 11;
|
||||
private static final Uri SETTINGS_URI = null;
|
||||
|
||||
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
|
||||
private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
|
||||
private ClockManager mClockManager;
|
||||
private ContentObserver mContentObserver;
|
||||
private DockManagerFake mFakeDockManager;
|
||||
private ArgumentCaptor<UserTracker.Callback> mUserTrackerCallbackCaptor;
|
||||
@Mock PluginManager mMockPluginManager;
|
||||
@Mock SysuiColorExtractor mMockColorExtractor;
|
||||
@Mock ContentResolver mMockContentResolver;
|
||||
@Mock UserTracker mUserTracker;
|
||||
@Mock SettingsWrapper mMockSettingsWrapper;
|
||||
@Mock ClockManager.ClockChangedListener mMockListener1;
|
||||
@Mock ClockManager.ClockChangedListener mMockListener2;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
|
||||
mFakeDockManager = new DockManagerFake();
|
||||
|
||||
when(mUserTracker.getUserId()).thenReturn(MAIN_USER_ID);
|
||||
mUserTrackerCallbackCaptor = ArgumentCaptor.forClass(UserTracker.Callback.class);
|
||||
|
||||
mClockManager = new ClockManager(getContext(), inflater,
|
||||
mMockPluginManager, mMockColorExtractor, mMockContentResolver,
|
||||
mUserTracker, mMainExecutor, mMockSettingsWrapper, mFakeDockManager);
|
||||
|
||||
mClockManager.addBuiltinClock(() -> new BubbleClockController(
|
||||
getContext().getResources(), inflater, mMockColorExtractor));
|
||||
mClockManager.addOnClockChangedListener(mMockListener1);
|
||||
mClockManager.addOnClockChangedListener(mMockListener2);
|
||||
verify(mUserTracker).addCallback(mUserTrackerCallbackCaptor.capture(), any());
|
||||
reset(mMockListener1, mMockListener2);
|
||||
|
||||
mContentObserver = mClockManager.getContentObserver();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
mClockManager.removeOnClockChangedListener(mMockListener1);
|
||||
mClockManager.removeOnClockChangedListener(mMockListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dockEvent() {
|
||||
mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED);
|
||||
assertThat(mClockManager.isDocked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void undockEvent() {
|
||||
mFakeDockManager.setDockEvent(DockManager.STATE_NONE);
|
||||
assertThat(mClockManager.isDocked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentClock_default() {
|
||||
// GIVEN that settings doesn't contain any values
|
||||
when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(null);
|
||||
when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn(null);
|
||||
// WHEN settings change event is fired
|
||||
mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID);
|
||||
// THEN the result is null, indicated the default clock face should be used.
|
||||
assertThat(mClockManager.getCurrentClock()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentClock_customClock() {
|
||||
// GIVEN that settings is set to the bubble clock face
|
||||
when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK);
|
||||
// WHEN settings change event is fired
|
||||
mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID);
|
||||
// THEN the plugin is the bubble clock face.
|
||||
assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClockChanged_customClock() {
|
||||
// GIVEN that settings is set to the bubble clock face
|
||||
when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK);
|
||||
// WHEN settings change event is fired
|
||||
mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID);
|
||||
// THEN the plugin is the bubble clock face.
|
||||
ArgumentCaptor<ClockPlugin> captor = ArgumentCaptor.forClass(ClockPlugin.class);
|
||||
verify(mMockListener1).onClockChanged(captor.capture());
|
||||
assertThat(captor.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClockChanged_uniqueInstances() {
|
||||
// GIVEN that settings is set to the bubble clock face
|
||||
when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK);
|
||||
// WHEN settings change event is fired
|
||||
mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID);
|
||||
// THEN the listeners receive separate instances of the Bubble clock plugin.
|
||||
ArgumentCaptor<ClockPlugin> captor1 = ArgumentCaptor.forClass(ClockPlugin.class);
|
||||
ArgumentCaptor<ClockPlugin> captor2 = ArgumentCaptor.forClass(ClockPlugin.class);
|
||||
verify(mMockListener1).onClockChanged(captor1.capture());
|
||||
verify(mMockListener2).onClockChanged(captor2.capture());
|
||||
assertThat(captor1.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS);
|
||||
assertThat(captor2.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS);
|
||||
assertThat(captor1.getValue()).isNotSameInstanceAs(captor2.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentClock_badSettingsValue() {
|
||||
// GIVEN that settings contains a value that doesn't correspond to a
|
||||
// custom clock face.
|
||||
when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn("bad value");
|
||||
// WHEN settings change event is fired
|
||||
mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID);
|
||||
// THEN the result is null.
|
||||
assertThat(mClockManager.getCurrentClock()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentClock_dockedDefault() {
|
||||
// WHEN dock event is fired
|
||||
mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED);
|
||||
// THEN the result is null, indicating the default clock face.
|
||||
assertThat(mClockManager.getCurrentClock()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentClock_dockedCustomClock() {
|
||||
// GIVEN settings is set to the bubble clock face
|
||||
when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn(BUBBLE_CLOCK);
|
||||
// WHEN dock event fires
|
||||
mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED);
|
||||
// THEN the plugin is the bubble clock face.
|
||||
assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentClock_badDockedSettingsValue() {
|
||||
// GIVEN settings contains a value that doesn't correspond to an available clock face.
|
||||
when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn("bad value");
|
||||
// WHEN dock event fires
|
||||
mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED);
|
||||
// THEN the result is null.
|
||||
assertThat(mClockManager.getCurrentClock()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentClock_badDockedSettingsFallback() {
|
||||
// GIVEN settings contains a value that doesn't correspond to an available clock face, but
|
||||
// locked screen settings is set to bubble clock.
|
||||
when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn("bad value");
|
||||
when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK);
|
||||
// WHEN dock event is fired
|
||||
mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED);
|
||||
// THEN the plugin is the bubble clock face.
|
||||
assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onUserChanged_defaultClock() {
|
||||
// WHEN the user changes
|
||||
switchUser(SECONDARY_USER_ID);
|
||||
// THEN the plugin is null for the default clock face
|
||||
assertThat(mClockManager.getCurrentClock()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onUserChanged_customClock() {
|
||||
// GIVEN that a second user has selected the bubble clock face
|
||||
when(mMockSettingsWrapper.getLockScreenCustomClockFace(SECONDARY_USER_ID)).thenReturn(
|
||||
BUBBLE_CLOCK);
|
||||
// WHEN the user changes
|
||||
switchUser(SECONDARY_USER_ID);
|
||||
// THEN the plugin is the bubble clock face.
|
||||
assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onUserChanged_docked() {
|
||||
// GIVEN device is docked
|
||||
mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED);
|
||||
// AND the second user as selected the bubble clock for the dock
|
||||
when(mMockSettingsWrapper.getDockedClockFace(SECONDARY_USER_ID)).thenReturn(BUBBLE_CLOCK);
|
||||
// WHEN the user changes
|
||||
switchUser(SECONDARY_USER_ID);
|
||||
// THEN the plugin is the bubble clock face.
|
||||
assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS);
|
||||
}
|
||||
|
||||
private void switchUser(int newUser) {
|
||||
when(mUserTracker.getUserId()).thenReturn(newUser);
|
||||
mUserTrackerCallbackCaptor.getValue().onUserChanged(newUser, mContext);
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper
|
||||
public final class ClockOptionsProviderTest extends SysuiTestCase {
|
||||
|
||||
private static final String CONTENT_SCHEME = "content";
|
||||
private static final String AUTHORITY = "com.android.keyguard.clock";
|
||||
private static final String LIST_OPTIONS = "list_options";
|
||||
private static final String PREVIEW = "preview";
|
||||
private static final String THUMBNAIL = "thumbnail";
|
||||
private static final String MIME_TYPE_LIST_OPTIONS = "vnd.android.cursor.dir/clock_faces";
|
||||
private static final String MIME_TYPE_PNG = "image/png";
|
||||
private static final String NAME_COLUMN = "name";
|
||||
private static final String TITLE_COLUMN = "title";
|
||||
private static final String ID_COLUMN = "id";
|
||||
private static final String PREVIEW_COLUMN = "preview";
|
||||
private static final String THUMBNAIL_COLUMN = "thumbnail";
|
||||
|
||||
private ClockOptionsProvider mProvider;
|
||||
private Supplier<List<ClockInfo>> mMockSupplier;
|
||||
private List<ClockInfo> mClocks;
|
||||
private Uri mListOptionsUri;
|
||||
@Mock
|
||||
private Supplier<Bitmap> mMockBitmapSupplier;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mClocks = new ArrayList<>();
|
||||
mProvider = new ClockOptionsProvider(() -> mClocks);
|
||||
mListOptionsUri = new Uri.Builder()
|
||||
.scheme(CONTENT_SCHEME)
|
||||
.authority(AUTHORITY)
|
||||
.appendPath(LIST_OPTIONS)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetType_listOptions() {
|
||||
Uri uri = new Uri.Builder()
|
||||
.scheme(CONTENT_SCHEME)
|
||||
.authority(AUTHORITY)
|
||||
.appendPath(LIST_OPTIONS)
|
||||
.build();
|
||||
assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_LIST_OPTIONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetType_preview() {
|
||||
Uri uri = new Uri.Builder()
|
||||
.scheme(CONTENT_SCHEME)
|
||||
.authority(AUTHORITY)
|
||||
.appendPath(PREVIEW)
|
||||
.appendPath("id")
|
||||
.build();
|
||||
assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_PNG);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetType_thumbnail() {
|
||||
Uri uri = new Uri.Builder()
|
||||
.scheme(CONTENT_SCHEME)
|
||||
.authority(AUTHORITY)
|
||||
.appendPath(THUMBNAIL)
|
||||
.appendPath("id")
|
||||
.build();
|
||||
assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_PNG);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuery_noClocks() {
|
||||
Cursor cursor = mProvider.query(mListOptionsUri, null, null, null);
|
||||
assertThat(cursor.getCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuery_listOptions() {
|
||||
mClocks.add(ClockInfo.builder()
|
||||
.setName("name_a")
|
||||
.setTitle(() -> "title_a")
|
||||
.setId("id_a")
|
||||
.build());
|
||||
mClocks.add(ClockInfo.builder()
|
||||
.setName("name_b")
|
||||
.setTitle(() -> "title_b")
|
||||
.setId("id_b")
|
||||
.build());
|
||||
Cursor cursor = mProvider.query(mListOptionsUri, null, null, null);
|
||||
assertThat(cursor.getCount()).isEqualTo(2);
|
||||
cursor.moveToFirst();
|
||||
assertThat(cursor.getString(
|
||||
cursor.getColumnIndex(NAME_COLUMN))).isEqualTo("name_a");
|
||||
assertThat(cursor.getString(
|
||||
cursor.getColumnIndex(TITLE_COLUMN))).isEqualTo("title_a");
|
||||
assertThat(cursor.getString(
|
||||
cursor.getColumnIndex(ID_COLUMN))).isEqualTo("id_a");
|
||||
assertThat(cursor.getString(
|
||||
cursor.getColumnIndex(PREVIEW_COLUMN)))
|
||||
.isEqualTo("content://com.android.keyguard.clock/preview/id_a");
|
||||
assertThat(cursor.getString(
|
||||
cursor.getColumnIndex(THUMBNAIL_COLUMN)))
|
||||
.isEqualTo("content://com.android.keyguard.clock/thumbnail/id_a");
|
||||
cursor.moveToNext();
|
||||
assertThat(cursor.getString(
|
||||
cursor.getColumnIndex(NAME_COLUMN))).isEqualTo("name_b");
|
||||
assertThat(cursor.getString(
|
||||
cursor.getColumnIndex(TITLE_COLUMN))).isEqualTo("title_b");
|
||||
assertThat(cursor.getString(
|
||||
cursor.getColumnIndex(ID_COLUMN))).isEqualTo("id_b");
|
||||
assertThat(cursor.getString(
|
||||
cursor.getColumnIndex(PREVIEW_COLUMN)))
|
||||
.isEqualTo("content://com.android.keyguard.clock/preview/id_b");
|
||||
assertThat(cursor.getString(
|
||||
cursor.getColumnIndex(THUMBNAIL_COLUMN)))
|
||||
.isEqualTo("content://com.android.keyguard.clock/thumbnail/id_b");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenFile_preview() throws Exception {
|
||||
mClocks.add(ClockInfo.builder()
|
||||
.setId("id")
|
||||
.setPreview(mMockBitmapSupplier)
|
||||
.build());
|
||||
Uri uri = new Uri.Builder()
|
||||
.scheme(CONTENT_SCHEME)
|
||||
.authority(AUTHORITY)
|
||||
.appendPath(PREVIEW)
|
||||
.appendPath("id")
|
||||
.build();
|
||||
mProvider.openFile(uri, "r").close();
|
||||
verify(mMockBitmapSupplier).get();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenFile_thumbnail() throws Exception {
|
||||
mClocks.add(ClockInfo.builder()
|
||||
.setId("id")
|
||||
.setThumbnail(mMockBitmapSupplier)
|
||||
.build());
|
||||
Uri uri = new Uri.Builder()
|
||||
.scheme(CONTENT_SCHEME)
|
||||
.authority(AUTHORITY)
|
||||
.appendPath(THUMBNAIL)
|
||||
.appendPath("id")
|
||||
.build();
|
||||
mProvider.openFile(uri, "r").close();
|
||||
verify(mMockBitmapSupplier).get();
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock
|
||||
|
||||
import android.graphics.Color
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
class ClockPaletteTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var clockPalette: ClockPalette
|
||||
private lateinit var colors: IntArray
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
clockPalette = ClockPalette()
|
||||
// colors used are reds from light to dark.
|
||||
val hsv: FloatArray = FloatArray(3)
|
||||
Color.colorToHSV(Color.RED, hsv)
|
||||
colors = IntArray(10)
|
||||
val step: Float = (0f - hsv[2]) / colors.size
|
||||
for (i in 0 until colors.size) {
|
||||
hsv[2] += step
|
||||
colors[i] = Color.HSVToColor(hsv)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDark() {
|
||||
// GIVEN on AOD
|
||||
clockPalette.setDarkAmount(1f)
|
||||
// AND GIVEN that wallpaper doesn't support dark text
|
||||
clockPalette.setColorPalette(false, colors)
|
||||
// THEN the secondary color should be lighter than the primary color
|
||||
assertThat(value(clockPalette.getPrimaryColor()))
|
||||
.isGreaterThan(value(clockPalette.getSecondaryColor()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDarkText() {
|
||||
// GIVEN on lock screen
|
||||
clockPalette.setDarkAmount(0f)
|
||||
// AND GIVEN that wallpaper supports dark text
|
||||
clockPalette.setColorPalette(true, colors)
|
||||
// THEN the secondary color should be darker the primary color
|
||||
assertThat(value(clockPalette.getPrimaryColor()))
|
||||
.isLessThan(value(clockPalette.getSecondaryColor()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLightText() {
|
||||
// GIVEN on lock screen
|
||||
clockPalette.setDarkAmount(0f)
|
||||
// AND GIVEN that wallpaper doesn't support dark text
|
||||
clockPalette.setColorPalette(false, colors)
|
||||
// THEN the secondary color should be darker than the primary color
|
||||
assertThat(value(clockPalette.getPrimaryColor()))
|
||||
.isGreaterThan(value(clockPalette.getSecondaryColor()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNullColors() {
|
||||
// GIVEN on AOD
|
||||
clockPalette.setDarkAmount(1f)
|
||||
// AND GIVEN that wallpaper colors are null
|
||||
clockPalette.setColorPalette(false, null)
|
||||
// THEN the primary color should be whilte
|
||||
assertThat(clockPalette.getPrimaryColor()).isEqualTo(Color.WHITE)
|
||||
}
|
||||
|
||||
private fun value(color: Int): Float {
|
||||
val hsv: FloatArray = FloatArray(3)
|
||||
Color.colorToHSV(color, hsv)
|
||||
return hsv[2]
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper
|
||||
public final class CrossFadeDarkControllerTest extends SysuiTestCase {
|
||||
|
||||
private View mViewFadeIn;
|
||||
private View mViewFadeOut;
|
||||
private CrossFadeDarkController mDarkController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mViewFadeIn = new TextView(getContext());
|
||||
mViewFadeIn.setVisibility(View.VISIBLE);
|
||||
mViewFadeIn.setAlpha(1f);
|
||||
mViewFadeOut = new TextView(getContext());
|
||||
mViewFadeOut.setVisibility(View.VISIBLE);
|
||||
mViewFadeOut.setAlpha(1f);
|
||||
|
||||
mDarkController = new CrossFadeDarkController(mViewFadeIn, mViewFadeOut);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setDarkAmount_fadeIn() {
|
||||
// WHEN dark amount corresponds to AOD
|
||||
mDarkController.setDarkAmount(1f);
|
||||
// THEN fade in view should be faded in and fade out view faded out.
|
||||
assertThat(mViewFadeIn.getAlpha()).isEqualTo(1f);
|
||||
assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(mViewFadeOut.getAlpha()).isEqualTo(0f);
|
||||
assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setDarkAmount_fadeOut() {
|
||||
// WHEN dark amount corresponds to lock screen
|
||||
mDarkController.setDarkAmount(0f);
|
||||
// THEN fade out view should bed faded out and fade in view faded in.
|
||||
assertThat(mViewFadeIn.getAlpha()).isEqualTo(0f);
|
||||
assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.GONE);
|
||||
assertThat(mViewFadeOut.getAlpha()).isEqualTo(1f);
|
||||
assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setDarkAmount_partialFadeIn() {
|
||||
// WHEN dark amount corresponds to a partial transition
|
||||
mDarkController.setDarkAmount(0.9f);
|
||||
// THEN views should have intermediate alpha value.
|
||||
assertThat(mViewFadeIn.getAlpha()).isGreaterThan(0f);
|
||||
assertThat(mViewFadeIn.getAlpha()).isLessThan(1f);
|
||||
assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setDarkAmount_partialFadeOut() {
|
||||
// WHEN dark amount corresponds to a partial transition
|
||||
mDarkController.setDarkAmount(0.1f);
|
||||
// THEN views should have intermediate alpha value.
|
||||
assertThat(mViewFadeOut.getAlpha()).isGreaterThan(0f);
|
||||
assertThat(mViewFadeOut.getAlpha()).isLessThan(1f);
|
||||
assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.json.JSONObject
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
|
||||
private const val PACKAGE = "com.android.keyguard.clock.Clock"
|
||||
private const val CLOCK_FIELD = "clock"
|
||||
private const val TIMESTAMP_FIELD = "_applied_timestamp"
|
||||
private const val USER_ID = 0
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
class SettingsWrapperTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var wrapper: SettingsWrapper
|
||||
private lateinit var migration: SettingsWrapper.Migration
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
migration = mock(SettingsWrapper.Migration::class.java)
|
||||
wrapper = SettingsWrapper(getContext().contentResolver, migration)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDecodeUnnecessary() {
|
||||
// GIVEN a settings value that doesn't need to be decoded
|
||||
val value = PACKAGE
|
||||
// WHEN the value is decoded
|
||||
val decoded = wrapper.decode(value, USER_ID)
|
||||
// THEN the same value is returned, because decoding isn't necessary.
|
||||
// TODO(b/135674383): Null should be returned when the migration code in removed.
|
||||
assertThat(decoded).isEqualTo(value)
|
||||
// AND the value is migrated to JSON format
|
||||
verify(migration).migrate(value, USER_ID)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDecodeJSON() {
|
||||
// GIVEN a settings value that is encoded in JSON
|
||||
val json: JSONObject = JSONObject()
|
||||
json.put(CLOCK_FIELD, PACKAGE)
|
||||
json.put(TIMESTAMP_FIELD, System.currentTimeMillis())
|
||||
val value = json.toString()
|
||||
// WHEN the value is decoded
|
||||
val decoded = wrapper.decode(value, USER_ID)
|
||||
// THEN the clock field should have been extracted
|
||||
assertThat(decoded).isEqualTo(PACKAGE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDecodeJSONWithoutClockField() {
|
||||
// GIVEN a settings value that doesn't contain the CLOCK_FIELD
|
||||
val json: JSONObject = JSONObject()
|
||||
json.put(TIMESTAMP_FIELD, System.currentTimeMillis())
|
||||
val value = json.toString()
|
||||
// WHEN the value is decoded
|
||||
val decoded = wrapper.decode(value, USER_ID)
|
||||
// THEN null is returned
|
||||
assertThat(decoded).isNull()
|
||||
// AND the value is not migrated to JSON format
|
||||
verify(migration, never()).migrate(value, USER_ID)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDecodeNullJSON() {
|
||||
assertThat(wrapper.decode(null, USER_ID)).isNull()
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
class SmallClockPositionTest : SysuiTestCase() {
|
||||
|
||||
private val statusBarHeight = 100
|
||||
private val lockPadding = 15
|
||||
private val lockHeight = 35
|
||||
private val burnInY = 20
|
||||
|
||||
private lateinit var position: SmallClockPosition
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
position = SmallClockPosition(statusBarHeight, lockPadding, lockHeight, burnInY)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadResources() {
|
||||
// Cover constructor taking Resources object.
|
||||
position = SmallClockPosition(context)
|
||||
position.setDarkAmount(1f)
|
||||
assertThat(position.preferredY).isGreaterThan(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun darkPosition() {
|
||||
// GIVEN on AOD
|
||||
position.setDarkAmount(1f)
|
||||
// THEN Y is sum of statusBarHeight, lockPadding, lockHeight, lockPadding, burnInY
|
||||
assertThat(position.preferredY).isEqualTo(185)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun lockPosition() {
|
||||
// GIVEN on lock screen
|
||||
position.setDarkAmount(0f)
|
||||
// THEN Y position is statusBarHeight + lockPadding + lockHeight + lockPadding
|
||||
// (100 + 15 + 35 + 15 = 165)
|
||||
assertThat(position.preferredY).isEqualTo(165)
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.clock
|
||||
|
||||
import android.content.Context
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.view.View
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
class ViewPreviewerTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var previewer: ViewPreviewer
|
||||
private lateinit var view: View
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
previewer = ViewPreviewer()
|
||||
view = TestView(context)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCreatePreview() {
|
||||
val width = 100
|
||||
val height = 100
|
||||
// WHEN a preview image is created
|
||||
val bitmap = previewer.createPreview(view, width, height)!!
|
||||
// THEN the bitmap has the expected width and height
|
||||
assertThat(bitmap.height).isEqualTo(height)
|
||||
assertThat(bitmap.width).isEqualTo(width)
|
||||
assertThat(bitmap.getPixel(0, 0)).isEqualTo(Color.RED)
|
||||
}
|
||||
|
||||
class TestView(context: Context) : View(context) {
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
super.onDraw(canvas)
|
||||
canvas?.drawColor(Color.RED)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user