• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     private final DarkModeSnapshotRestorer mSnapshotRestorer;
63 
DarkModeSectionController( Context context, Lifecycle lifecycle, DarkModeSnapshotRestorer snapshotRestorer)64     public DarkModeSectionController(
65             Context context,
66             Lifecycle lifecycle,
67             DarkModeSnapshotRestorer snapshotRestorer) {
68         mContext = context;
69         mLifecycle = lifecycle;
70         mPowerManager = context.getSystemService(PowerManager.class);
71         mLifecycle.addObserver(this);
72         mSnapshotRestorer = snapshotRestorer;
73     }
74 
75     @OnLifecycleEvent(Lifecycle.Event.ON_START)
76     @MainThread
onStart()77     public void onStart() {
78         sExecutorService.submit(() -> {
79             if (mContext != null && mLifecycle.getCurrentState().isAtLeast(
80                     Lifecycle.State.STARTED)) {
81                 mContext.registerReceiver(mBatterySaverStateReceiver,
82                         new IntentFilter(ACTION_POWER_SAVE_MODE_CHANGED));
83             }
84         });
85     }
86 
87     @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
88     @MainThread
onStop()89     public void onStop() {
90         sExecutorService.submit(() -> {
91             if (mContext != null && mBatterySaverStateReceiver != null) {
92                 mContext.unregisterReceiver(mBatterySaverStateReceiver);
93             }
94         });
95     }
96 
97     @Override
release()98     public void release() {
99         mLifecycle.removeObserver(this);
100         mContext = null;
101     }
102 
103     @Override
isAvailable(Context context)104     public boolean isAvailable(Context context) {
105         if (context == null) {
106             return false;
107         }
108         return ContextCompat.checkSelfPermission(context, MODIFY_DAY_NIGHT_MODE)
109                 == PERMISSION_GRANTED;
110     }
111 
112     @Override
createView(Context context)113     public DarkModeSectionView createView(Context context) {
114         mDarkModeSectionView = (DarkModeSectionView) LayoutInflater.from(
115                 context).inflate(R.layout.dark_mode_section_view, /* root= */ null);
116         mDarkModeSectionView.setViewListener(this::onViewActivated);
117         PowerManager pm = context.getSystemService(PowerManager.class);
118         mDarkModeSectionView.setEnabled(!pm.isPowerSaveMode());
119         return mDarkModeSectionView;
120     }
121 
onViewActivated(Context context, boolean viewActivated)122     private void onViewActivated(Context context, boolean viewActivated) {
123         if (context == null) {
124             return;
125         }
126         Switch switchView = mDarkModeSectionView.findViewById(R.id.dark_mode_toggle);
127         if (!switchView.isEnabled()) {
128             Toast disableToast = Toast.makeText(mContext,
129                     mContext.getString(R.string.mode_disabled_msg), Toast.LENGTH_SHORT);
130             disableToast.show();
131             return;
132         }
133         UiModeManager uiModeManager = context.getSystemService(UiModeManager.class);
134         int shortDelay = context.getResources().getInteger(android.R.integer.config_shortAnimTime);
135         new Handler(Looper.getMainLooper()).postDelayed(
136                 () -> {
137                     mDarkModeSectionView.announceForAccessibility(
138                             context.getString(R.string.mode_changed));
139                     uiModeManager.setNightModeActivated(viewActivated);
140                     mSnapshotRestorer.store(viewActivated);
141                 },
142                 /* delayMillis= */ shortDelay);
143     }
144 
145     private class BatterySaverStateReceiver extends BroadcastReceiver {
146         @Override
onReceive(Context context, Intent intent)147         public void onReceive(Context context, Intent intent) {
148             if (TextUtils.equals(intent.getAction(), ACTION_POWER_SAVE_MODE_CHANGED)
149                     && mDarkModeSectionView != null) {
150                 mDarkModeSectionView.setEnabled(!mPowerManager.isPowerSaveMode());
151             }
152         }
153     }
154 }
155