1 /*
2  * Copyright (C) 2016 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.settings.network;
17 
18 import static android.provider.SettingsSlicesContract.KEY_AIRPLANE_MODE;
19 
20 import static com.android.settings.network.SatelliteWarningDialogActivity.EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG;
21 import static com.android.settings.network.SatelliteWarningDialogActivity.TYPE_IS_AIRPLANE_MODE;
22 
23 import android.app.Activity;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.PackageManager;
28 import android.net.Uri;
29 import android.provider.SettingsSlicesContract;
30 import android.telephony.TelephonyManager;
31 import android.util.Log;
32 
33 import androidx.annotation.VisibleForTesting;
34 import androidx.fragment.app.Fragment;
35 import androidx.preference.Preference;
36 import androidx.preference.PreferenceScreen;
37 import androidx.preference.TwoStatePreference;
38 
39 import com.android.settings.AirplaneModeEnabler;
40 import com.android.settings.R;
41 import com.android.settings.Utils;
42 import com.android.settings.core.TogglePreferenceController;
43 import com.android.settingslib.core.lifecycle.LifecycleObserver;
44 import com.android.settingslib.core.lifecycle.events.OnDestroy;
45 import com.android.settingslib.core.lifecycle.events.OnResume;
46 import com.android.settingslib.core.lifecycle.events.OnStart;
47 import com.android.settingslib.core.lifecycle.events.OnStop;
48 
49 import java.util.concurrent.ExecutionException;
50 import java.util.concurrent.Executors;
51 import java.util.concurrent.TimeUnit;
52 import java.util.concurrent.TimeoutException;
53 import java.util.concurrent.atomic.AtomicBoolean;
54 
55 public class AirplaneModePreferenceController extends TogglePreferenceController
56         implements LifecycleObserver, OnStart, OnResume, OnStop, OnDestroy,
57         AirplaneModeEnabler.OnAirplaneModeChangedListener {
58     private static final String TAG = AirplaneModePreferenceController.class.getSimpleName();
59     public static final int REQUEST_CODE_EXIT_ECM = 1;
60 
61     /**
62      * Uri for Airplane mode Slice.
63      */
64     public static final Uri SLICE_URI = new Uri.Builder()
65             .scheme(ContentResolver.SCHEME_CONTENT)
66             .authority(SettingsSlicesContract.AUTHORITY)
67             .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
68             .appendPath(SettingsSlicesContract.KEY_AIRPLANE_MODE)
69             .build();
70 
71     private Fragment mFragment;
72     private AirplaneModeEnabler mAirplaneModeEnabler;
73     private TwoStatePreference mAirplaneModePreference;
74     private SatelliteRepository mSatelliteRepository;
75     @VisibleForTesting
76     AtomicBoolean mIsSatelliteOn = new AtomicBoolean(false);
77 
AirplaneModePreferenceController(Context context, String key)78     public AirplaneModePreferenceController(Context context, String key) {
79         super(context, key);
80         if (isAvailable(mContext)) {
81             mAirplaneModeEnabler = new AirplaneModeEnabler(mContext, this);
82             mSatelliteRepository = new SatelliteRepository(mContext);
83         }
84     }
85 
setFragment(Fragment hostFragment)86     public void setFragment(Fragment hostFragment) {
87         mFragment = hostFragment;
88     }
89 
90     @VisibleForTesting
setAirplaneModeEnabler(AirplaneModeEnabler airplaneModeEnabler)91     void setAirplaneModeEnabler(AirplaneModeEnabler airplaneModeEnabler) {
92         mAirplaneModeEnabler = airplaneModeEnabler;
93     }
94 
95     @Override
handlePreferenceTreeClick(Preference preference)96     public boolean handlePreferenceTreeClick(Preference preference) {
97         if (KEY_AIRPLANE_MODE.equals(preference.getKey()) && isAvailable()) {
98             // In ECM mode launch ECM app dialog
99             if (mAirplaneModeEnabler.isInEcmMode()) {
100                 if (mFragment != null) {
101                     mFragment.startActivityForResult(
102                             new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null)
103                                     .setPackage(Utils.PHONE_PACKAGE_NAME),
104                             REQUEST_CODE_EXIT_ECM);
105                 }
106                 return true;
107             }
108 
109             if (mIsSatelliteOn.get()) {
110                 mContext.startActivity(
111                         new Intent(mContext, SatelliteWarningDialogActivity.class)
112                                 .putExtra(
113                                         EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG,
114                                         TYPE_IS_AIRPLANE_MODE)
115                 );
116                 return true;
117             }
118         }
119         return false;
120     }
121 
122     @Override
getSliceUri()123     public Uri getSliceUri() {
124         return SLICE_URI;
125     }
126 
127     @Override
displayPreference(PreferenceScreen screen)128     public void displayPreference(PreferenceScreen screen) {
129         super.displayPreference(screen);
130         mAirplaneModePreference = screen.findPreference(getPreferenceKey());
131     }
132 
isAvailable(Context context)133     public static boolean isAvailable(Context context) {
134         return context.getResources().getBoolean(R.bool.config_show_toggle_airplane)
135                 && !context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
136     }
137 
138     @Override
isPublicSlice()139     public boolean isPublicSlice() {
140         return true;
141     }
142 
143     @Override
144     @AvailabilityStatus
getAvailabilityStatus()145     public int getAvailabilityStatus() {
146         return isAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
147     }
148 
149     @Override
getSliceHighlightMenuRes()150     public int getSliceHighlightMenuRes() {
151         return R.string.menu_key_network;
152     }
153 
154     @Override
onStart()155     public void onStart() {
156         if (isAvailable()) {
157             mAirplaneModeEnabler.start();
158         }
159     }
160 
161     @Override
onResume()162     public void onResume() {
163         try {
164             mIsSatelliteOn.set(
165                     mSatelliteRepository.requestIsEnabled(Executors.newSingleThreadExecutor())
166                             .get(2000, TimeUnit.MILLISECONDS));
167         } catch (ExecutionException | TimeoutException | InterruptedException e) {
168             Log.e(TAG, "Error to get satellite status : " + e);
169         }
170     }
171 
172     @Override
onStop()173     public void onStop() {
174         if (isAvailable()) {
175             mAirplaneModeEnabler.stop();
176         }
177     }
178 
179     @Override
onDestroy()180     public void onDestroy() {
181         if (isAvailable()) {
182             mAirplaneModeEnabler.close();
183         }
184     }
185 
186 
onActivityResult(int requestCode, int resultCode, Intent data)187     public void onActivityResult(int requestCode, int resultCode, Intent data) {
188         if (requestCode == REQUEST_CODE_EXIT_ECM && isAvailable()) {
189             final boolean isChoiceYes = (resultCode == Activity.RESULT_OK);
190             // Set Airplane mode based on the return value and checkbox state
191             mAirplaneModeEnabler.setAirplaneModeInECM(isChoiceYes,
192                     mAirplaneModePreference.isChecked());
193         }
194     }
195 
196     @Override
isChecked()197     public boolean isChecked() {
198         return isAvailable() && mAirplaneModeEnabler.isAirplaneModeOn();
199     }
200 
201     @Override
setChecked(boolean isChecked)202     public boolean setChecked(boolean isChecked) {
203         if (isChecked() == isChecked || mIsSatelliteOn.get()) {
204             return false;
205         }
206         if (isAvailable()) {
207             mAirplaneModeEnabler.setAirplaneMode(isChecked);
208         }
209         return true;
210     }
211 
212     @Override
onAirplaneModeChanged(boolean isAirplaneModeOn)213     public void onAirplaneModeChanged(boolean isAirplaneModeOn) {
214         if (mAirplaneModePreference != null) {
215             mAirplaneModePreference.setChecked(isAirplaneModeOn);
216         }
217     }
218 }
219