1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.settings.brightness;
18 
19 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
20 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
21 import static android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY;
22 
23 import android.app.Activity;
24 import android.graphics.Rect;
25 import android.os.Bundle;
26 import android.view.Gravity;
27 import android.view.KeyEvent;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import android.view.Window;
31 import android.view.WindowManager;
32 import android.view.accessibility.AccessibilityManager;
33 import android.widget.FrameLayout;
34 
35 import com.android.internal.annotations.VisibleForTesting;
36 import com.android.internal.logging.MetricsLogger;
37 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
38 import com.android.systemui.R;
39 import com.android.systemui.dagger.qualifiers.Main;
40 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
41 import com.android.systemui.util.concurrency.DelayableExecutor;
42 
43 import java.util.List;
44 
45 import javax.inject.Inject;
46 
47 /** A dialog that provides controls for adjusting the screen brightness. */
48 public class BrightnessDialog extends Activity {
49 
50     @VisibleForTesting
51     static final int DIALOG_TIMEOUT_MILLIS = 3000;
52 
53     private BrightnessController mBrightnessController;
54     private final BrightnessSliderController.Factory mToggleSliderFactory;
55     private final BrightnessController.Factory mBrightnessControllerFactory;
56     private final DelayableExecutor mMainExecutor;
57     private final AccessibilityManagerWrapper mAccessibilityMgr;
58     private Runnable mCancelTimeoutRunnable;
59 
60     @Inject
BrightnessDialog( BrightnessSliderController.Factory brightnessSliderfactory, BrightnessController.Factory brightnessControllerFactory, @Main DelayableExecutor mainExecutor, AccessibilityManagerWrapper accessibilityMgr )61     public BrightnessDialog(
62             BrightnessSliderController.Factory brightnessSliderfactory,
63             BrightnessController.Factory brightnessControllerFactory,
64             @Main DelayableExecutor mainExecutor,
65             AccessibilityManagerWrapper accessibilityMgr
66     ) {
67         mToggleSliderFactory = brightnessSliderfactory;
68         mBrightnessControllerFactory = brightnessControllerFactory;
69         mMainExecutor = mainExecutor;
70         mAccessibilityMgr = accessibilityMgr;
71     }
72 
73 
74     @Override
onCreate(Bundle savedInstanceState)75     protected void onCreate(Bundle savedInstanceState) {
76         super.onCreate(savedInstanceState);
77 
78         final Window window = getWindow();
79 
80         window.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
81         window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
82         window.requestFeature(Window.FEATURE_NO_TITLE);
83 
84         // Calling this creates the decor View, so setLayout takes proper effect
85         // (see Dialog#onWindowAttributesChanged)
86         window.getDecorView();
87         window.setLayout(
88                 WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);
89         getTheme().applyStyle(R.style.Theme_SystemUI_QuickSettings, false);
90 
91         setContentView(R.layout.brightness_mirror_container);
92         FrameLayout frame = findViewById(R.id.brightness_mirror_container);
93         // The brightness mirror container is INVISIBLE by default.
94         frame.setVisibility(View.VISIBLE);
95         ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) frame.getLayoutParams();
96         int horizontalMargin =
97                 getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
98         lp.leftMargin = horizontalMargin;
99         lp.rightMargin = horizontalMargin;
100         frame.setLayoutParams(lp);
101         Rect bounds = new Rect();
102         frame.addOnLayoutChangeListener(
103                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
104                     // Exclude this view (and its horizontal margins) from triggering gestures.
105                     // This prevents back gesture from being triggered by dragging close to the
106                     // edge of the slider (0% or 100%).
107                     bounds.set(-horizontalMargin, 0, right - left + horizontalMargin, bottom - top);
108                     v.setSystemGestureExclusionRects(List.of(bounds));
109                 });
110 
111         BrightnessSliderController controller = mToggleSliderFactory.create(this, frame);
112         controller.init();
113         frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT);
114 
115         mBrightnessController = mBrightnessControllerFactory.create(controller);
116     }
117 
118     @Override
onStart()119     protected void onStart() {
120         super.onStart();
121         mBrightnessController.registerCallbacks();
122         MetricsLogger.visible(this, MetricsEvent.BRIGHTNESS_DIALOG);
123     }
124 
125     @Override
onResume()126     protected void onResume() {
127         super.onResume();
128         if (triggeredByBrightnessKey()) {
129             scheduleTimeout();
130         }
131     }
132 
133     @Override
onPause()134     protected void onPause() {
135         super.onPause();
136         overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
137     }
138 
139     @Override
onStop()140     protected void onStop() {
141         super.onStop();
142         MetricsLogger.hidden(this, MetricsEvent.BRIGHTNESS_DIALOG);
143         mBrightnessController.unregisterCallbacks();
144     }
145 
146     @Override
onKeyDown(int keyCode, KeyEvent event)147     public boolean onKeyDown(int keyCode, KeyEvent event) {
148         if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
149                 || keyCode == KeyEvent.KEYCODE_VOLUME_UP
150                 || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
151             if (mCancelTimeoutRunnable != null) {
152                 mCancelTimeoutRunnable.run();
153             }
154             finish();
155         }
156 
157         return super.onKeyDown(keyCode, event);
158     }
159 
triggeredByBrightnessKey()160     private boolean triggeredByBrightnessKey() {
161         return getIntent().getBooleanExtra(EXTRA_FROM_BRIGHTNESS_KEY, false);
162     }
163 
scheduleTimeout()164     private void scheduleTimeout() {
165         if (mCancelTimeoutRunnable != null) {
166             mCancelTimeoutRunnable.run();
167         }
168         final int timeout = mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS,
169                 AccessibilityManager.FLAG_CONTENT_CONTROLS);
170         mCancelTimeoutRunnable = mMainExecutor.executeDelayed(this::finish, timeout);
171     }
172 }
173