/* * Copyright (C) 2022 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.complication; import static com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent.DreamHomeControlsModule.DREAM_HOME_CONTROLS_CHIP_VIEW; import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS; import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE; import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK; import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.util.Log; import android.view.View; import android.widget.ImageView; import com.android.internal.logging.UiEventLogger; import com.android.systemui.CoreStartable; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent; import com.android.systemui.controls.ControlsServiceInfo; import com.android.systemui.controls.dagger.ControlsComponent; import com.android.systemui.controls.management.ControlsListingController; import com.android.systemui.controls.ui.ControlsActivity; import com.android.systemui.controls.ui.ControlsUiController; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.SystemUser; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.shared.condition.Monitor; import com.android.systemui.util.ViewController; import com.android.systemui.util.condition.ConditionalCoreStartable; import java.util.List; import javax.inject.Inject; import javax.inject.Named; /** * A dream complication that shows a home controls chip to launch device controls (to control * devices at home like lights and thermostats). */ public class DreamHomeControlsComplication implements Complication { private final Resources mResources; private final DreamHomeControlsComplicationComponent.Factory mComponentFactory; @Inject public DreamHomeControlsComplication( @Main Resources resources, DreamHomeControlsComplicationComponent.Factory componentFactory) { mResources = resources; mComponentFactory = componentFactory; } @Override public ViewHolder createView(ComplicationViewModel model) { return mComponentFactory.create(mResources).getViewHolder(); } @Override public int getRequiredTypeAvailability() { return COMPLICATION_TYPE_HOME_CONTROLS; } /** * {@link CoreStartable} for registering the complication with SystemUI on startup. */ public static class Registrant extends ConditionalCoreStartable { private final DreamHomeControlsComplication mComplication; private final DreamOverlayStateController mDreamOverlayStateController; private final ControlsComponent mControlsComponent; private boolean mOverlayActive = false; // Callback for when the home controls service availability changes. private final ControlsListingController.ControlsListingCallback mControlsCallback = services -> updateHomeControlsComplication(); private final DreamOverlayStateController.Callback mOverlayStateCallback = new DreamOverlayStateController.Callback() { @Override public void onStateChanged() { if (mOverlayActive == mDreamOverlayStateController.isOverlayActive()) { return; } mOverlayActive = !mOverlayActive; if (mOverlayActive) { updateHomeControlsComplication(); } } }; @Inject public Registrant(DreamHomeControlsComplication complication, DreamOverlayStateController dreamOverlayStateController, ControlsComponent controlsComponent, @SystemUser Monitor monitor) { super(monitor); mComplication = complication; mControlsComponent = controlsComponent; mDreamOverlayStateController = dreamOverlayStateController; } @Override public void onStart() { mControlsComponent.getControlsListingController().ifPresent( c -> c.addCallback(mControlsCallback)); mDreamOverlayStateController.addCallback(mOverlayStateCallback); } private void updateHomeControlsComplication() { mControlsComponent.getControlsListingController().ifPresent(c -> { if (isHomeControlsAvailable(c.getCurrentServices())) { mDreamOverlayStateController.addComplication(mComplication); } else { mDreamOverlayStateController.removeComplication(mComplication); } }); } private boolean isHomeControlsAvailable(List controlsServices) { if (controlsServices.isEmpty()) { return false; } final boolean hasFavorites = mControlsComponent.getControlsController() .map(c -> !c.getFavorites().isEmpty()) .orElse(false); boolean hasPanels = false; for (int i = 0; i < controlsServices.size(); i++) { if (controlsServices.get(i).getPanelActivity() != null) { hasPanels = true; break; } } final ControlsComponent.Visibility visibility = mControlsComponent.getVisibility(); return (hasFavorites || hasPanels) && visibility != UNAVAILABLE; } } /** * Contains values/logic associated with the dream complication view. */ public static class DreamHomeControlsChipViewHolder implements ViewHolder { private final ImageView mView; private final ComplicationLayoutParams mLayoutParams; private final DreamHomeControlsChipViewController mViewController; @Inject DreamHomeControlsChipViewHolder( DreamHomeControlsChipViewController dreamHomeControlsChipViewController, @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view, @Named(DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS) ComplicationLayoutParams layoutParams ) { mView = view; mLayoutParams = layoutParams; mViewController = dreamHomeControlsChipViewController; mViewController.init(); } @Override public ImageView getView() { return mView; } @Override public ComplicationLayoutParams getLayoutParams() { return mLayoutParams; } } /** * Controls behavior of the dream complication. */ static class DreamHomeControlsChipViewController extends ViewController { private static final String TAG = "DreamHomeControlsCtrl"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final ActivityStarter mActivityStarter; private final Context mContext; private final ControlsComponent mControlsComponent; private final UiEventLogger mUiEventLogger; @Inject DreamHomeControlsChipViewController( @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view, ActivityStarter activityStarter, Context context, ControlsComponent controlsComponent, UiEventLogger uiEventLogger) { super(view); mActivityStarter = activityStarter; mContext = context; mControlsComponent = controlsComponent; mUiEventLogger = uiEventLogger; } @Override protected void onViewAttached() { mView.setImageResource(mControlsComponent.getTileImageId()); mView.setContentDescription(mContext.getString(mControlsComponent.getTileTitleId())); mView.setOnClickListener(this::onClickHomeControls); } @Override protected void onViewDetached() {} private void onClickHomeControls(View v) { if (DEBUG) Log.d(TAG, "home controls complication tapped"); mUiEventLogger.log(DreamOverlayUiEvent.DREAM_HOME_CONTROLS_TAPPED); final Intent intent = new Intent(mContext, ControlsActivity.class) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(ControlsUiController.EXTRA_ANIMATE, true) .putExtra(ControlsUiController.EXIT_TO_DREAM, true); final ActivityLaunchAnimator.Controller controller = v != null ? ActivityLaunchAnimator.Controller.fromView(v, null /* cujType */) : null; if (mControlsComponent.getVisibility() == AVAILABLE) { // Controls can be made visible. mActivityStarter.startActivity(intent, true /* dismissShade */, controller, true /* showOverLockscreenWhenLocked */); } else if (mControlsComponent.getVisibility() == AVAILABLE_AFTER_UNLOCK) { // Controls can be made visible only after device unlock. mActivityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */, controller); } } } }