Wire the new FooterActions implementation behind a flag
This CL wires the new footer actions implementation from ag/19678215 behind a new SysUI flag. This flag will be enabled by default in a follow-up CL. Bug: 242040009 Test: atest FooterActionsViewModelTest Test: atest FooterActionsScreenshotTest Change-Id: If23711e400a86ad247666a1532cb17616c01ed85
This commit is contained in:
@@ -7,3 +7,11 @@ packages/SystemUI/src/com/android/systemui/keyguard/dagger
|
|||||||
packages/SystemUI/src/com/android/systemui/keyguard/domain
|
packages/SystemUI/src/com/android/systemui/keyguard/domain
|
||||||
packages/SystemUI/src/com/android/systemui/keyguard/shared
|
packages/SystemUI/src/com/android/systemui/keyguard/shared
|
||||||
packages/SystemUI/src/com/android/systemui/keyguard/ui
|
packages/SystemUI/src/com/android/systemui/keyguard/ui
|
||||||
|
packages/SystemUI/src/com/android/systemui/qs/footer
|
||||||
|
packages/SystemUI/src/com/android/systemui/security
|
||||||
|
packages/SystemUI/src/com/android/systemui/common/
|
||||||
|
packages/SystemUI/tests/utils/src/com/android/systemui/qs/
|
||||||
|
packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt
|
||||||
|
packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeUserInfoController.kt
|
||||||
|
packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/MockUserSwitcherControllerWrapper.kt
|
||||||
|
packages/SystemUI/tests/src/com/android/systemui/qs/footer/
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
<!-- TODO(b/242040009): Remove this file. -->
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- Action buttons for footer in QS/QQS, containing settings button, power off button etc -->
|
<!-- Action buttons for footer in QS/QQS, containing settings button, power off button etc -->
|
||||||
|
<!-- TODO(b/242040009): Clean up this file. -->
|
||||||
<com.android.systemui.qs.FooterActionsView
|
<com.android.systemui.qs.FooterActionsView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
android:paddingBottom="@dimen/qs_footer_actions_bottom_padding"
|
android:paddingBottom="@dimen/qs_footer_actions_bottom_padding"
|
||||||
android:background="@drawable/qs_footer_actions_background"
|
android:background="@drawable/qs_footer_actions_background"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical|end"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="@dimen/qs_footer_action_button_size"
|
||||||
|
android:layout_height="@dimen/qs_footer_action_button_size"
|
||||||
|
android:visibility="gone">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/icon"
|
||||||
|
android:layout_width="@dimen/qs_footer_icon_size"
|
||||||
|
android:layout_height="@dimen/qs_footer_icon_size"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:scaleType="centerInside" />
|
||||||
|
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="@dimen/qs_footer_action_button_size"
|
||||||
|
android:layout_height="@dimen/qs_footer_action_button_size"
|
||||||
|
android:background="@drawable/qs_footer_action_circle"
|
||||||
|
android:visibility="gone">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/number"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/TextAppearance.QS.SecurityFooter"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textSize="18sp"/>
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/new_dot"
|
||||||
|
android:layout_width="12dp"
|
||||||
|
android:layout_height="12dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:src="@drawable/fgs_dot"
|
||||||
|
android:contentDescription="@string/fgs_dot_content_description" />
|
||||||
|
</FrameLayout>
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="@dimen/qs_security_footer_single_line_height"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="@dimen/qs_footer_padding"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_marginEnd="@dimen/qs_footer_action_inset"
|
||||||
|
android:background="@drawable/qs_security_footer_background"
|
||||||
|
android:visibility="gone">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/icon"
|
||||||
|
android:layout_width="@dimen/qs_footer_icon_size"
|
||||||
|
android:layout_height="@dimen/qs_footer_icon_size"
|
||||||
|
android:gravity="start"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
android:src="@drawable/ic_info_outline"
|
||||||
|
android:tint="?android:attr/textColorSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:textAppearance="@style/TextAppearance.QS.SecurityFooter"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/new_dot"
|
||||||
|
android:layout_width="12dp"
|
||||||
|
android:layout_height="12dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/fgs_dot"
|
||||||
|
android:contentDescription="@string/fgs_dot_content_description"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/chevron_icon"
|
||||||
|
android:layout_width="@dimen/qs_footer_icon_size"
|
||||||
|
android:layout_height="@dimen/qs_footer_icon_size"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
android:src="@*android:drawable/ic_chevron_end"
|
||||||
|
android:autoMirrored="true"
|
||||||
|
android:tint="?android:attr/textColorSecondary" />
|
||||||
|
</LinearLayout>
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
<!-- TODO(b/242040009): Remove this file. -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* 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.common.ui
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import com.android.systemui.common.shared.model.ContentDescription
|
||||||
|
|
||||||
|
object ContentDescriptionViewBinder {
|
||||||
|
fun bind(
|
||||||
|
contentDescription: ContentDescription,
|
||||||
|
view: View,
|
||||||
|
) {
|
||||||
|
when (contentDescription) {
|
||||||
|
is ContentDescription.Loaded -> view.contentDescription = contentDescription.description
|
||||||
|
is ContentDescription.Resource -> {
|
||||||
|
view.contentDescription = view.context.resources.getString(contentDescription.res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,8 +48,12 @@ import com.android.systemui.navigationbar.NavigationBarComponent;
|
|||||||
import com.android.systemui.people.PeopleModule;
|
import com.android.systemui.people.PeopleModule;
|
||||||
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
|
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
|
||||||
import com.android.systemui.privacy.PrivacyModule;
|
import com.android.systemui.privacy.PrivacyModule;
|
||||||
|
import com.android.systemui.qs.FgsManagerController;
|
||||||
|
import com.android.systemui.qs.FgsManagerControllerImpl;
|
||||||
|
import com.android.systemui.qs.footer.dagger.FooterActionsModule;
|
||||||
import com.android.systemui.recents.Recents;
|
import com.android.systemui.recents.Recents;
|
||||||
import com.android.systemui.screenshot.dagger.ScreenshotModule;
|
import com.android.systemui.screenshot.dagger.ScreenshotModule;
|
||||||
|
import com.android.systemui.security.data.repository.SecurityRepositoryModule;
|
||||||
import com.android.systemui.settings.dagger.MultiUserUtilsModule;
|
import com.android.systemui.settings.dagger.MultiUserUtilsModule;
|
||||||
import com.android.systemui.shade.ShadeController;
|
import com.android.systemui.shade.ShadeController;
|
||||||
import com.android.systemui.smartspace.dagger.SmartspaceModule;
|
import com.android.systemui.smartspace.dagger.SmartspaceModule;
|
||||||
@@ -121,6 +125,7 @@ import dagger.Provides;
|
|||||||
DemoModeModule.class,
|
DemoModeModule.class,
|
||||||
FalsingModule.class,
|
FalsingModule.class,
|
||||||
FlagsModule.class,
|
FlagsModule.class,
|
||||||
|
FooterActionsModule.class,
|
||||||
LogModule.class,
|
LogModule.class,
|
||||||
MediaProjectionModule.class,
|
MediaProjectionModule.class,
|
||||||
PeopleHubModule.class,
|
PeopleHubModule.class,
|
||||||
@@ -131,6 +136,7 @@ import dagger.Provides;
|
|||||||
ScreenshotModule.class,
|
ScreenshotModule.class,
|
||||||
SensorModule.class,
|
SensorModule.class,
|
||||||
MultiUserUtilsModule.class,
|
MultiUserUtilsModule.class,
|
||||||
|
SecurityRepositoryModule.class,
|
||||||
SettingsUtilModule.class,
|
SettingsUtilModule.class,
|
||||||
SmartRepliesInflationModule.class,
|
SmartRepliesInflationModule.class,
|
||||||
SmartspaceModule.class,
|
SmartspaceModule.class,
|
||||||
@@ -256,4 +262,7 @@ public abstract class SystemUIModule {
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract FgsManagerController bindFgsManagerController(FgsManagerControllerImpl impl);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,6 +147,8 @@ public class Flags {
|
|||||||
public static final ResourceBooleanFlag FULL_SCREEN_USER_SWITCHER =
|
public static final ResourceBooleanFlag FULL_SCREEN_USER_SWITCHER =
|
||||||
new ResourceBooleanFlag(506, R.bool.config_enableFullscreenUserSwitcher);
|
new ResourceBooleanFlag(506, R.bool.config_enableFullscreenUserSwitcher);
|
||||||
|
|
||||||
|
public static final UnreleasedFlag NEW_FOOTER_ACTIONS = new UnreleasedFlag(507);
|
||||||
|
|
||||||
/***************************************/
|
/***************************************/
|
||||||
// 600- status bar
|
// 600- status bar
|
||||||
public static final ResourceBooleanFlag STATUS_BAR_USER_SWITCHER =
|
public static final ResourceBooleanFlag STATUS_BAR_USER_SWITCHER =
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ import javax.inject.Provider
|
|||||||
* determined by [buttonsVisibleState]
|
* determined by [buttonsVisibleState]
|
||||||
*/
|
*/
|
||||||
@QSScope
|
@QSScope
|
||||||
|
// TODO(b/242040009): Remove this file.
|
||||||
internal class FooterActionsController @Inject constructor(
|
internal class FooterActionsController @Inject constructor(
|
||||||
view: FooterActionsView,
|
view: FooterActionsView,
|
||||||
multiUserSwitchControllerFactory: MultiUserSwitchController.Factory,
|
multiUserSwitchControllerFactory: MultiUserSwitchController.Factory,
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import com.android.systemui.statusbar.phone.MultiUserSwitch
|
|||||||
* in split shade mode visible also in collapsed state. May contain up to 5 buttons: settings,
|
* in split shade mode visible also in collapsed state. May contain up to 5 buttons: settings,
|
||||||
* edit tiles, power off and conditionally: user switch and tuner
|
* edit tiles, power off and conditionally: user switch and tuner
|
||||||
*/
|
*/
|
||||||
|
// TODO(b/242040009): Remove this file.
|
||||||
class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) {
|
class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) {
|
||||||
private lateinit var settingsContainer: View
|
private lateinit var settingsContainer: View
|
||||||
private lateinit var multiUserSwitch: MultiUserSwitch
|
private lateinit var multiUserSwitch: MultiUserSwitch
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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.qs
|
||||||
|
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/** Controller for the footer actions. This manages the initialization of its dependencies. */
|
||||||
|
@SysUISingleton
|
||||||
|
class NewFooterActionsController
|
||||||
|
@Inject
|
||||||
|
// TODO(b/242040009): Rename this to FooterActionsController.
|
||||||
|
constructor(
|
||||||
|
private val fgsManagerController: FgsManagerController,
|
||||||
|
) {
|
||||||
|
fun init() {
|
||||||
|
fgsManagerController.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,6 +41,7 @@ import javax.inject.Named;
|
|||||||
/**
|
/**
|
||||||
* Footer entry point for the foreground service manager
|
* Footer entry point for the foreground service manager
|
||||||
*/
|
*/
|
||||||
|
// TODO(b/242040009): Remove this file.
|
||||||
@QSScope
|
@QSScope
|
||||||
public class QSFgsManagerFooter implements View.OnClickListener,
|
public class QSFgsManagerFooter implements View.OnClickListener,
|
||||||
FgsManagerController.OnDialogDismissedListener,
|
FgsManagerController.OnDialogDismissedListener,
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ import android.view.ViewTreeObserver;
|
|||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
import androidx.lifecycle.Lifecycle;
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
import androidx.lifecycle.LifecycleRegistry;
|
||||||
|
|
||||||
import com.android.keyguard.BouncerPanelExpansionCalculator;
|
import com.android.keyguard.BouncerPanelExpansionCalculator;
|
||||||
import com.android.systemui.Dumpable;
|
import com.android.systemui.Dumpable;
|
||||||
@@ -43,6 +46,8 @@ import com.android.systemui.R;
|
|||||||
import com.android.systemui.animation.Interpolators;
|
import com.android.systemui.animation.Interpolators;
|
||||||
import com.android.systemui.animation.ShadeInterpolation;
|
import com.android.systemui.animation.ShadeInterpolation;
|
||||||
import com.android.systemui.dump.DumpManager;
|
import com.android.systemui.dump.DumpManager;
|
||||||
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
|
import com.android.systemui.flags.Flags;
|
||||||
import com.android.systemui.media.MediaHost;
|
import com.android.systemui.media.MediaHost;
|
||||||
import com.android.systemui.plugins.FalsingManager;
|
import com.android.systemui.plugins.FalsingManager;
|
||||||
import com.android.systemui.plugins.qs.QS;
|
import com.android.systemui.plugins.qs.QS;
|
||||||
@@ -50,6 +55,8 @@ import com.android.systemui.plugins.qs.QSContainerController;
|
|||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||||
import com.android.systemui.qs.customize.QSCustomizerController;
|
import com.android.systemui.qs.customize.QSCustomizerController;
|
||||||
import com.android.systemui.qs.dagger.QSFragmentComponent;
|
import com.android.systemui.qs.dagger.QSFragmentComponent;
|
||||||
|
import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
|
||||||
|
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
|
||||||
import com.android.systemui.statusbar.CommandQueue;
|
import com.android.systemui.statusbar.CommandQueue;
|
||||||
import com.android.systemui.statusbar.StatusBarState;
|
import com.android.systemui.statusbar.StatusBarState;
|
||||||
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
|
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
|
||||||
@@ -104,6 +111,10 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
private final QSFragmentComponent.Factory mQsComponentFactory;
|
private final QSFragmentComponent.Factory mQsComponentFactory;
|
||||||
private final QSFragmentDisableFlagsLogger mQsFragmentDisableFlagsLogger;
|
private final QSFragmentDisableFlagsLogger mQsFragmentDisableFlagsLogger;
|
||||||
private final QSTileHost mHost;
|
private final QSTileHost mHost;
|
||||||
|
private final FeatureFlags mFeatureFlags;
|
||||||
|
private final NewFooterActionsController mNewFooterActionsController;
|
||||||
|
private final FooterActionsViewModel.Factory mFooterActionsViewModelFactory;
|
||||||
|
private final ListeningAndVisibilityLifecycleOwner mListeningAndVisibilityLifecycleOwner;
|
||||||
private boolean mShowCollapsedOnKeyguard;
|
private boolean mShowCollapsedOnKeyguard;
|
||||||
private boolean mLastKeyguardAndExpanded;
|
private boolean mLastKeyguardAndExpanded;
|
||||||
/**
|
/**
|
||||||
@@ -119,8 +130,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
private QSPanelController mQSPanelController;
|
private QSPanelController mQSPanelController;
|
||||||
private QuickQSPanelController mQuickQSPanelController;
|
private QuickQSPanelController mQuickQSPanelController;
|
||||||
private QSCustomizerController mQSCustomizerController;
|
private QSCustomizerController mQSCustomizerController;
|
||||||
|
@Nullable
|
||||||
private FooterActionsController mQSFooterActionController;
|
private FooterActionsController mQSFooterActionController;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
private FooterActionsViewModel mQSFooterActionsViewModel;
|
||||||
|
@Nullable
|
||||||
private ScrollListener mScrollListener;
|
private ScrollListener mScrollListener;
|
||||||
/**
|
/**
|
||||||
* When true, QS will translate from outside the screen. It will be clipped with parallax
|
* When true, QS will translate from outside the screen. It will be clipped with parallax
|
||||||
@@ -161,7 +175,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
KeyguardBypassController keyguardBypassController,
|
KeyguardBypassController keyguardBypassController,
|
||||||
QSFragmentComponent.Factory qsComponentFactory,
|
QSFragmentComponent.Factory qsComponentFactory,
|
||||||
QSFragmentDisableFlagsLogger qsFragmentDisableFlagsLogger,
|
QSFragmentDisableFlagsLogger qsFragmentDisableFlagsLogger,
|
||||||
FalsingManager falsingManager, DumpManager dumpManager) {
|
FalsingManager falsingManager, DumpManager dumpManager, FeatureFlags featureFlags,
|
||||||
|
NewFooterActionsController newFooterActionsController,
|
||||||
|
FooterActionsViewModel.Factory footerActionsViewModelFactory) {
|
||||||
mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
|
mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
|
||||||
mQsMediaHost = qsMediaHost;
|
mQsMediaHost = qsMediaHost;
|
||||||
mQqsMediaHost = qqsMediaHost;
|
mQqsMediaHost = qqsMediaHost;
|
||||||
@@ -173,6 +189,10 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
mBypassController = keyguardBypassController;
|
mBypassController = keyguardBypassController;
|
||||||
mStatusBarStateController = statusBarStateController;
|
mStatusBarStateController = statusBarStateController;
|
||||||
mDumpManager = dumpManager;
|
mDumpManager = dumpManager;
|
||||||
|
mFeatureFlags = featureFlags;
|
||||||
|
mNewFooterActionsController = newFooterActionsController;
|
||||||
|
mFooterActionsViewModelFactory = footerActionsViewModelFactory;
|
||||||
|
mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -193,11 +213,22 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
|
QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
|
||||||
mQSPanelController = qsFragmentComponent.getQSPanelController();
|
mQSPanelController = qsFragmentComponent.getQSPanelController();
|
||||||
mQuickQSPanelController = qsFragmentComponent.getQuickQSPanelController();
|
mQuickQSPanelController = qsFragmentComponent.getQuickQSPanelController();
|
||||||
mQSFooterActionController = qsFragmentComponent.getQSFooterActionController();
|
|
||||||
|
|
||||||
mQSPanelController.init();
|
mQSPanelController.init();
|
||||||
mQuickQSPanelController.init();
|
mQuickQSPanelController.init();
|
||||||
mQSFooterActionController.init();
|
|
||||||
|
if (mFeatureFlags.isEnabled(Flags.NEW_FOOTER_ACTIONS)) {
|
||||||
|
mQSFooterActionsViewModel = mFooterActionsViewModelFactory.create(/* lifecycleOwner */
|
||||||
|
this);
|
||||||
|
FooterActionsView footerActionsView = view.findViewById(R.id.qs_footer_actions);
|
||||||
|
FooterActionsViewBinder.bind(footerActionsView, mQSFooterActionsViewModel,
|
||||||
|
mListeningAndVisibilityLifecycleOwner);
|
||||||
|
|
||||||
|
mNewFooterActionsController.init();
|
||||||
|
} else {
|
||||||
|
mQSFooterActionController = qsFragmentComponent.getQSFooterActionController();
|
||||||
|
mQSFooterActionController.init();
|
||||||
|
}
|
||||||
|
|
||||||
mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view);
|
mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view);
|
||||||
mQSPanelScrollView.addOnLayoutChangeListener(
|
mQSPanelScrollView.addOnLayoutChangeListener(
|
||||||
@@ -283,6 +314,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
mDumpManager.unregisterDumpable(mContainer.getClass().getName());
|
mDumpManager.unregisterDumpable(mContainer.getClass().getName());
|
||||||
}
|
}
|
||||||
mDumpManager.unregisterDumpable(getClass().getName());
|
mDumpManager.unregisterDumpable(getClass().getName());
|
||||||
|
mListeningAndVisibilityLifecycleOwner.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -395,7 +427,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
mContainer.disable(state1, state2, animate);
|
mContainer.disable(state1, state2, animate);
|
||||||
mHeader.disable(state1, state2, animate);
|
mHeader.disable(state1, state2, animate);
|
||||||
mFooter.disable(state1, state2, animate);
|
mFooter.disable(state1, state2, animate);
|
||||||
mQSFooterActionController.disable(state2);
|
if (mQSFooterActionController != null) {
|
||||||
|
mQSFooterActionController.disable(state2);
|
||||||
|
}
|
||||||
updateQsState();
|
updateQsState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,7 +449,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
boolean footerVisible = qsPanelVisible && (expanded || !keyguardShowing || mHeaderAnimating
|
boolean footerVisible = qsPanelVisible && (expanded || !keyguardShowing || mHeaderAnimating
|
||||||
|| mShowCollapsedOnKeyguard);
|
|| mShowCollapsedOnKeyguard);
|
||||||
mFooter.setVisibility(footerVisible ? View.VISIBLE : View.INVISIBLE);
|
mFooter.setVisibility(footerVisible ? View.VISIBLE : View.INVISIBLE);
|
||||||
mQSFooterActionController.setVisible(footerVisible);
|
if (mQSFooterActionController != null) {
|
||||||
|
mQSFooterActionController.setVisible(footerVisible);
|
||||||
|
} else {
|
||||||
|
mQSFooterActionsViewModel.onVisibilityChangeRequested(footerVisible);
|
||||||
|
}
|
||||||
mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
|
mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
|
||||||
|| (expanded && !mStackScrollerOverscrolling));
|
|| (expanded && !mStackScrollerOverscrolling));
|
||||||
mQSPanelController.setVisibility(qsPanelVisible ? View.VISIBLE : View.INVISIBLE);
|
mQSPanelController.setVisibility(qsPanelVisible ? View.VISIBLE : View.INVISIBLE);
|
||||||
@@ -482,7 +520,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
}
|
}
|
||||||
|
|
||||||
mFooter.setKeyguardShowing(keyguardShowing);
|
mFooter.setKeyguardShowing(keyguardShowing);
|
||||||
mQSFooterActionController.setKeyguardShowing(keyguardShowing);
|
if (mQSFooterActionController != null) {
|
||||||
|
mQSFooterActionController.setKeyguardShowing(keyguardShowing);
|
||||||
|
}
|
||||||
updateQsState();
|
updateQsState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,7 +538,10 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
if (DEBUG) Log.d(TAG, "setListening " + listening);
|
if (DEBUG) Log.d(TAG, "setListening " + listening);
|
||||||
mListening = listening;
|
mListening = listening;
|
||||||
mQSContainerImplController.setListening(listening && mQsVisible);
|
mQSContainerImplController.setListening(listening && mQsVisible);
|
||||||
mQSFooterActionController.setListening(listening && mQsVisible);
|
if (mQSFooterActionController != null) {
|
||||||
|
mQSFooterActionController.setListening(listening && mQsVisible);
|
||||||
|
}
|
||||||
|
mListeningAndVisibilityLifecycleOwner.updateState();
|
||||||
updateQsPanelControllerListening();
|
updateQsPanelControllerListening();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,6 +554,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
if (DEBUG) Log.d(TAG, "setQsVisible " + visible);
|
if (DEBUG) Log.d(TAG, "setQsVisible " + visible);
|
||||||
mQsVisible = visible;
|
mQsVisible = visible;
|
||||||
setListening(mListening);
|
setListening(mListening);
|
||||||
|
mListeningAndVisibilityLifecycleOwner.updateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -602,7 +646,12 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
|
mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
|
||||||
float footerActionsExpansion =
|
float footerActionsExpansion =
|
||||||
onKeyguardAndExpanded ? 1 : mInSplitShade ? alphaProgress : expansion;
|
onKeyguardAndExpanded ? 1 : mInSplitShade ? alphaProgress : expansion;
|
||||||
mQSFooterActionController.setExpansion(footerActionsExpansion);
|
if (mQSFooterActionController != null) {
|
||||||
|
mQSFooterActionController.setExpansion(footerActionsExpansion);
|
||||||
|
} else {
|
||||||
|
mQSFooterActionsViewModel.onQuickSettingsExpansionChanged(footerActionsExpansion,
|
||||||
|
mInSplitShade);
|
||||||
|
}
|
||||||
mQSPanelController.setRevealExpansion(expansion);
|
mQSPanelController.setRevealExpansion(expansion);
|
||||||
mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
|
mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
|
||||||
mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
|
mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
|
||||||
@@ -714,7 +763,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
boolean customizing = isCustomizing();
|
boolean customizing = isCustomizing();
|
||||||
mQSPanelScrollView.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
|
mQSPanelScrollView.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
|
||||||
mFooter.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
|
mFooter.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
|
||||||
mQSFooterActionController.setVisible(!customizing);
|
if (mQSFooterActionController != null) {
|
||||||
|
mQSFooterActionController.setVisible(!customizing);
|
||||||
|
} else {
|
||||||
|
mQSFooterActionsViewModel.onVisibilityChangeRequested(!customizing);
|
||||||
|
}
|
||||||
mHeader.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
|
mHeader.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
|
||||||
// Let the panel know the position changed and it needs to update where notifications
|
// Let the panel know the position changed and it needs to update where notifications
|
||||||
// and whatnot are.
|
// and whatnot are.
|
||||||
@@ -860,4 +913,56 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
}
|
}
|
||||||
return "GONE";
|
return "GONE";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link LifecycleOwner} whose state is driven by the current state of this fragment:
|
||||||
|
*
|
||||||
|
* - DESTROYED when the fragment is destroyed.
|
||||||
|
* - CREATED when mListening == mQsVisible == false.
|
||||||
|
* - STARTED when mListening == true && mQsVisible == false.
|
||||||
|
* - RESUMED when mListening == true && mQsVisible == true.
|
||||||
|
*/
|
||||||
|
private class ListeningAndVisibilityLifecycleOwner implements LifecycleOwner {
|
||||||
|
private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
|
||||||
|
private boolean mDestroyed = false;
|
||||||
|
|
||||||
|
{
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Lifecycle getLifecycle() {
|
||||||
|
return mLifecycleRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the state of the associated lifecycle. This should be called whenever
|
||||||
|
* {@code mListening} or {@code mQsVisible} is changed.
|
||||||
|
*/
|
||||||
|
public void updateState() {
|
||||||
|
if (mDestroyed) {
|
||||||
|
mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mListening) {
|
||||||
|
mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mListening && !mQsVisible.
|
||||||
|
if (!mQsVisible) {
|
||||||
|
mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mListening && mQsVisible.
|
||||||
|
mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
mDestroyed = true;
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,11 +174,12 @@ public class QSSecurityFooterUtils implements DialogInterface.OnClickListener {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
QSSecurityFooterUtils(
|
QSSecurityFooterUtils(
|
||||||
@Application Context context, UserTracker userTracker, @Main Handler mainHandler,
|
@Application Context context, DevicePolicyManager devicePolicyManager,
|
||||||
ActivityStarter activityStarter, SecurityController securityController,
|
UserTracker userTracker, @Main Handler mainHandler, ActivityStarter activityStarter,
|
||||||
@Background Looper bgLooper, DialogLaunchAnimator dialogLaunchAnimator) {
|
SecurityController securityController, @Background Looper bgLooper,
|
||||||
|
DialogLaunchAnimator dialogLaunchAnimator) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mDpm = mContext.getSystemService(DevicePolicyManager.class);
|
mDpm = devicePolicyManager;
|
||||||
mUserTracker = userTracker;
|
mUserTracker = userTracker;
|
||||||
mMainHandler = mainHandler;
|
mMainHandler = mainHandler;
|
||||||
mActivityStarter = activityStarter;
|
mActivityStarter = activityStarter;
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ import com.android.systemui.battery.BatteryMeterView;
|
|||||||
import com.android.systemui.dagger.qualifiers.RootView;
|
import com.android.systemui.dagger.qualifiers.RootView;
|
||||||
import com.android.systemui.plugins.qs.QS;
|
import com.android.systemui.plugins.qs.QS;
|
||||||
import com.android.systemui.privacy.OngoingPrivacyChip;
|
import com.android.systemui.privacy.OngoingPrivacyChip;
|
||||||
import com.android.systemui.qs.FgsManagerController;
|
|
||||||
import com.android.systemui.qs.FgsManagerControllerImpl;
|
|
||||||
import com.android.systemui.qs.FooterActionsView;
|
import com.android.systemui.qs.FooterActionsView;
|
||||||
import com.android.systemui.qs.QSContainerImpl;
|
import com.android.systemui.qs.QSContainerImpl;
|
||||||
import com.android.systemui.qs.QSFooter;
|
import com.android.systemui.qs.QSFooter;
|
||||||
@@ -196,8 +194,4 @@ public interface QSFragmentModule {
|
|||||||
) {
|
) {
|
||||||
return layoutInflater.inflate(R.layout.fgs_footer, footerActionsView, false);
|
return layoutInflater.inflate(R.layout.fgs_footer, footerActionsView, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
|
||||||
@Binds
|
|
||||||
FgsManagerController bindFgsManagerController(FgsManagerControllerImpl impl);
|
|
||||||
}
|
}
|
||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package com.android.systemui.qs.footer.dagger
|
package com.android.systemui.qs.footer.dagger
|
||||||
|
|
||||||
import com.android.systemui.qs.FgsManagerController
|
|
||||||
import com.android.systemui.qs.FgsManagerControllerImpl
|
|
||||||
import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepository
|
import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepository
|
||||||
import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepositoryImpl
|
import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepositoryImpl
|
||||||
import com.android.systemui.qs.footer.data.repository.UserSwitcherRepository
|
import com.android.systemui.qs.footer.data.repository.UserSwitcherRepository
|
||||||
@@ -38,6 +36,4 @@ interface FooterActionsModule {
|
|||||||
): ForegroundServicesRepository
|
): ForegroundServicesRepository
|
||||||
|
|
||||||
@Binds fun footerActionsInteractor(impl: FooterActionsInteractorImpl): FooterActionsInteractor
|
@Binds fun footerActionsInteractor(impl: FooterActionsInteractorImpl): FooterActionsInteractor
|
||||||
|
|
||||||
@Binds fun fgsManagerControllerImpl(impl: FgsManagerControllerImpl): FgsManagerController
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,321 @@
|
|||||||
|
/*
|
||||||
|
* 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.qs.footer.ui.binder
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.PorterDuff
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.core.view.isInvisible
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
|
import com.android.systemui.R
|
||||||
|
import com.android.systemui.common.ui.ContentDescriptionViewBinder
|
||||||
|
import com.android.systemui.common.ui.IconViewBinder
|
||||||
|
import com.android.systemui.lifecycle.repeatWhenAttached
|
||||||
|
import com.android.systemui.people.ui.view.PeopleViewBinder.bind
|
||||||
|
import com.android.systemui.qs.FooterActionsView
|
||||||
|
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel
|
||||||
|
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel
|
||||||
|
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel
|
||||||
|
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
/** A ViewBinder for [FooterActionsViewBinder]. */
|
||||||
|
object FooterActionsViewBinder {
|
||||||
|
/**
|
||||||
|
* Create a [FooterActionsView] that can later be [bound][bind] to a [FooterActionsViewModel].
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun create(context: Context): FooterActionsView {
|
||||||
|
return LayoutInflater.from(context).inflate(R.layout.footer_actions, /* root= */ null)
|
||||||
|
as FooterActionsView
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Bind [view] to [viewModel]. */
|
||||||
|
@JvmStatic
|
||||||
|
fun bind(
|
||||||
|
view: FooterActionsView,
|
||||||
|
viewModel: FooterActionsViewModel,
|
||||||
|
qsVisibilityLifecycleOwner: LifecycleOwner,
|
||||||
|
) {
|
||||||
|
// Remove all children of the FooterActionsView that are used by the old implementation.
|
||||||
|
// TODO(b/242040009): Clean up the XML once the old implementation is removed.
|
||||||
|
view.removeAllViews()
|
||||||
|
|
||||||
|
// Add the views used by this new implementation.
|
||||||
|
val context = view.context
|
||||||
|
val inflater = LayoutInflater.from(context)
|
||||||
|
|
||||||
|
val securityHolder = TextButtonViewHolder.createAndAdd(inflater, view)
|
||||||
|
val foregroundServicesWithTextHolder = TextButtonViewHolder.createAndAdd(inflater, view)
|
||||||
|
val foregroundServicesWithNumberHolder = NumberButtonViewHolder.createAndAdd(inflater, view)
|
||||||
|
val userSwitcherHolder = IconButtonViewHolder.createAndAdd(inflater, view, isLast = false)
|
||||||
|
val settingsHolder =
|
||||||
|
IconButtonViewHolder.createAndAdd(inflater, view, isLast = viewModel.power == null)
|
||||||
|
|
||||||
|
// Bind the static power and settings buttons.
|
||||||
|
bindButton(settingsHolder, viewModel.settings)
|
||||||
|
|
||||||
|
if (viewModel.power != null) {
|
||||||
|
val powerHolder = IconButtonViewHolder.createAndAdd(inflater, view, isLast = true)
|
||||||
|
bindButton(powerHolder, viewModel.power)
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are 2 lifecycle scopes we are using here:
|
||||||
|
// 1) The scope created by [repeatWhenAttached] when [view] is attached, and destroyed
|
||||||
|
// when the [view] is detached. We use this as the parent scope for all our [viewModel]
|
||||||
|
// state collection, given that we don't want to do any work when [view] is detached.
|
||||||
|
// 2) The scope owned by [lifecycleOwner], which should be RESUMED only when Quick
|
||||||
|
// Settings are visible. We use this to make sure we collect UI state only when the
|
||||||
|
// View is visible.
|
||||||
|
//
|
||||||
|
// Given that we start our collection when the Quick Settings become visible, which happens
|
||||||
|
// every time the user swipes down the shade, we remember our previous UI state already
|
||||||
|
// bound to the UI to avoid binding the same values over and over for nothing.
|
||||||
|
|
||||||
|
// TODO(b/242040009): Look into using only a single scope.
|
||||||
|
|
||||||
|
var previousSecurity: FooterActionsSecurityButtonViewModel? = null
|
||||||
|
var previousForegroundServices: FooterActionsForegroundServicesButtonViewModel? = null
|
||||||
|
var previousUserSwitcher: FooterActionsButtonViewModel? = null
|
||||||
|
|
||||||
|
view.repeatWhenAttached {
|
||||||
|
val attachedScope = this.lifecycleScope
|
||||||
|
|
||||||
|
attachedScope.launch {
|
||||||
|
// Listen for dialog requests as soon as we are attached, even when not visible.
|
||||||
|
// TODO(b/242040009): Should this move somewhere else?
|
||||||
|
launch { viewModel.observeDeviceMonitoringDialogRequests(view.context) }
|
||||||
|
|
||||||
|
// Make sure we set the correct visibility and alpha even when QS are not currently
|
||||||
|
// shown.
|
||||||
|
launch {
|
||||||
|
viewModel.isVisible.collect { isVisible -> view.isInvisible = !isVisible }
|
||||||
|
}
|
||||||
|
|
||||||
|
launch { viewModel.alpha.collect { view.alpha = it } }
|
||||||
|
launch { viewModel.backgroundAlpha.collect { view.backgroundAlpha = it } }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for model changes only when QS are visible.
|
||||||
|
qsVisibilityLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
// Security.
|
||||||
|
launch {
|
||||||
|
viewModel.security.collect { security ->
|
||||||
|
if (previousSecurity != security) {
|
||||||
|
bindSecurity(securityHolder, security)
|
||||||
|
previousSecurity = security
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Foreground services.
|
||||||
|
launch {
|
||||||
|
viewModel.foregroundServices.collect { foregroundServices ->
|
||||||
|
if (previousForegroundServices != foregroundServices) {
|
||||||
|
bindForegroundService(
|
||||||
|
foregroundServicesWithNumberHolder,
|
||||||
|
foregroundServicesWithTextHolder,
|
||||||
|
foregroundServices,
|
||||||
|
)
|
||||||
|
previousForegroundServices = foregroundServices
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// User switcher.
|
||||||
|
launch {
|
||||||
|
viewModel.userSwitcher.collect { userSwitcher ->
|
||||||
|
if (previousUserSwitcher != userSwitcher) {
|
||||||
|
bindButton(userSwitcherHolder, userSwitcher)
|
||||||
|
previousUserSwitcher = userSwitcher
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindSecurity(
|
||||||
|
securityHolder: TextButtonViewHolder,
|
||||||
|
security: FooterActionsSecurityButtonViewModel?,
|
||||||
|
) {
|
||||||
|
val securityView = securityHolder.view
|
||||||
|
securityView.isVisible = security != null
|
||||||
|
if (security == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that the chevron is visible and that the button is clickable if there is a
|
||||||
|
// listener.
|
||||||
|
val chevron = securityHolder.chevron
|
||||||
|
if (security.onClick != null) {
|
||||||
|
securityView.isClickable = true
|
||||||
|
securityView.setOnClickListener(security.onClick)
|
||||||
|
chevron.isVisible = true
|
||||||
|
} else {
|
||||||
|
securityView.isClickable = false
|
||||||
|
securityView.setOnClickListener(null)
|
||||||
|
chevron.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
securityHolder.text.text = security.text
|
||||||
|
securityHolder.newDot.isVisible = false
|
||||||
|
IconViewBinder.bind(security.icon, securityHolder.icon)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindForegroundService(
|
||||||
|
foregroundServicesWithNumberHolder: NumberButtonViewHolder,
|
||||||
|
foregroundServicesWithTextHolder: TextButtonViewHolder,
|
||||||
|
foregroundServices: FooterActionsForegroundServicesButtonViewModel?,
|
||||||
|
) {
|
||||||
|
val foregroundServicesWithNumberView = foregroundServicesWithNumberHolder.view
|
||||||
|
val foregroundServicesWithTextView = foregroundServicesWithTextHolder.view
|
||||||
|
if (foregroundServices == null) {
|
||||||
|
foregroundServicesWithNumberView.isVisible = false
|
||||||
|
foregroundServicesWithTextView.isVisible = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val foregroundServicesCount = foregroundServices.foregroundServicesCount
|
||||||
|
if (foregroundServices.displayText) {
|
||||||
|
// Button with text, icon and chevron.
|
||||||
|
foregroundServicesWithNumberView.isVisible = false
|
||||||
|
|
||||||
|
foregroundServicesWithTextView.isVisible = true
|
||||||
|
foregroundServicesWithTextView.setOnClickListener(foregroundServices.onClick)
|
||||||
|
foregroundServicesWithTextHolder.text.text = foregroundServices.text
|
||||||
|
foregroundServicesWithTextHolder.newDot.isVisible = foregroundServices.hasNewChanges
|
||||||
|
} else {
|
||||||
|
// Small button with the number only.
|
||||||
|
foregroundServicesWithTextView.isVisible = false
|
||||||
|
|
||||||
|
foregroundServicesWithNumberView.visibility = View.VISIBLE
|
||||||
|
foregroundServicesWithNumberView.setOnClickListener(foregroundServices.onClick)
|
||||||
|
foregroundServicesWithNumberHolder.number.text = foregroundServicesCount.toString()
|
||||||
|
foregroundServicesWithNumberHolder.number.contentDescription = foregroundServices.text
|
||||||
|
foregroundServicesWithNumberHolder.newDot.isVisible = foregroundServices.hasNewChanges
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindButton(button: IconButtonViewHolder, model: FooterActionsButtonViewModel?) {
|
||||||
|
val buttonView = button.view
|
||||||
|
buttonView.isVisible = model != null
|
||||||
|
if (model == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonView.setBackgroundResource(model.background)
|
||||||
|
buttonView.setOnClickListener(model.onClick)
|
||||||
|
|
||||||
|
val icon = model.icon
|
||||||
|
val iconView = button.icon
|
||||||
|
val contentDescription = model.contentDescription
|
||||||
|
|
||||||
|
IconViewBinder.bind(icon, iconView)
|
||||||
|
ContentDescriptionViewBinder.bind(contentDescription, iconView)
|
||||||
|
if (model.iconTint != null) {
|
||||||
|
iconView.setColorFilter(model.iconTint, PorterDuff.Mode.SRC_IN)
|
||||||
|
} else {
|
||||||
|
iconView.clearColorFilter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TextButtonViewHolder(val view: View) {
|
||||||
|
val icon = view.requireViewById<ImageView>(R.id.icon)
|
||||||
|
val text = view.requireViewById<TextView>(R.id.text)
|
||||||
|
val newDot = view.requireViewById<ImageView>(R.id.new_dot)
|
||||||
|
val chevron = view.requireViewById<ImageView>(R.id.chevron_icon)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun createAndAdd(inflater: LayoutInflater, root: ViewGroup): TextButtonViewHolder {
|
||||||
|
val view =
|
||||||
|
inflater.inflate(
|
||||||
|
R.layout.footer_actions_text_button,
|
||||||
|
/* root= */ root,
|
||||||
|
/* attachToRoot= */ false,
|
||||||
|
)
|
||||||
|
root.addView(view)
|
||||||
|
return TextButtonViewHolder(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NumberButtonViewHolder(val view: View) {
|
||||||
|
val number = view.requireViewById<TextView>(R.id.number)
|
||||||
|
val newDot = view.requireViewById<ImageView>(R.id.new_dot)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun createAndAdd(inflater: LayoutInflater, root: ViewGroup): NumberButtonViewHolder {
|
||||||
|
val view =
|
||||||
|
inflater.inflate(
|
||||||
|
R.layout.footer_actions_number_button,
|
||||||
|
/* root= */ root,
|
||||||
|
/* attachToRoot= */ false,
|
||||||
|
)
|
||||||
|
root.addView(view)
|
||||||
|
return NumberButtonViewHolder(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class IconButtonViewHolder(val view: View) {
|
||||||
|
val icon = view.requireViewById<ImageView>(R.id.icon)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun createAndAdd(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
root: ViewGroup,
|
||||||
|
isLast: Boolean,
|
||||||
|
): IconButtonViewHolder {
|
||||||
|
val view =
|
||||||
|
inflater.inflate(
|
||||||
|
R.layout.footer_actions_icon_button,
|
||||||
|
/* root= */ root,
|
||||||
|
/* attachToRoot= */ false,
|
||||||
|
)
|
||||||
|
|
||||||
|
// All buttons have a background with an inset of qs_footer_action_inset, so the last
|
||||||
|
// button must have a negative inset of -qs_footer_action_inset to compensate and be
|
||||||
|
// aligned with its parent.
|
||||||
|
val marginEnd =
|
||||||
|
if (isLast) {
|
||||||
|
-view.context.resources.getDimensionPixelSize(R.dimen.qs_footer_action_inset)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
val size =
|
||||||
|
view.context.resources.getDimensionPixelSize(R.dimen.qs_footer_action_button_size)
|
||||||
|
root.addView(
|
||||||
|
view,
|
||||||
|
LinearLayout.LayoutParams(size, size).apply { this.marginEnd = marginEnd },
|
||||||
|
)
|
||||||
|
return IconButtonViewHolder(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,6 +46,7 @@ import kotlinx.coroutines.flow.StateFlow
|
|||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
/** A ViewModel for the footer actions. */
|
/** A ViewModel for the footer actions. */
|
||||||
@@ -74,60 +75,65 @@ class FooterActionsViewModel(
|
|||||||
|
|
||||||
/** The model for the security button. */
|
/** The model for the security button. */
|
||||||
val security: Flow<FooterActionsSecurityButtonViewModel?> =
|
val security: Flow<FooterActionsSecurityButtonViewModel?> =
|
||||||
footerActionsInteractor.securityButtonConfig.map { config ->
|
footerActionsInteractor.securityButtonConfig
|
||||||
val (icon, text, isClickable) = config ?: return@map null
|
.map { config ->
|
||||||
FooterActionsSecurityButtonViewModel(
|
val (icon, text, isClickable) = config ?: return@map null
|
||||||
icon,
|
FooterActionsSecurityButtonViewModel(
|
||||||
text,
|
icon,
|
||||||
if (isClickable) this::onSecurityButtonClicked else null,
|
text,
|
||||||
)
|
if (isClickable) this::onSecurityButtonClicked else null,
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
|
||||||
/** The model for the foreground services button. */
|
/** The model for the foreground services button. */
|
||||||
val foregroundServices: Flow<FooterActionsForegroundServicesButtonViewModel?> =
|
val foregroundServices: Flow<FooterActionsForegroundServicesButtonViewModel?> =
|
||||||
combine(
|
combine(
|
||||||
footerActionsInteractor.foregroundServicesCount,
|
footerActionsInteractor.foregroundServicesCount,
|
||||||
footerActionsInteractor.hasNewForegroundServices,
|
footerActionsInteractor.hasNewForegroundServices,
|
||||||
security,
|
security,
|
||||||
) { foregroundServicesCount, hasNewChanges, securityModel ->
|
) { foregroundServicesCount, hasNewChanges, securityModel ->
|
||||||
if (foregroundServicesCount <= 0) {
|
if (foregroundServicesCount <= 0) {
|
||||||
return@combine null
|
return@combine null
|
||||||
}
|
}
|
||||||
|
|
||||||
val text =
|
val text =
|
||||||
icuMessageFormat(
|
icuMessageFormat(
|
||||||
context.resources,
|
context.resources,
|
||||||
R.string.fgs_manager_footer_label,
|
R.string.fgs_manager_footer_label,
|
||||||
|
foregroundServicesCount,
|
||||||
|
)
|
||||||
|
FooterActionsForegroundServicesButtonViewModel(
|
||||||
foregroundServicesCount,
|
foregroundServicesCount,
|
||||||
|
text = text,
|
||||||
|
displayText = securityModel == null,
|
||||||
|
hasNewChanges = hasNewChanges,
|
||||||
|
this::onForegroundServiceButtonClicked,
|
||||||
)
|
)
|
||||||
FooterActionsForegroundServicesButtonViewModel(
|
}
|
||||||
foregroundServicesCount,
|
.distinctUntilChanged()
|
||||||
text = text,
|
|
||||||
displayText = securityModel == null,
|
|
||||||
hasNewChanges = hasNewChanges,
|
|
||||||
this::onForegroundServiceButtonClicked,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The model for the user switcher button. */
|
/** The model for the user switcher button. */
|
||||||
val userSwitcher: Flow<FooterActionsButtonViewModel?> =
|
val userSwitcher: Flow<FooterActionsButtonViewModel?> =
|
||||||
footerActionsInteractor.userSwitcherStatus.map { userSwitcherStatus ->
|
footerActionsInteractor.userSwitcherStatus
|
||||||
when (userSwitcherStatus) {
|
.map { userSwitcherStatus ->
|
||||||
UserSwitcherStatusModel.Disabled -> null
|
when (userSwitcherStatus) {
|
||||||
is UserSwitcherStatusModel.Enabled -> {
|
UserSwitcherStatusModel.Disabled -> null
|
||||||
if (userSwitcherStatus.currentUserImage == null) {
|
is UserSwitcherStatusModel.Enabled -> {
|
||||||
Log.e(
|
if (userSwitcherStatus.currentUserImage == null) {
|
||||||
TAG,
|
Log.e(
|
||||||
"Skipped the addition of user switcher button because " +
|
TAG,
|
||||||
"currentUserImage is missing",
|
"Skipped the addition of user switcher button because " +
|
||||||
)
|
"currentUserImage is missing",
|
||||||
return@map null
|
)
|
||||||
}
|
return@map null
|
||||||
|
}
|
||||||
|
|
||||||
userSwitcherButton(userSwitcherStatus)
|
userSwitcherButton(userSwitcherStatus)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.distinctUntilChanged()
|
||||||
|
|
||||||
/** The model for the settings button. */
|
/** The model for the settings button. */
|
||||||
val settings: FooterActionsButtonViewModel =
|
val settings: FooterActionsButtonViewModel =
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import com.android.systemui.R;
|
|||||||
/**
|
/**
|
||||||
* Container for image of the multi user switcher (tappable).
|
* Container for image of the multi user switcher (tappable).
|
||||||
*/
|
*/
|
||||||
|
// TODO(b/242040009): Remove this file.
|
||||||
public class MultiUserSwitch extends FrameLayout {
|
public class MultiUserSwitch extends FrameLayout {
|
||||||
public MultiUserSwitch(Context context, AttributeSet attrs) {
|
public MultiUserSwitch(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import com.android.systemui.util.ViewController;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
/** View Controller for {@link MultiUserSwitch}. */
|
/** View Controller for {@link MultiUserSwitch}. */
|
||||||
|
// TODO(b/242040009): Remove this file.
|
||||||
public class MultiUserSwitchController extends ViewController<MultiUserSwitch> {
|
public class MultiUserSwitchController extends ViewController<MultiUserSwitch> {
|
||||||
private final UserManager mUserManager;
|
private final UserManager mUserManager;
|
||||||
private final UserSwitcherController mUserSwitcherController;
|
private final UserSwitcherController mUserSwitcherController;
|
||||||
|
|||||||
@@ -45,12 +45,15 @@ import com.android.systemui.R;
|
|||||||
import com.android.systemui.SysuiBaseFragmentTest;
|
import com.android.systemui.SysuiBaseFragmentTest;
|
||||||
import com.android.systemui.animation.ShadeInterpolation;
|
import com.android.systemui.animation.ShadeInterpolation;
|
||||||
import com.android.systemui.dump.DumpManager;
|
import com.android.systemui.dump.DumpManager;
|
||||||
|
import com.android.systemui.flags.FakeFeatureFlags;
|
||||||
|
import com.android.systemui.flags.Flags;
|
||||||
import com.android.systemui.media.MediaHost;
|
import com.android.systemui.media.MediaHost;
|
||||||
import com.android.systemui.plugins.FalsingManager;
|
import com.android.systemui.plugins.FalsingManager;
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||||
import com.android.systemui.qs.customize.QSCustomizerController;
|
import com.android.systemui.qs.customize.QSCustomizerController;
|
||||||
import com.android.systemui.qs.dagger.QSFragmentComponent;
|
import com.android.systemui.qs.dagger.QSFragmentComponent;
|
||||||
import com.android.systemui.qs.external.TileServiceRequestController;
|
import com.android.systemui.qs.external.TileServiceRequestController;
|
||||||
|
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
|
||||||
import com.android.systemui.statusbar.CommandQueue;
|
import com.android.systemui.statusbar.CommandQueue;
|
||||||
import com.android.systemui.statusbar.StatusBarState;
|
import com.android.systemui.statusbar.StatusBarState;
|
||||||
import com.android.systemui.statusbar.phone.KeyguardBypassController;
|
import com.android.systemui.statusbar.phone.KeyguardBypassController;
|
||||||
@@ -390,6 +393,8 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
|||||||
setUpMedia();
|
setUpMedia();
|
||||||
setUpOther();
|
setUpOther();
|
||||||
|
|
||||||
|
FakeFeatureFlags featureFlags = new FakeFeatureFlags();
|
||||||
|
featureFlags.set(Flags.NEW_FOOTER_ACTIONS, false);
|
||||||
return new QSFragment(
|
return new QSFragment(
|
||||||
new RemoteInputQuickSettingsDisabler(
|
new RemoteInputQuickSettingsDisabler(
|
||||||
context, commandQueue, mock(ConfigurationController.class)),
|
context, commandQueue, mock(ConfigurationController.class)),
|
||||||
@@ -402,7 +407,10 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
|||||||
mQsComponentFactory,
|
mQsComponentFactory,
|
||||||
mock(QSFragmentDisableFlagsLogger.class),
|
mock(QSFragmentDisableFlagsLogger.class),
|
||||||
mFalsingManager,
|
mFalsingManager,
|
||||||
mock(DumpManager.class));
|
mock(DumpManager.class),
|
||||||
|
featureFlags,
|
||||||
|
mock(NewFooterActionsController.class),
|
||||||
|
mock(FooterActionsViewModel.Factory.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpOther() {
|
private void setUpOther() {
|
||||||
|
|||||||
@@ -124,8 +124,9 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
|||||||
mRootView = (ViewGroup) new LayoutInflaterBuilder(mContext)
|
mRootView = (ViewGroup) new LayoutInflaterBuilder(mContext)
|
||||||
.replace("ImageView", TestableImageView.class)
|
.replace("ImageView", TestableImageView.class)
|
||||||
.build().inflate(R.layout.quick_settings_security_footer, null, false);
|
.build().inflate(R.layout.quick_settings_security_footer, null, false);
|
||||||
mFooterUtils = new QSSecurityFooterUtils(getContext(), mUserTracker, mainHandler,
|
mFooterUtils = new QSSecurityFooterUtils(getContext(),
|
||||||
mActivityStarter, mSecurityController, looper, mDialogLaunchAnimator);
|
getContext().getSystemService(DevicePolicyManager.class), mUserTracker,
|
||||||
|
mainHandler, mActivityStarter, mSecurityController, looper, mDialogLaunchAnimator);
|
||||||
mFooter = new QSSecurityFooter(mRootView, mainHandler, mSecurityController, looper,
|
mFooter = new QSSecurityFooter(mRootView, mainHandler, mSecurityController, looper,
|
||||||
mBroadcastDispatcher, mFooterUtils);
|
mBroadcastDispatcher, mFooterUtils);
|
||||||
mFooterText = mRootView.findViewById(R.id.footer_text);
|
mFooterText = mRootView.findViewById(R.id.footer_text);
|
||||||
|
|||||||
Reference in New Issue
Block a user