1 /*
2  * Copyright (C) 2021 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 package com.android.customization.model.mode;
17 
18 
19 import static android.Manifest.permission.MODIFY_DAY_NIGHT_MODE;
20 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
21 import static android.os.PowerManager.ACTION_POWER_SAVE_MODE_CHANGED;
22 
23 import android.app.UiModeManager;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.os.Handler;
29 import android.os.Looper;
30 import android.os.PowerManager;
31 import android.text.TextUtils;
32 import android.view.LayoutInflater;
33 import android.widget.Switch;
34 import android.widget.Toast;
35 
36 import androidx.annotation.MainThread;
37 import androidx.core.content.ContextCompat;
38 import androidx.lifecycle.Lifecycle;
39 import androidx.lifecycle.LifecycleObserver;
40 import androidx.lifecycle.OnLifecycleEvent;
41 
42 import com.android.customization.picker.mode.DarkModeSectionView;
43 import com.android.wallpaper.R;
44 import com.android.wallpaper.model.CustomizationSectionController;
45 
46 import java.util.concurrent.ExecutorService;
47 import java.util.concurrent.Executors;
48 
49 /** Section for dark theme toggle that controls if this section will be shown visually. */
50 public class DarkModeSectionController implements
51         CustomizationSectionController<DarkModeSectionView>, LifecycleObserver {
52 
53     private static final ExecutorService sExecutorService = Executors.newSingleThreadExecutor();
54 
55     private final Lifecycle mLifecycle;
56     private final PowerManager mPowerManager;
57     private final BatterySaverStateReceiver mBatterySaverStateReceiver =
58             new BatterySaverStateReceiver();
59 
60     private Context mContext;
61     private DarkModeSectionView mDarkModeSectionView;
62 
DarkModeSectionController(Context context, Lifecycle lifecycle)63     public DarkModeSectionController(Context context, Lifecycle lifecycle) {
64         mContext = context;
65         mLifecycle = lifecycle;
66         mPowerManager = context.getSystemService(PowerManager.class);
67         mLifecycle.addObserver(this);
68     }
69 
70     @OnLifecycleEvent(Lifecycle.Event.ON_START)
71     @MainThread
onStart()72     public void onStart() {
73         sExecutorService.submit(() -> {
74             if (mContext != null && mLifecycle.getCurrentState().isAtLeast(
75                     Lifecycle.State.STARTED)) {
76                 mContext.registerReceiver(mBatterySaverStateReceiver,
77                         new IntentFilter(ACTION_POWER_SAVE_MODE_CHANGED));
78             }
79         });
80     }
81 
82     @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
83     @MainThread
onStop()84     public void onStop() {
85         sExecutorService.submit(() -> {
86             if (mContext != null && mBatterySaverStateReceiver != null) {
87                 mContext.unregisterReceiver(mBatterySaverStateReceiver);
88             }
89         });
90     }
91 
92     @Override
release()93     public void release() {
94         mLifecycle.removeObserver(this);
95         mContext = null;
96     }
97 
98     @Override
isAvailable(Context context)99     public boolean isAvailable(Context context) {
100         if (context == null) {
101             return false;
102         }
103         return ContextCompat.checkSelfPermission(context, MODIFY_DAY_NIGHT_MODE)
104                 == PERMISSION_GRANTED;
105     }
106 
107     @Override
createView(Context context)108     public DarkModeSectionView createView(Context context) {
109         mDarkModeSectionView = (DarkModeSectionView) LayoutInflater.from(
110                 context).inflate(R.layout.dark_mode_section_view, /* root= */ null);
111         mDarkModeSectionView.setViewListener(this::onViewActivated);
112         PowerManager pm = context.getSystemService(PowerManager.class);
113         mDarkModeSectionView.setEnabled(!pm.isPowerSaveMode());
114         return mDarkModeSectionView;
115     }
116 
onViewActivated(Context context, boolean viewActivated)117     private void onViewActivated(Context context, boolean viewActivated) {
118         if (context == null) {
119             return;
120         }
121         Switch switchView = mDarkModeSectionView.findViewById(R.id.dark_mode_toggle);
122         if (!switchView.isEnabled()) {
123             Toast disableToast = Toast.makeText(mContext,
124                     mContext.getString(R.string.mode_disabled_msg), Toast.LENGTH_SHORT);
125             disableToast.show();
126             return;
127         }
128         UiModeManager uiModeManager = context.getSystemService(UiModeManager.class);
129         int shortDelay = context.getResources().getInteger(android.R.integer.config_shortAnimTime);
130         new Handler(Looper.getMainLooper()).postDelayed(
131                 () -> {
132                     mDarkModeSectionView.announceForAccessibility(
133                             context.getString(R.string.mode_changed));
134                     uiModeManager.setNightModeActivated(viewActivated);
135                 },
136                 /* delayMillis= */ shortDelay);
137     }
138 
139     private class BatterySaverStateReceiver extends BroadcastReceiver {
140         @Override
onReceive(Context context, Intent intent)141         public void onReceive(Context context, Intent intent) {
142             if (TextUtils.equals(intent.getAction(), ACTION_POWER_SAVE_MODE_CHANGED)
143                     && mDarkModeSectionView != null) {
144                 mDarkModeSectionView.setEnabled(!mPowerManager.isPowerSaveMode());
145             }
146         }
147     }
148 }
149