diff options
| author | 2019-01-11 08:21:59 +0000 | |
|---|---|---|
| committer | 2019-01-11 08:21:59 +0000 | |
| commit | 1153b780335071540b64a00d07d2a898b9c29af6 (patch) | |
| tree | ecf54139afb5d691beebaef867aa5a3e0cec1ffd | |
| parent | 71839b732dbc2a40bbb02e7cd34de7b194ff98b8 (diff) | |
| parent | 723a80e4fd70ddfb37a881f023c4ced4ad03f775 (diff) | |
Merge "Enable AOD image wallpaper and apply aod mask view."
14 files changed, 1010 insertions, 12 deletions
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 8b97e0e4a107..0edcb3d8eb6a 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -22,7 +22,9 @@ import android.provider.Settings; import android.text.TextUtils; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; /** * Util class to get feature flag information. @@ -37,8 +39,11 @@ public class FeatureFlagUtils { public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid"; public static final String SAFETY_HUB = "settings_safety_hub"; public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press"; + public static final String AOD_IMAGEWALLPAPER_ENABLED = "settings_aod_imagewallpaper_enabled"; private static final Map<String, String> DEFAULT_FLAGS; + private static final Set<String> OBSERVABLE_FLAGS; + static { DEFAULT_FLAGS = new HashMap<>(); DEFAULT_FLAGS.put("settings_audio_switcher", "true"); @@ -54,6 +59,10 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false"); DEFAULT_FLAGS.put(SAFETY_HUB, "false"); DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false"); + DEFAULT_FLAGS.put(AOD_IMAGEWALLPAPER_ENABLED, "false"); + + OBSERVABLE_FLAGS = new HashSet<>(); + OBSERVABLE_FLAGS.add(AOD_IMAGEWALLPAPER_ENABLED); } /** @@ -90,6 +99,16 @@ public class FeatureFlagUtils { */ public static void setEnabled(Context context, String feature, boolean enabled) { SystemProperties.set(FFLAG_OVERRIDE_PREFIX + feature, enabled ? "true" : "false"); + + // Also update Settings.Global if needed so that we can observe it via observer. + if (OBSERVABLE_FLAGS.contains(feature)) { + setObservableFlag(context, feature, enabled); + } + } + + private static void setObservableFlag(Context context, String feature, boolean enabled) { + Settings.Global.putString( + context.getContentResolver(), feature, enabled ? "true" : "false"); } /** diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml index 34c208ab81aa..02062bb7b2c4 100644 --- a/packages/SystemUI/res/layout/super_status_bar.xml +++ b/packages/SystemUI/res/layout/super_status_bar.xml @@ -43,6 +43,14 @@ android:visibility="invisible" /> </com.android.systemui.statusbar.BackDropView> + <com.android.systemui.wallpaper.AodMaskView + android:id="@+id/aod_mask" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:importantForAccessibility="no" + android:visibility="invisible" + sysui:ignoreRightInset="true" /> + <com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_behind" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 59838d23278d..633f8686b804 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -109,5 +109,10 @@ <!-- Optional cancel button on Keyguard --> <item type="id" name="cancel_button"/> + + <!-- AodMaskView transition tag --> + <item type="id" name="aod_mask_transition_progress_tag" /> + <item type="id" name="aod_mask_transition_progress_end_tag" /> + <item type="id" name="aod_mask_transition_progress_start_tag" /> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index fb3c4aa16ef5..72519ba3503c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -219,6 +219,14 @@ public enum ScrimState { public void prepare(ScrimState previousState) { } + /** + * Check if lockscreen wallpaper or music album art exists. + * @return true if lockscreen wallpaper or music album art exists. + */ + public boolean hasBackdrop() { + return mHasBackdrop; + } + public int getIndex() { return mIndex; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 6f877ba90cfa..9abd86d7088b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -70,6 +70,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.content.res.Resources; +import android.database.ContentObserver; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; @@ -98,6 +99,7 @@ import android.service.dreams.IDreamManager; import android.service.notification.StatusBarNotification; import android.util.DisplayMetrics; import android.util.EventLog; +import android.util.FeatureFlagUtils; import android.util.Log; import android.util.Slog; import android.view.Display; @@ -478,8 +480,13 @@ public class StatusBar extends SystemUI implements DemoMode, WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT); final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean( com.android.internal.R.bool.config_dozeSupportsAodWallpaper); + final boolean aodImageWallpaperEnabled = FeatureFlagUtils.isEnabled(mContext, + FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED); + updateAodMaskVisibility(deviceSupportsAodWallpaper && aodImageWallpaperEnabled); + // If WallpaperInfo is null, it must be ImageWallpaper. final boolean supportsAmbientMode = deviceSupportsAodWallpaper - && info != null && info.supportsAmbientMode(); + && (info == null && aodImageWallpaperEnabled + || info != null && info.supportsAmbientMode()); mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode); mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode); @@ -581,6 +588,7 @@ public class StatusBar extends SystemUI implements DemoMode, protected NotificationPresenter mPresenter; private NotificationActivityStarter mNotificationActivityStarter; private boolean mPulsing; + private ContentObserver mFeatureFlagObserver; @Override public void onActiveStateChanged(int code, int uid, String packageName, boolean active) { @@ -697,6 +705,9 @@ public class StatusBar extends SystemUI implements DemoMode, mContext.registerReceiverAsUser(mWallpaperChangedReceiver, UserHandle.ALL, wallpaperChangedFilter, null /* broadcastPermission */, null /* scheduler */); mWallpaperChangedReceiver.onReceive(mContext, null); + mFeatureFlagObserver = new FeatureFlagObserver( + FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED /* feature */, + () -> mWallpaperChangedReceiver.onReceive(mContext, null) /* callback */); // Set up the initial notification state. This needs to happen before CommandQueue.disable() setUpPresenter(); @@ -4415,4 +4426,33 @@ public class StatusBar extends SystemUI implements DemoMode, public @TransitionMode int getStatusBarMode() { return mStatusBarMode; } + + private void updateAodMaskVisibility(boolean supportsAodWallpaper) { + View mask = mStatusBarWindow.findViewById(R.id.aod_mask); + if (mask != null) { + mask.setVisibility(supportsAodWallpaper ? View.VISIBLE : View.INVISIBLE); + } + } + + private final class FeatureFlagObserver extends ContentObserver { + private final Runnable mCallback; + + FeatureFlagObserver(String feature, Runnable callback) { + this(null, feature, callback); + } + + private FeatureFlagObserver(Handler handler, String feature, Runnable callback) { + super(handler); + mCallback = callback; + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(feature), false, this); + } + + @Override + public void onChange(boolean selfChange) { + if (mCallback != null) { + mStatusBarWindow.post(mCallback); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java new file mode 100644 index 000000000000..52cabe278e2d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java @@ -0,0 +1,222 @@ +/* + * 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.wallpaper; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.app.WallpaperManager; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.RectF; +import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManager.DisplayListener; +import android.util.AttributeSet; +import android.util.FeatureFlagUtils; +import android.util.Log; +import android.view.Display; +import android.view.DisplayInfo; +import android.widget.ImageView; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.AnimatableProperty; +import com.android.systemui.statusbar.notification.PropertyAnimator; +import com.android.systemui.statusbar.notification.stack.AnimationProperties; +import com.android.systemui.statusbar.phone.ScrimState; + +/** + * A view that draws mask upon either image wallpaper or music album art in AOD. + */ +public class AodMaskView extends ImageView implements StatusBarStateController.StateListener, + ImageWallpaperTransformer.TransformationListener { + private static final String TAG = AodMaskView.class.getSimpleName(); + private static final int TRANSITION_DURATION = 1000; + + private static final AnimatableProperty TRANSITION_PROGRESS = AnimatableProperty.from( + "transition_progress", + AodMaskView::setTransitionAmount, + AodMaskView::getTransitionAmount, + R.id.aod_mask_transition_progress_tag, + R.id.aod_mask_transition_progress_start_tag, + R.id.aod_mask_transition_progress_end_tag + ); + + private final AnimationProperties mTransitionProperties = new AnimationProperties(); + private final ImageWallpaperTransformer mTransformer; + private final RectF mBounds = new RectF(); + private boolean mChangingStates; + private boolean mNeedMask; + private float mTransitionAmount; + private final WallpaperManager mWallpaperManager; + private final DisplayManager mDisplayManager; + private DisplayListener mDisplayListener = new DisplayListener() { + @Override + public void onDisplayAdded(int displayId) { + } + + @Override + public void onDisplayRemoved(int displayId) { + } + + @Override + public void onDisplayChanged(int displayId) { + // We just support DEFAULT_DISPLAY currently. + if (displayId == Display.DEFAULT_DISPLAY) { + mTransformer.updateDisplayInfo(getDisplayInfo(displayId)); + } + } + }; + + public AodMaskView(Context context) { + this(context, null); + } + + public AodMaskView(Context context, AttributeSet attrs) { + this(context, attrs, null); + } + + @VisibleForTesting + public AodMaskView(Context context, AttributeSet attrs, ImageWallpaperTransformer transformer) { + super(context, attrs); + setClickable(false); + + StatusBarStateController controller = Dependency.get(StatusBarStateController.class); + if (controller != null) { + controller.addCallback(this); + } else { + Log.d(TAG, "Can not get StatusBarStateController!"); + } + + mDisplayManager = (DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE); + mDisplayManager.registerDisplayListener(mDisplayListener, null); + mWallpaperManager = + (WallpaperManager) getContext().getSystemService(Context.WALLPAPER_SERVICE); + + if (transformer == null) { + mTransformer = new ImageWallpaperTransformer(this); + mTransformer.addFilter(new ScrimFilter()); + mTransformer.addFilter(new VignetteFilter()); + mTransformer.updateOffsets(); + mTransformer.updateDisplayInfo(getDisplayInfo(Display.DEFAULT_DISPLAY)); + + mTransitionProperties.setDuration(TRANSITION_DURATION); + mTransitionProperties.setAnimationFinishListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mTransformer.setIsTransiting(false); + } + + @Override + public void onAnimationStart(Animator animation) { + mTransformer.setIsTransiting(true); + } + }); + } else { + // This part should only be hit by test cases. + mTransformer = transformer; + } + } + + private DisplayInfo getDisplayInfo(int displayId) { + DisplayInfo displayInfo = new DisplayInfo(); + mDisplayManager.getDisplay(displayId).getDisplayInfo(displayInfo); + return displayInfo; + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + mBounds.set(0, 0, w, h); + mTransformer.updateOffsets(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (mNeedMask) { + mTransformer.drawTransformedImage(canvas, null /* target */, null /* src */, mBounds); + } + } + + private boolean checkIfNeedMask() { + // We need mask for ImageWallpaper / LockScreen Wallpaper (Music album art). + return mWallpaperManager.getWallpaperInfo() == null || ScrimState.AOD.hasBackdrop(); + } + + @Override + public void onStatePreChange(int oldState, int newState) { + mChangingStates = oldState != newState; + mNeedMask = checkIfNeedMask(); + } + + @Override + public void onStatePostChange() { + mChangingStates = false; + } + + @Override + public void onStateChanged(int newState) { + } + + @Override + public void onDozingChanged(boolean isDozing) { + if (!mNeedMask) { + return; + } + + boolean enabled = checkFeatureIsEnabled(); + mTransformer.updateAmbientModeState(enabled && isDozing); + + if (enabled && !mChangingStates) { + setAnimatorProperty(isDozing); + } else { + invalidate(); + } + } + + private boolean checkFeatureIsEnabled() { + return FeatureFlagUtils.isEnabled( + getContext(), FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED); + } + + @VisibleForTesting + void setAnimatorProperty(boolean isDozing) { + PropertyAnimator.setProperty( + this, + TRANSITION_PROGRESS, + isDozing ? 1f : 0f /* newEndValue */, + mTransitionProperties, + true /* animated */); + } + + @Override + public void onTransformationUpdated() { + invalidate(); + } + + private void setTransitionAmount(float amount) { + mTransitionAmount = amount; + mTransformer.updateTransitionAmount(amount); + } + + private float getTransitionAmount() { + return mTransitionAmount; + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperFilter.java b/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperFilter.java new file mode 100644 index 000000000000..d457dac3e14f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperFilter.java @@ -0,0 +1,81 @@ +/* + * 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.wallpaper; + +import android.animation.ValueAnimator; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.RectF; + +/** + * Abstract filter used by static image wallpaper. + */ +abstract class ImageWallpaperFilter { + protected static final boolean DEBUG = false; + + private ImageWallpaperTransformer mTransformer; + + /** + * Apply this filter to the bitmap before drawing on canvas. + * @param c The canvas that will draw to. + * @param bitmap The bitmap to apply this filter. + * @param src The subset of the bitmap to be drawn. + * @param dest The rectangle that the bitmap will be scaled/translated to fit into. + */ + public abstract void apply(@NonNull Canvas c, @Nullable Bitmap bitmap, + @Nullable Rect src, @NonNull RectF dest); + + /** + * Notifies the occurrence of built-in transition of the animation. + * @param animator The animator which was animated. + */ + public abstract void onAnimatorUpdate(ValueAnimator animator); + + /** + * Notifies the occurrence of another transition of the animation. + * @param amount The transition amount. + */ + public abstract void onTransitionAmountUpdate(float amount); + + /** + * To set the associated transformer. + * @param transformer The transformer that is associated with this filter. + */ + public void setTransformer(ImageWallpaperTransformer transformer) { + if (transformer != null) { + mTransformer = transformer; + } + } + + protected ImageWallpaperTransformer getTransformer() { + return mTransformer; + } + + /** + * Notifies the changing of the offset value of the ImageWallpaper. + * @param force True to force re-evaluate offsets. + * @param xOffset X offset of the ImageWallpaper in percentage. + * @param yOffset Y offset of the ImageWallpaper in percentage. + */ + public void onOffsetsUpdate(boolean force, float xOffset, float yOffset) { + // No-op + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperTransformer.java b/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperTransformer.java new file mode 100644 index 000000000000..25b0b0aaf60b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperTransformer.java @@ -0,0 +1,173 @@ +/* + * 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.wallpaper; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.RectF; +import android.view.DisplayInfo; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class is used to manage the filters that will be applied. + */ +public class ImageWallpaperTransformer { + private static final String TAG = ImageWallpaperTransformer.class.getSimpleName(); + + private DisplayInfo mDisplayInfo; + private final List<ImageWallpaperFilter> mFilters; + private final TransformationListener mListener; + private boolean mIsInAmbientMode; + private boolean mIsTransiting; + + /** + * Constructor. + * @param listener A listener to inform you the transformation has updated. + */ + public ImageWallpaperTransformer(TransformationListener listener) { + mFilters = new ArrayList<>(); + mListener = listener; + } + + /** + * Claim that we want to use the specified filter. + * @param filter The filter will be used. + */ + public void addFilter(ImageWallpaperFilter filter) { + if (filter != null) { + filter.setTransformer(this); + mFilters.add(filter); + } + } + + /** + * Check if any transition is running. + * @return True if the transition is running, false otherwise. + */ + boolean isTransiting() { + return mIsTransiting; + } + + /** + * Indicate if any transition is running. <br/> + * @param isTransiting True if the transition is running. + */ + void setIsTransiting(boolean isTransiting) { + mIsTransiting = isTransiting; + } + + /** + * Check if the device is in ambient mode. + * @return True if the device is in ambient mode, false otherwise. + */ + public boolean isInAmbientMode() { + return mIsInAmbientMode; + } + + /** + * Update current state of ambient mode. + * @param isInAmbientMode Current ambient mode state. + */ + public void updateAmbientModeState(boolean isInAmbientMode) { + mIsInAmbientMode = isInAmbientMode; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + int idx = 0; + for (ImageWallpaperFilter filter : mFilters) { + sb.append(idx++).append(": ").append(filter.getClass().getSimpleName()).append("\n"); + } + if (sb.length() == 0) { + sb.append("No filters applied"); + } + return sb.toString(); + } + + /** + * Set a new display info. + * @param displayInfo New display info. + */ + public void updateDisplayInfo(DisplayInfo displayInfo) { + mDisplayInfo = displayInfo; + } + + /** + * To get current display info. + * @return Current display info. + */ + public DisplayInfo getDisplayInfo() { + return mDisplayInfo; + } + + /** + * Update the offsets with default value. + */ + public void updateOffsets() { + this.updateOffsets(true, 0f, .5f); + } + + /** + * To notify the filters that the offset of the ImageWallpaper changes. + * @param force True to force re-evaluate offsets. + * @param offsetX X offset of the ImageWallpaper in percentage. + * @param offsetY Y offset of the ImageWallpaper in percentage. + */ + public void updateOffsets(boolean force, float offsetX, float offsetY) { + mFilters.forEach(filter -> filter.onOffsetsUpdate(force, offsetX, offsetY)); + } + + /** + * Apply all specified filters to the bitmap then draw to the canvas. + * @param c The canvas that will draw to. + * @param target The bitmap to apply filters. + * @param src The subset of the bitmap to be drawn + * @param dest The rectangle that the bitmap will be scaled/translated to fit into. + */ + void drawTransformedImage(@NonNull Canvas c, @Nullable Bitmap target, + @Nullable Rect src, @NonNull RectF dest) { + mFilters.forEach(filter -> filter.apply(c, target, src, dest)); + } + + /** + * Update the transition amount. <br/> + * Must invoke this to update transition amount if not running built-in transition. + * @param amount The transition amount. + */ + void updateTransitionAmount(float amount) { + mFilters.forEach(filter -> filter.onTransitionAmountUpdate(amount)); + if (mListener != null) { + mListener.onTransformationUpdated(); + } + } + + /** + * An interface that informs the transformation status. + */ + public interface TransformationListener { + /** + * Notifies the update of the transformation. + */ + void onTransformationUpdated(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/ScrimFilter.java b/packages/SystemUI/src/com/android/systemui/wallpaper/ScrimFilter.java new file mode 100644 index 000000000000..637e48e67de6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallpaper/ScrimFilter.java @@ -0,0 +1,67 @@ +/* + * 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.wallpaper; + +import android.animation.ValueAnimator; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; + +/** + * A filter that implements 70% black scrim effect. + */ +public class ScrimFilter extends ImageWallpaperFilter { + private static final int MAX_ALPHA = (int) (255 * .7f); + private static final int MIN_ALPHA = 0; + + private final Paint mPaint; + + public ScrimFilter() { + mPaint = new Paint(); + mPaint.setColor(Color.BLACK); + mPaint.setAlpha(MAX_ALPHA); + } + + @Override + public void apply(Canvas c, Bitmap bitmap, Rect src, RectF dest) { + ImageWallpaperTransformer transformer = getTransformer(); + + // If it is not in the transition, we need to set the property according to aod state. + if (!transformer.isTransiting()) { + mPaint.setAlpha(transformer.isInAmbientMode() ? MAX_ALPHA : MIN_ALPHA); + } + + c.drawRect(dest, mPaint); + } + + @Override + public void onAnimatorUpdate(ValueAnimator animator) { + ImageWallpaperTransformer transformer = getTransformer(); + float fraction = animator.getAnimatedFraction(); + float factor = transformer.isInAmbientMode() ? fraction : 1f - fraction; + mPaint.setAlpha((int) (factor * MAX_ALPHA)); + } + + @Override + public void onTransitionAmountUpdate(float amount) { + mPaint.setAlpha((int) (amount * MAX_ALPHA)); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/VignetteFilter.java b/packages/SystemUI/src/com/android/systemui/wallpaper/VignetteFilter.java new file mode 100644 index 000000000000..ad0b98b67e68 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallpaper/VignetteFilter.java @@ -0,0 +1,124 @@ +/* + * 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.wallpaper; + +import android.animation.ValueAnimator; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PointF; +import android.graphics.RadialGradient; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader; +import android.util.Log; +import android.view.DisplayInfo; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * A filter that implements vignette effect. + */ +public class VignetteFilter extends ImageWallpaperFilter { + private static final String TAG = VignetteFilter.class.getSimpleName(); + private static final int MAX_ALPHA = 255; + private static final int MIN_ALPHA = 0; + + private final Paint mPaint; + private final Matrix mMatrix; + private final Shader mShader; + + private float mXOffset; + private float mYOffset; + private float mCenterX; + private float mCenterY; + private float mStretchX; + private float mStretchY; + private boolean mCalculateOffsetNeeded; + + public VignetteFilter() { + mPaint = new Paint(); + mMatrix = new Matrix(); + mShader = new RadialGradient(0, 0, 1, + Color.TRANSPARENT, Color.BLACK, Shader.TileMode.CLAMP); + } + + @Override + public void apply(Canvas c, Bitmap bitmap, Rect src, RectF dest) { + DisplayInfo info = getTransformer().getDisplayInfo(); + + if (mCalculateOffsetNeeded) { + int lw = info.logicalWidth; + int lh = info.logicalHeight; + mCenterX = lw / 2 + (dest.width() - lw) * mXOffset; + mCenterY = lh / 2 + (dest.height() - lh) * mYOffset; + mStretchX = info.logicalWidth / 2; + mStretchY = info.logicalHeight / 2; + mCalculateOffsetNeeded = false; + } + + if (DEBUG) { + Log.d(TAG, "apply: lw=" + info.logicalWidth + ", lh=" + info.logicalHeight + + ", center=(" + mCenterX + "," + mCenterY + ")" + + ", stretch=(" + mStretchX + "," + mStretchY + ")"); + } + + mMatrix.reset(); + mMatrix.postTranslate(mCenterX, mCenterY); + mMatrix.postScale(mStretchX, mStretchY, mCenterX, mCenterY); + mShader.setLocalMatrix(mMatrix); + mPaint.setShader(mShader); + + ImageWallpaperTransformer transformer = getTransformer(); + + // If it is not in the transition, we need to set the property according to aod state. + if (!transformer.isTransiting()) { + mPaint.setAlpha(transformer.isInAmbientMode() ? MAX_ALPHA : MIN_ALPHA); + } + + c.drawRect(dest, mPaint); + } + + @Override + public void onAnimatorUpdate(ValueAnimator animator) { + ImageWallpaperTransformer transformer = getTransformer(); + float fraction = animator.getAnimatedFraction(); + float factor = transformer.isInAmbientMode() ? fraction : 1f - fraction; + mPaint.setAlpha((int) (factor * MAX_ALPHA)); + } + + @Override + public void onTransitionAmountUpdate(float amount) { + mPaint.setAlpha((int) (amount * MAX_ALPHA)); + } + + @Override + public void onOffsetsUpdate(boolean force, float xOffset, float yOffset) { + if (force || mXOffset != xOffset || mYOffset != yOffset) { + mXOffset = xOffset; + mYOffset = yOffset; + mCalculateOffsetNeeded = true; + } + } + + @VisibleForTesting + public PointF getCenterPoint() { + return new PointF(mCenterX, mCenterY); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java index 521d5d10a621..53ad0b5132c0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java @@ -16,17 +16,12 @@ package com.android.systemui; - -import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.graphics.Bitmap; @@ -58,13 +53,15 @@ public class ImageWallpaperTest extends SysuiTestCase { @Mock private SurfaceHolder mSurfaceHolder; @Mock private DisplayInfo mDisplayInfo; - CountDownLatch mEventCountdown; + private CountDownLatch mEventCountdown; + private CountDownLatch mAmbientEventCountdown; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mEventCountdown = new CountDownLatch(1); + mAmbientEventCountdown = new CountDownLatch(2); mImageWallpaper = new ImageWallpaper() { @Override @@ -86,6 +83,11 @@ public class ImageWallpaperTest extends SysuiTestCase { assertTrue("mFixedSizeAllowed should be true", allowed); mEventCountdown.countDown(); } + + @Override + public void onAmbientModeChanged(boolean inAmbientMode, long duration) { + mAmbientEventCountdown.countDown(); + } }; } }; @@ -132,4 +134,23 @@ public class ImageWallpaperTest extends SysuiTestCase { verify(mSurfaceHolder, times(1)).setFixedSize(ImageWallpaper.DrawableEngine.MIN_BACKGROUND_WIDTH, ImageWallpaper.DrawableEngine.MIN_BACKGROUND_HEIGHT); } + @Test + public void testDeliversAmbientModeChanged() { + ImageWallpaper.DrawableEngine wallpaperEngine = + (ImageWallpaper.DrawableEngine) mImageWallpaper.onCreateEngine(); + + assertEquals("setFixedSizeAllowed should have been called.", + 0, mEventCountdown.getCount()); + + wallpaperEngine.setCreated(true); + wallpaperEngine.doAmbientModeChanged(false, 1000); + assertFalse("ambient mode should be false", wallpaperEngine.isInAmbientMode()); + assertEquals("onAmbientModeChanged should have been called.", + 1, mAmbientEventCountdown.getCount()); + + wallpaperEngine.doAmbientModeChanged(true, 1000); + assertTrue("ambient mode should be true", wallpaperEngine.isInAmbientMode()); + assertEquals("onAmbientModeChanged should have been called.", + 0, mAmbientEventCountdown.getCount()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpaper/AodMaskViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpaper/AodMaskViewTest.java new file mode 100644 index 000000000000..c44a366e683c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/wallpaper/AodMaskViewTest.java @@ -0,0 +1,128 @@ +/* + * 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.wallpaper; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.app.WallpaperManager; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.RectF; +import android.hardware.display.DisplayManager; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.FeatureFlagUtils; +import android.view.DisplayInfo; +import android.view.WindowManager; + +import com.android.systemui.SysuiTestCase; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AodMaskViewTest extends SysuiTestCase { + private AodMaskView mMaskView; + private DisplayInfo mDisplayInfo; + private ImageWallpaperTransformer mTransformer; + + @Before + public void setUp() throws Exception { + DisplayManager displayManager = + spy((DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE)); + doNothing().when(displayManager).registerDisplayListener(any(), any()); + mContext.addMockSystemService(DisplayManager.class, displayManager); + + WallpaperManager wallpaperManager = + spy((WallpaperManager) mContext.getSystemService(Context.WALLPAPER_SERVICE)); + doReturn(null).when(wallpaperManager).getWallpaperInfo(); + mContext.addMockSystemService(WallpaperManager.class, wallpaperManager); + + mTransformer = spy(new ImageWallpaperTransformer(null /* listener */)); + mMaskView = spy(new AodMaskView(getContext(), null /* attrs */, mTransformer)); + mDisplayInfo = new DisplayInfo(); + + ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)) + .getDefaultDisplay().getDisplayInfo(mDisplayInfo); + + FeatureFlagUtils.setEnabled( + mContext, FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED, true); + } + + @After + public void tearDown() { + FeatureFlagUtils.setEnabled( + mContext, FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED, false); + } + + @Test + public void testCreateMaskView_TransformerIsNotNull() { + assertNotNull("mTransformer should not be null", mTransformer); + } + + @Test + public void testAodMaskView_ShouldNotClickable() { + assertFalse("MaskView should not be clickable", mMaskView.isClickable()); + } + + @Test + public void testAodMaskView_OnSizeChange_ShouldUpdateTransformerOffsets() { + mMaskView.onSizeChanged(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight, 0, 0); + verify(mTransformer, times(1)).updateOffsets(); + } + + @Test + public void testAodMaskView_OnDraw_ShouldDrawTransformedImage() { + Canvas c = new Canvas(); + RectF bounds = new RectF(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); + mMaskView.onSizeChanged(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight, 0, 0); + mMaskView.onStatePreChange(0, 1); + mMaskView.onDraw(c); + verify(mTransformer, times(1)).drawTransformedImage(c, null, null, bounds); + } + + @Test + public void testAodMaskView_IsDozing_ShouldUpdateAmbientModeState() { + doNothing().when(mMaskView).setAnimatorProperty(anyBoolean()); + mMaskView.onStatePreChange(0, 1); + mMaskView.onDozingChanged(true); + verify(mTransformer, times(1)).updateAmbientModeState(true); + } + + @Test + public void testAodMaskView_IsDozing_ShouldDoTransitionOrDrawFinalFrame() { + doNothing().when(mMaskView).setAnimatorProperty(anyBoolean()); + mMaskView.onStatePreChange(0, 1); + mMaskView.onDozingChanged(true); + mMaskView.onStatePostChange(); + mMaskView.onDozingChanged(false); + verify(mMaskView, times(1)).invalidate(); + verify(mMaskView, times(1)).setAnimatorProperty(false); + } + +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpaper/ImageWallpaperTransformerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpaper/ImageWallpaperTransformerTest.java new file mode 100644 index 000000000000..55b0aaefff51 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/wallpaper/ImageWallpaperTransformerTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2009 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.wallpaper; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.PointF; +import android.graphics.RectF; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; +import android.view.DisplayInfo; +import android.view.WindowManager; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ImageWallpaperTransformerTest extends SysuiTestCase { + private DisplayInfo mDisplayInfo; + private Bitmap mBitmap; + private Canvas mCanvas; + private RectF mDestination; + + @Before + public void setUp() throws Exception { + mDisplayInfo = new DisplayInfo(); + ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)) + .getDefaultDisplay().getDisplayInfo(mDisplayInfo); + int dimension = Math.max(mDisplayInfo.logicalHeight, mDisplayInfo.logicalWidth); + mBitmap = Bitmap.createBitmap(dimension, dimension, Bitmap.Config.ARGB_8888); + mCanvas = new Canvas(mBitmap); + mCanvas.drawColor(Color.RED); + mDestination = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); + } + + @Test + public void testVignetteFilter() { + VignetteFilter vignette = new VignetteFilter(); + + ImageWallpaperTransformer transformer = getTransformer(vignette); + transformer.drawTransformedImage(mCanvas, mBitmap, null, mDestination); + + PointF center = vignette.getCenterPoint(); + int p1 = mBitmap.getPixel((int) center.x, (int) center.y); + int p2 = mBitmap.getPixel(0, 0); + int p3 = mBitmap.getPixel(mBitmap.getWidth() - 1, mBitmap.getHeight() - 1); + + assertThat(p1).isEqualTo(Color.RED); + assertThat(p2 | p3).isEqualTo(Color.BLACK); + } + + @Test + public void testScrimFilter() { + getTransformer(new ScrimFilter()) + .drawTransformedImage(mCanvas, mBitmap, null, mDestination); + + int pixel = mBitmap.getPixel(0, 0); + + // 0xff4d0000 is the result of 70% alpha pre-multiplied which is 0.7*(0,0,0)+0.3*(255,0,0). + assertThat(pixel).isEqualTo(0xff4d0000); + } + + private ImageWallpaperTransformer getTransformer(ImageWallpaperFilter filter) { + ImageWallpaperTransformer transformer = new ImageWallpaperTransformer(null); + transformer.addFilter(filter); + transformer.updateDisplayInfo(mDisplayInfo); + transformer.updateOffsets(); + transformer.updateAmbientModeState(true); + return transformer; + } +} diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 410f864b5893..fcc828461bc3 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -84,6 +84,7 @@ import android.service.wallpaper.WallpaperService; import android.system.ErrnoException; import android.system.Os; import android.util.EventLog; +import android.util.FeatureFlagUtils; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -2219,8 +2220,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub synchronized (mLock) { mInAmbientMode = inAmbientMode; final WallpaperData data = mWallpaperMap.get(mCurrentUserId); - if (data != null && data.connection != null && data.connection.mInfo != null - && data.connection.mInfo.supportsAmbientMode()) { + final boolean hasConnection = data != null && data.connection != null; + final WallpaperInfo info = hasConnection ? data.connection.mInfo : null; + + // The wallpaper info is null for image wallpaper, also use the engine in this case. + if (hasConnection && (info == null && isAodImageWallpaperEnabled() + || info != null && info.supportsAmbientMode())) { // TODO(multi-display) Extends this method with specific display. engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine; } else { @@ -2237,6 +2242,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + private boolean isAodImageWallpaperEnabled() { + return FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED); + } + @Override public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) { checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW); |