1 /*
2  * Copyright (C) 2017 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.settings.wifi;
18 
19 import static com.android.settings.wifi.ConfigureWifiSettings.WIFI_WAKEUP_REQUEST_CODE;
20 
21 import android.app.Service;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.location.LocationManager;
27 import android.provider.Settings;
28 import android.text.TextUtils;
29 
30 import androidx.annotation.VisibleForTesting;
31 import androidx.fragment.app.Fragment;
32 import androidx.preference.Preference;
33 import androidx.preference.PreferenceScreen;
34 import androidx.preference.SwitchPreference;
35 
36 import com.android.settings.R;
37 import com.android.settings.core.PreferenceControllerMixin;
38 import com.android.settings.dashboard.DashboardFragment;
39 import com.android.settings.utils.AnnotationSpan;
40 import com.android.settingslib.core.AbstractPreferenceController;
41 import com.android.settingslib.core.lifecycle.Lifecycle;
42 import com.android.settingslib.core.lifecycle.LifecycleObserver;
43 import com.android.settingslib.core.lifecycle.events.OnPause;
44 import com.android.settingslib.core.lifecycle.events.OnResume;
45 
46 /**
47  * {@link PreferenceControllerMixin} that controls whether the Wi-Fi Wakeup feature should be
48  * enabled.
49  */
50 public class WifiWakeupPreferenceController extends AbstractPreferenceController implements
51         LifecycleObserver, OnPause, OnResume {
52 
53     private static final String TAG = "WifiWakeupPrefController";
54     private static final String KEY_ENABLE_WIFI_WAKEUP = "enable_wifi_wakeup";
55 
56     private final Fragment mFragment;
57 
58     @VisibleForTesting
59     SwitchPreference mPreference;
60     @VisibleForTesting
61     LocationManager mLocationManager;
62     private final BroadcastReceiver mLocationReceiver = new BroadcastReceiver() {
63         @Override
64         public void onReceive(Context context, Intent intent) {
65             updateState(mPreference);
66         }
67     };
68     private final IntentFilter mLocationFilter =
69             new IntentFilter(LocationManager.MODE_CHANGED_ACTION);
70 
WifiWakeupPreferenceController(Context context, DashboardFragment fragment, Lifecycle lifecycle)71     public WifiWakeupPreferenceController(Context context, DashboardFragment fragment,
72             Lifecycle lifecycle) {
73         super(context);
74         mFragment = fragment;
75         mLocationManager = (LocationManager) context.getSystemService(Service.LOCATION_SERVICE);
76         lifecycle.addObserver(this);
77     }
78 
79     @Override
displayPreference(PreferenceScreen screen)80     public void displayPreference(PreferenceScreen screen) {
81         super.displayPreference(screen);
82         mPreference = screen.findPreference(KEY_ENABLE_WIFI_WAKEUP);
83         updateState(mPreference);
84     }
85 
86     @Override
isAvailable()87     public boolean isAvailable() {
88         return true;
89     }
90 
91     @Override
handlePreferenceTreeClick(Preference preference)92     public boolean handlePreferenceTreeClick(Preference preference) {
93         if (!TextUtils.equals(preference.getKey(), KEY_ENABLE_WIFI_WAKEUP)) {
94             return false;
95         }
96         if (!(preference instanceof SwitchPreference)) {
97             return false;
98         }
99 
100         // TODO(b/132391311): WifiWakeupPreferenceController is essentially reimplementing
101         // TogglePreferenceController. Refactor it into TogglePreferenceController.
102 
103         // Toggle wifi-wakeup setting between 1/0 based on its current state, and some other checks.
104         if (isWifiWakeupAvailable()) {
105             setWifiWakeupEnabled(false);
106         } else {
107             if (!mLocationManager.isLocationEnabled()) {
108                 final Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
109                 mFragment.startActivityForResult(intent, WIFI_WAKEUP_REQUEST_CODE);
110                 return true;
111             } else if (!getWifiScanningEnabled()) {
112                 showScanningDialog();
113             } else {
114                 setWifiWakeupEnabled(true);
115             }
116         }
117 
118         updateState(mPreference);
119         return true;
120     }
121 
122     @Override
getPreferenceKey()123     public String getPreferenceKey() {
124         return KEY_ENABLE_WIFI_WAKEUP;
125     }
126 
127     @Override
updateState(Preference preference)128     public void updateState(Preference preference) {
129         if (!(preference instanceof SwitchPreference)) {
130             return;
131         }
132         final SwitchPreference enableWifiWakeup = (SwitchPreference) preference;
133 
134         enableWifiWakeup.setChecked(isWifiWakeupAvailable());
135         if (!mLocationManager.isLocationEnabled()) {
136             preference.setSummary(getNoLocationSummary());
137         } else {
138             preference.setSummary(R.string.wifi_wakeup_summary);
139         }
140     }
141 
142     @VisibleForTesting
getNoLocationSummary()143     CharSequence getNoLocationSummary() {
144         AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo("link", null);
145         CharSequence locationText = mContext.getText(R.string.wifi_wakeup_summary_no_location);
146         return AnnotationSpan.linkify(locationText, linkInfo);
147     }
148 
onActivityResult(int requestCode, int resultCode)149     public void onActivityResult(int requestCode, int resultCode) {
150         if (requestCode != WIFI_WAKEUP_REQUEST_CODE) {
151             return;
152         }
153         if (mLocationManager.isLocationEnabled() && getWifiScanningEnabled()) {
154             setWifiWakeupEnabled(true);
155         }
156         updateState(mPreference);
157     }
158 
getWifiScanningEnabled()159     private boolean getWifiScanningEnabled() {
160         return Settings.Global.getInt(mContext.getContentResolver(),
161                 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1;
162     }
163 
showScanningDialog()164     private void showScanningDialog() {
165         final WifiScanningRequiredFragment dialogFragment =
166                 WifiScanningRequiredFragment.newInstance();
167         dialogFragment.setTargetFragment(mFragment, WIFI_WAKEUP_REQUEST_CODE /* requestCode */);
168         dialogFragment.show(mFragment.getFragmentManager(), TAG);
169     }
170 
getWifiWakeupEnabled()171     private boolean getWifiWakeupEnabled() {
172         return Settings.Global.getInt(mContext.getContentResolver(),
173                 Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1;
174     }
175 
176     /**
177      * Wifi wakeup is available only when both location and Wi-Fi scanning are enabled.
178      */
isWifiWakeupAvailable()179     private boolean isWifiWakeupAvailable() {
180         return getWifiWakeupEnabled()
181                 && getWifiScanningEnabled()
182                 && mLocationManager.isLocationEnabled();
183     }
184 
setWifiWakeupEnabled(boolean enabled)185     private void setWifiWakeupEnabled(boolean enabled) {
186         Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.WIFI_WAKEUP_ENABLED,
187                 enabled ? 1 : 0);
188     }
189 
190     @Override
onResume()191     public void onResume() {
192         mContext.registerReceiver(mLocationReceiver, mLocationFilter);
193     }
194 
195     @Override
onPause()196     public void onPause() {
197         mContext.unregisterReceiver(mLocationReceiver);
198     }
199 }
200