diff --git a/packages/SystemUI/res/drawable/ic_signal_wifi_off.xml b/packages/SystemUI/res/drawable/ic_signal_wifi_off.xml
new file mode 100644
index 0000000000000..a93abb71c9d75
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_wifi_off.xml
@@ -0,0 +1,24 @@
+
+
+
+
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
new file mode 100644
index 0000000000000..b611ffa6d84e1
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c7350a1eee8b3..ddec6cb373e50 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1303,4 +1303,9 @@
16dp
100%
+
+
+ 80dp
+ 40dp
+ 8dp
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerView.java
new file mode 100644
index 0000000000000..bc1f772e14bb3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerView.java
@@ -0,0 +1,44 @@
+/*
+ * 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.systemui.dreams;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+/**
+ * {@link DreamOverlayContainerView} contains a dream overlay and its status bar.
+ */
+public class DreamOverlayContainerView extends ConstraintLayout {
+ public DreamOverlayContainerView(Context context) {
+ this(context, null);
+ }
+
+ public DreamOverlayContainerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DreamOverlayContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DreamOverlayContainerView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 8f0ea2fb2f878..20ccacc49fc68 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -29,11 +29,11 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import androidx.annotation.NonNull;
-import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.PhoneWindow;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import java.util.concurrent.Executor;
@@ -54,10 +54,12 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
private final Executor mExecutor;
// The state controller informs the service of updates to the overlays present.
private final DreamOverlayStateController mStateController;
+ // The component used to resolve dream overlay dependencies.
+ private final DreamOverlayComponent mDreamOverlayComponent;
- // The window is populated once the dream informs the service it has begun dreaming.
- private Window mWindow;
- private ConstraintLayout mLayout;
+ // The dream overlay's content view, which is located below the status bar (in z-order) and is
+ // the space into which widgets are placed.
+ private ViewGroup mDreamOverlayContentView;
private final DreamOverlayStateController.Callback mOverlayStateCallback =
new DreamOverlayStateController.Callback() {
@@ -90,12 +92,12 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
new ViewTreeObserver.OnComputeInternalInsetsListener() {
@Override
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
- if (mLayout != null) {
+ if (mDreamOverlayContentView != null) {
inoutInfo.setTouchableInsets(
ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
final Region region = new Region();
- for (int i = 0; i < mLayout.getChildCount(); i++) {
- View child = mLayout.getChildAt(i);
+ for (int i = 0; i < mDreamOverlayContentView.getChildCount(); i++) {
+ View child = mDreamOverlayContentView.getChildAt(i);
final Rect rect = new Rect();
child.getGlobalVisibleRect(rect);
region.op(rect, Region.Op.UNION);
@@ -106,16 +108,30 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
}
};
+ @Inject
+ public DreamOverlayService(
+ Context context,
+ @Main Executor executor,
+ DreamOverlayStateController overlayStateController,
+ DreamOverlayComponent.Factory dreamOverlayComponentFactory) {
+ mContext = context;
+ mExecutor = executor;
+ mStateController = overlayStateController;
+ mDreamOverlayComponent = dreamOverlayComponentFactory.create();
+
+ mStateController.addCallback(mOverlayStateCallback);
+ }
+
@Override
public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
mExecutor.execute(() -> addOverlayWindowLocked(layoutParams));
}
private void reloadOverlaysLocked() {
- if (mLayout == null) {
+ if (mDreamOverlayContentView == null) {
return;
}
- mLayout.removeAllViews();
+ mDreamOverlayContentView.removeAllViews();
for (OverlayProvider overlayProvider : mStateController.getOverlays()) {
addOverlay(overlayProvider);
}
@@ -129,31 +145,30 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
* into the dream window.
*/
private void addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) {
- mWindow = new PhoneWindow(mContext);
- mWindow.setAttributes(layoutParams);
- mWindow.setWindowManager(null, layoutParams.token, "DreamOverlay", true);
+ final PhoneWindow window = new PhoneWindow(mContext);
+ window.setAttributes(layoutParams);
+ window.setWindowManager(null, layoutParams.token, "DreamOverlay", true);
- mWindow.setBackgroundDrawable(new ColorDrawable(0));
+ window.setBackgroundDrawable(new ColorDrawable(0));
- mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
- mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+ window.requestFeature(Window.FEATURE_NO_TITLE);
// Hide all insets when the dream is showing
- mWindow.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
- mWindow.setDecorFitsSystemWindows(false);
+ window.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
+ window.setDecorFitsSystemWindows(false);
if (DEBUG) {
Log.d(TAG, "adding overlay window to dream");
}
- mLayout = new ConstraintLayout(mContext);
- mLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- mLayout.addOnAttachStateChangeListener(mRootViewAttachListener);
- mWindow.setContentView(mLayout);
+ window.setContentView(mDreamOverlayComponent.getDreamOverlayContainerView());
+ mDreamOverlayContentView = mDreamOverlayComponent.getDreamOverlayContentView();
+
+ mDreamOverlayComponent.getDreamOverlayStatusBarViewController().init();
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
- windowManager.addView(mWindow.getDecorView(), mWindow.getAttributes());
+ windowManager.addView(window.getDecorView(), window.getAttributes());
mExecutor.execute(this::reloadOverlaysLocked);
}
@@ -163,11 +178,11 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
(view, layoutParams) -> {
// Always move UI related work to the main thread.
mExecutor.execute(() -> {
- if (mLayout == null) {
+ if (mDreamOverlayContentView == null) {
return;
}
- mLayout.addView(view, layoutParams);
+ mDreamOverlayContentView.addView(view, layoutParams);
});
},
() -> {
@@ -178,15 +193,6 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
});
}
- @Inject
- public DreamOverlayService(Context context, @Main Executor executor,
- DreamOverlayStateController overlayStateController) {
- mContext = context;
- mExecutor = executor;
- mStateController = overlayStateController;
- mStateController.addCallback(mOverlayStateCallback);
- }
-
@Override
public void onDestroy() {
mStateController.removeCallback(mOverlayStateCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
new file mode 100644
index 0000000000000..9847ef633bc17
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
@@ -0,0 +1,86 @@
+/*
+ * 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.systemui.dreams;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
+import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
+
+/**
+ * {@link DreamOverlayStatusBarView} is the view responsible for displaying the status bar in a
+ * dream. The status bar includes status icons such as battery and wifi.
+ */
+public class DreamOverlayStatusBarView extends ConstraintLayout implements
+ BatteryStateChangeCallback {
+
+ private BatteryMeterView mBatteryView;
+ private ImageView mWifiStatusView;
+
+ public DreamOverlayStatusBarView(Context context) {
+ this(context, null);
+ }
+
+ public DreamOverlayStatusBarView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DreamOverlayStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DreamOverlayStatusBarView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mBatteryView = Preconditions.checkNotNull(findViewById(R.id.dream_overlay_battery),
+ "R.id.dream_overlay_battery must not be null");
+ mWifiStatusView = Preconditions.checkNotNull(findViewById(R.id.dream_overlay_wifi_status),
+ "R.id.dream_overlay_wifi_status must not be null");
+
+ mWifiStatusView.setImageDrawable(getContext().getDrawable(R.drawable.ic_signal_wifi_off));
+ }
+
+ /**
+ * Whether to show the battery percent text next to the battery status icons.
+ * @param show True if the battery percent text should be shown.
+ */
+ void showBatteryPercentText(boolean show) {
+ mBatteryView.setForceShowPercent(show);
+ }
+
+ /**
+ * Whether to show the wifi status icon.
+ * @param show True if the wifi status icon should be shown.
+ */
+ void showWifiStatus(boolean show) {
+ // Only show the wifi status icon when wifi isn't available.
+ mWifiStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
new file mode 100644
index 0000000000000..5674b9f3f9fd6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -0,0 +1,172 @@
+/*
+ * 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.systemui.dreams;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.dreams.dagger.DreamOverlayModule;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.util.ViewController;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * View controller for {@link DreamOverlayStatusBarView}.
+ */
+@DreamOverlayComponent.DreamOverlayScope
+public class DreamOverlayStatusBarViewController extends ViewController {
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "WIFI_STATUS_" }, value = {
+ WIFI_STATUS_UNKNOWN,
+ WIFI_STATUS_UNAVAILABLE,
+ WIFI_STATUS_AVAILABLE
+ })
+ private @interface WifiStatus {}
+ private static final int WIFI_STATUS_UNKNOWN = 0;
+ private static final int WIFI_STATUS_UNAVAILABLE = 1;
+ private static final int WIFI_STATUS_AVAILABLE = 2;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "BATTERY_STATUS_" }, value = {
+ BATTERY_STATUS_UNKNOWN,
+ BATTERY_STATUS_NOT_CHARGING,
+ BATTERY_STATUS_CHARGING
+ })
+ private @interface BatteryStatus {}
+ private static final int BATTERY_STATUS_UNKNOWN = 0;
+ private static final int BATTERY_STATUS_NOT_CHARGING = 1;
+ private static final int BATTERY_STATUS_CHARGING = 2;
+
+ private final BatteryController mBatteryController;
+ private final BatteryMeterViewController mBatteryMeterViewController;
+ private final ConnectivityManager mConnectivityManager;
+ private final boolean mShowPercentAvailable;
+
+ private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
+
+ private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+ @Override
+ public void onCapabilitiesChanged(
+ Network network, NetworkCapabilities networkCapabilities) {
+ onWifiAvailabilityChanged(
+ networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
+ }
+
+ @Override
+ public void onAvailable(Network network) {
+ onWifiAvailabilityChanged(true);
+ }
+
+ @Override
+ public void onLost(Network network) {
+ onWifiAvailabilityChanged(false);
+ }
+ };
+
+ private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
+ new BatteryController.BatteryStateChangeCallback() {
+ @Override
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+ DreamOverlayStatusBarViewController.this.onBatteryLevelChanged(charging);
+ }
+ };
+
+ private @WifiStatus int mWifiStatus = WIFI_STATUS_UNKNOWN;
+ private @BatteryStatus int mBatteryStatus = BATTERY_STATUS_UNKNOWN;
+
+ @Inject
+ public DreamOverlayStatusBarViewController(
+ Context context,
+ DreamOverlayStatusBarView view,
+ BatteryController batteryController,
+ @Named(DreamOverlayModule.DREAM_OVERLAY_BATTERY_CONTROLLER)
+ BatteryMeterViewController batteryMeterViewController,
+ ConnectivityManager connectivityManager) {
+ super(view);
+ mBatteryController = batteryController;
+ mBatteryMeterViewController = batteryMeterViewController;
+ mConnectivityManager = connectivityManager;
+
+ mShowPercentAvailable = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_battery_percentage_setting_available);
+ }
+
+ @Override
+ protected void onInit() {
+ super.onInit();
+ mBatteryMeterViewController.init();
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mBatteryController.addCallback(mBatteryStateChangeCallback);
+ mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
+
+ NetworkCapabilities capabilities =
+ mConnectivityManager.getNetworkCapabilities(
+ mConnectivityManager.getActiveNetwork());
+ onWifiAvailabilityChanged(
+ capabilities != null
+ && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mBatteryController.removeCallback(mBatteryStateChangeCallback);
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ }
+
+ /**
+ * Wifi availability has changed. Update the wifi status icon as appropriate.
+ * @param available Whether wifi is available.
+ */
+ private void onWifiAvailabilityChanged(boolean available) {
+ final int newWifiStatus = available ? WIFI_STATUS_AVAILABLE : WIFI_STATUS_UNAVAILABLE;
+ if (mWifiStatus != newWifiStatus) {
+ mWifiStatus = newWifiStatus;
+ mView.showWifiStatus(mWifiStatus == WIFI_STATUS_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * The battery level has changed. Update the battery status icon as appropriate.
+ * @param charging Whether the battery is currently charging.
+ */
+ private void onBatteryLevelChanged(boolean charging) {
+ final int newBatteryStatus =
+ charging ? BATTERY_STATUS_CHARGING : BATTERY_STATUS_NOT_CHARGING;
+ if (mBatteryStatus != newBatteryStatus) {
+ mBatteryStatus = newBatteryStatus;
+ mView.showBatteryPercentText(
+ mBatteryStatus == BATTERY_STATUS_CHARGING && mShowPercentAvailable);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 7bf2361e471c4..ff5beb54bd898 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -21,8 +21,6 @@ import dagger.Module;
/**
* Dagger Module providing Communal-related functionality.
*/
-@Module(subcomponents = {
- AppWidgetOverlayComponent.class,
-})
+@Module(subcomponents = {AppWidgetOverlayComponent.class, DreamOverlayComponent.class})
public interface DreamModule {
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
new file mode 100644
index 0000000000000..a3a446a0dbcaa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
@@ -0,0 +1,62 @@
+/*
+ * 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.systemui.dreams.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.view.ViewGroup;
+
+import com.android.systemui.dreams.DreamOverlayContainerView;
+import com.android.systemui.dreams.DreamOverlayStatusBarViewController;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Scope;
+
+import dagger.Subcomponent;
+
+/**
+ * Dagger subcomponent for {@link DreamOverlayModule}.
+ */
+@Subcomponent(modules = {DreamOverlayModule.class})
+@DreamOverlayComponent.DreamOverlayScope
+public interface DreamOverlayComponent {
+ /** Simple factory for {@link DreamOverlayComponent}. */
+ @Subcomponent.Factory
+ interface Factory {
+ DreamOverlayComponent create();
+ }
+
+ /** Scope annotation for singleton items within the {@link DreamOverlayComponent}. */
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface DreamOverlayScope {}
+
+ /** Builds a {@link DreamOverlayContainerView} */
+ @DreamOverlayScope
+ DreamOverlayContainerView getDreamOverlayContainerView();
+
+ /** Builds a content view for dream overlays */
+ @DreamOverlayScope
+ ViewGroup getDreamOverlayContentView();
+
+ /** Builds a {@link DreamOverlayStatusBarViewController}. */
+ @DreamOverlayScope
+ DreamOverlayStatusBarViewController getDreamOverlayStatusBarViewController();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
new file mode 100644
index 0000000000000..d0a8fad9b1e58
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -0,0 +1,105 @@
+/*
+ * 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.systemui.dreams.dagger;
+
+import android.content.ContentResolver;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.DreamOverlayContainerView;
+import com.android.systemui.dreams.DreamOverlayStatusBarView;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.tuner.TunerService;
+
+import javax.inject.Named;
+
+import dagger.Module;
+import dagger.Provides;
+
+/** Dagger module for {@link DreamOverlayComponent}. */
+@Module
+public abstract class DreamOverlayModule {
+ private static final String DREAM_OVERLAY_BATTERY_VIEW = "dream_overlay_battery_view";
+ public static final String DREAM_OVERLAY_BATTERY_CONTROLLER =
+ "dream_overlay_battery_controller";
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ public static DreamOverlayContainerView providesDreamOverlayContainerView(
+ LayoutInflater layoutInflater) {
+ return Preconditions.checkNotNull((DreamOverlayContainerView)
+ layoutInflater.inflate(R.layout.dream_overlay_container, null),
+ "R.layout.dream_layout_container could not be properly inflated");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ public static ViewGroup providesDreamOverlayContentView(DreamOverlayContainerView view) {
+ return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_content),
+ "R.id.dream_overlay_content must not be null");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ public static DreamOverlayStatusBarView providesDreamOverlayStatusBarView(
+ DreamOverlayContainerView view) {
+ return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_status_bar),
+ "R.id.status_bar must not be null");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ @Named(DREAM_OVERLAY_BATTERY_VIEW)
+ static BatteryMeterView providesBatteryMeterView(DreamOverlayContainerView view) {
+ return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_battery),
+ "R.id.battery must not be null");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ @Named(DREAM_OVERLAY_BATTERY_CONTROLLER)
+ static BatteryMeterViewController providesBatteryMeterViewController(
+ @Named(DREAM_OVERLAY_BATTERY_VIEW) BatteryMeterView batteryMeterView,
+ ConfigurationController configurationController,
+ TunerService tunerService,
+ BroadcastDispatcher broadcastDispatcher,
+ @Main Handler mainHandler,
+ ContentResolver contentResolver,
+ BatteryController batteryController) {
+ return new BatteryMeterViewController(
+ batteryMeterView,
+ configurationController,
+ tunerService,
+ broadcastDispatcher,
+ mainHandler,
+ contentResolver,
+ batteryController);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 53bfeee9135ab..d0ec3f36427ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -37,6 +37,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
+import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -78,16 +79,41 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
@Mock
DreamOverlayStateController mDreamOverlayStateController;
+ @Mock
+ DreamOverlayComponent.Factory mDreamOverlayStatusBarViewComponentFactory;
+
+ @Mock
+ DreamOverlayComponent mDreamOverlayComponent;
+
+ @Mock
+ DreamOverlayStatusBarViewController mDreamOverlayStatusBarViewController;
+
+ @Mock
+ DreamOverlayContainerView mDreamOverlayContainerView;
+
+ @Mock
+ ViewGroup mDreamOverlayContentView;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext.addMockSystemService(WindowManager.class, mWindowManager);
+
+ when(mDreamOverlayComponent.getDreamOverlayContentView())
+ .thenReturn(mDreamOverlayContentView);
+ when(mDreamOverlayComponent.getDreamOverlayContainerView())
+ .thenReturn(mDreamOverlayContainerView);
+ when(mDreamOverlayComponent.getDreamOverlayStatusBarViewController())
+ .thenReturn(mDreamOverlayStatusBarViewController);
+ when(mDreamOverlayStatusBarViewComponentFactory.create())
+ .thenReturn(mDreamOverlayComponent);
+
}
@Test
public void testInteraction() throws Exception {
final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
- mDreamOverlayStateController);
+ mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory);
final IBinder proxy = service.onBind(new Intent());
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
clearInvocations(mWindowManager);
@@ -128,7 +154,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
@Test
public void testListening() throws Exception {
final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
- mDreamOverlayStateController);
+ mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory);
final IBinder proxy = service.onBind(new Intent());
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
@@ -150,4 +176,19 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
// Verify provider is asked to create overlay.
verify(mProvider).onCreateOverlay(any(), any(), any());
}
+
+ @Test
+ public void testDreamOverlayStatusBarViewControllerInitialized() throws Exception {
+ final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
+ mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory);
+
+ final IBinder proxy = service.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+ mMainExecutor.runAllReady();
+
+ verify(mDreamOverlayStatusBarViewController).init();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
new file mode 100644
index 0000000000000..7f72dda441d24
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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.systemui.dreams;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.statusbar.policy.BatteryController;
+
+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;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
+
+ @Mock
+ DreamOverlayStatusBarView mView;
+ @Mock
+ BatteryController mBatteryController;
+ @Mock
+ BatteryMeterViewController mBatteryMeterViewController;
+ @Mock
+ ConnectivityManager mConnectivityManager;
+ @Mock
+ NetworkCapabilities mNetworkCapabilities;
+ @Mock
+ Network mNetwork;
+
+ DreamOverlayStatusBarViewController mController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mController = new DreamOverlayStatusBarViewController(
+ mContext, mView, mBatteryController, mBatteryMeterViewController,
+ mConnectivityManager);
+ }
+
+ @Test
+ public void testOnInitInitializesControllers() {
+ mController.onInit();
+ verify(mBatteryMeterViewController).init();
+ }
+
+ @Test
+ public void testOnViewAttachedAddsBatteryControllerCallback() {
+ mController.onViewAttached();
+ verify(mBatteryController)
+ .addCallback(any(BatteryController.BatteryStateChangeCallback.class));
+ }
+
+ @Test
+ public void testOnViewAttachedRegistersNetworkCallback() {
+ mController.onViewAttached();
+ verify(mConnectivityManager)
+ .registerNetworkCallback(any(NetworkRequest.class), any(
+ ConnectivityManager.NetworkCallback.class));
+ }
+
+ @Test
+ public void testOnViewAttachedShowsWifiStatusWhenWifiUnavailable() {
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(false);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+ verify(mView).showWifiStatus(true);
+ }
+
+ @Test
+ public void testOnViewAttachedHidesWifiStatusWhenWifiAvailable() {
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(true);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+ verify(mView).showWifiStatus(false);
+ }
+
+ @Test
+ public void testOnViewAttachedShowsWifiStatusWhenNetworkCapabilitiesUnavailable() {
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(null);
+ mController.onViewAttached();
+ verify(mView).showWifiStatus(true);
+ }
+
+ @Test
+ public void testOnViewDetachedRemovesBatteryControllerCallback() {
+ mController.onViewDetached();
+ verify(mBatteryController)
+ .removeCallback(any(BatteryController.BatteryStateChangeCallback.class));
+ }
+
+ @Test
+ public void testOnViewDetachedUnregistersNetworkCallback() {
+ mController.onViewDetached();
+ verify(mConnectivityManager)
+ .unregisterNetworkCallback(any(ConnectivityManager.NetworkCallback.class));
+ }
+
+ @Test
+ public void testBatteryPercentTextShownWhenBatteryLevelChangesWhileCharging() {
+ final ArgumentCaptor callbackCapture =
+ ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback.class);
+ mController.onViewAttached();
+ verify(mBatteryController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onBatteryLevelChanged(1, true, true);
+ verify(mView).showBatteryPercentText(true);
+ }
+
+ @Test
+ public void testBatteryPercentTextHiddenWhenBatteryLevelChangesWhileNotCharging() {
+ final ArgumentCaptor callbackCapture =
+ ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback.class);
+ mController.onViewAttached();
+ verify(mBatteryController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onBatteryLevelChanged(1, true, false);
+ verify(mView).showBatteryPercentText(false);
+ }
+
+ @Test
+ public void testWifiStatusHiddenWhenWifiBecomesAvailable() {
+ // Make sure wifi starts out unavailable when onViewAttached is called.
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(false);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+
+ final ArgumentCaptor callbackCapture =
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
+ callbackCapture.getValue().onAvailable(mNetwork);
+ verify(mView).showWifiStatus(false);
+ }
+
+ @Test
+ public void testWifiStatusShownWhenWifiBecomesUnavailable() {
+ // Make sure wifi starts out available when onViewAttached is called.
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(true);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+
+ final ArgumentCaptor callbackCapture =
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
+ callbackCapture.getValue().onLost(mNetwork);
+ verify(mView).showWifiStatus(true);
+ }
+
+ @Test
+ public void testWifiStatusHiddenWhenCapabilitiesChange() {
+ // Make sure wifi starts out unavailable when onViewAttached is called.
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(false);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+
+ final ArgumentCaptor callbackCapture =
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(true);
+ callbackCapture.getValue().onCapabilitiesChanged(mNetwork, mNetworkCapabilities);
+ verify(mView).showWifiStatus(false);
+ }
+}