1c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock/* 2c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock * Copyright (C) 2014 The Android Open Source Project 3c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock * 4c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock * Licensed under the Apache License, Version 2.0 (the "License"); 5c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock * you may not use this file except in compliance with the License. 6c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock * You may obtain a copy of the License at 7c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock * 8c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock * http://www.apache.org/licenses/LICENSE-2.0 9c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock * 10c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock * Unless required by applicable law or agreed to in writing, software 11c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock * distributed under the License is distributed on an "AS IS" BASIS, 12c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock * See the License for the specific language governing permissions and 14c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock * limitations under the License. 15c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock */ 16c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock 17c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlockpackage com.android.settings.fuelgauge; 18c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock 1941e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlockimport static android.os.PowerManager.ACTION_POWER_SAVE_MODE_CHANGING; 2041e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock 2141e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlockimport android.content.BroadcastReceiver; 22c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlockimport android.content.ContentResolver; 23c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlockimport android.content.Context; 2441e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlockimport android.content.Intent; 2541e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlockimport android.content.IntentFilter; 26c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlockimport android.content.res.Resources; 27c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlockimport android.database.ContentObserver; 28c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlockimport android.net.Uri; 29c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlockimport android.os.AsyncTask; 30c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlockimport android.os.Bundle; 31c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlockimport android.os.Handler; 3241e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlockimport android.os.PowerManager; 33c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlockimport android.provider.Settings.Global; 34c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlockimport android.util.Log; 35249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlockimport android.widget.Switch; 36c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock 37c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlockimport com.android.settings.R; 38249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlockimport com.android.settings.SettingsActivity; 39c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlockimport com.android.settings.SettingsPreferenceFragment; 40c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlockimport com.android.settings.notification.SettingPref; 41249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlockimport com.android.settings.widget.SwitchBar; 42c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock 43249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlockpublic class BatterySaverSettings extends SettingsPreferenceFragment 44249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock implements SwitchBar.OnSwitchChangeListener { 45c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock private static final String TAG = "BatterySaverSettings"; 46249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 47c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock private static final String KEY_TURN_ON_AUTOMATICALLY = "turn_on_automatically"; 48c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock private static final long WAIT_FOR_SWITCH_ANIM = 500; 49c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock 505845d98a8aa0bb75bff6bd30646cdc8867002f99John Spurlock private final Handler mHandler = new Handler(); 51c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock private final SettingsObserver mSettingsObserver = new SettingsObserver(mHandler); 5241e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock private final Receiver mReceiver = new Receiver(); 53c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock 54c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock private Context mContext; 555fe76205b607f193b5925eccfabddc145118b5eaJohn Spurlock private boolean mCreated; 56c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock private SettingPref mTriggerPref; 57249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock private SwitchBar mSwitchBar; 58249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock private Switch mSwitch; 59249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock private boolean mValidListener; 6041e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock private PowerManager mPowerManager; 61c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock 62c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock @Override 63c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock public void onActivityCreated(Bundle savedInstanceState) { 64c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock super.onActivityCreated(savedInstanceState); 655fe76205b607f193b5925eccfabddc145118b5eaJohn Spurlock if (mCreated) return; 665fe76205b607f193b5925eccfabddc145118b5eaJohn Spurlock mCreated = true; 67c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock addPreferencesFromResource(R.xml.battery_saver_settings); 68c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock 69c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock mContext = getActivity(); 70249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock mSwitchBar = ((SettingsActivity) mContext).getSwitchBar(); 71249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock mSwitch = mSwitchBar.getSwitch(); 72249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock mSwitchBar.show(); 73249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock 74c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock mTriggerPref = new SettingPref(SettingPref.TYPE_GLOBAL, KEY_TURN_ON_AUTOMATICALLY, 75de159c8d16d94785476000e209daa7b5bd6ab5faDianne Hackborn Global.LOW_POWER_MODE_TRIGGER_LEVEL, 76249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock 0, /*default*/ 77c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock getResources().getIntArray(R.array.battery_saver_trigger_values)) { 78c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock @Override 79c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock protected String getCaption(Resources res, int value) { 80c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock if (value > 0 && value < 100) { 81c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock return res.getString(R.string.battery_saver_turn_on_automatically_pct, value); 82c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock } 83c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock return res.getString(R.string.battery_saver_turn_on_automatically_never); 84c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock } 85c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock }; 86c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock mTriggerPref.init(this); 8741e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock 8841e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 89c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock } 90c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock 91c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock @Override 92249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock public void onDestroyView() { 93249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock super.onDestroyView(); 94249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock mSwitchBar.hide(); 95249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock } 96249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock 97249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock @Override 98c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock public void onResume() { 99c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock super.onResume(); 100c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock mSettingsObserver.setListening(true); 10141e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock mReceiver.setListening(true); 102249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock if (!mValidListener) { 103249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock mSwitchBar.addOnSwitchChangeListener(this); 104249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock mValidListener = true; 105249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock } 106249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock updateSwitch(); 107c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock } 108c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock 109c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock @Override 110c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock public void onPause() { 111c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock super.onPause(); 112c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock mSettingsObserver.setListening(false); 11341e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock mReceiver.setListening(false); 114249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock if (mValidListener) { 115249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock mSwitchBar.removeOnSwitchChangeListener(this); 116249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock mValidListener = false; 117249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock } 118249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock } 119249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock 120249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock @Override 121249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock public void onSwitchChanged(Switch switchView, boolean isChecked) { 12241e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock mHandler.removeCallbacks(mStartMode); 123249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock if (isChecked) { 124249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock mHandler.postDelayed(mStartMode, WAIT_FOR_SWITCH_ANIM); 125249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock } else { 12641e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock if (DEBUG) Log.d(TAG, "Stopping low power mode from settings"); 12741e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock trySetPowerSaveMode(false); 12841e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock } 12941e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock } 13041e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock 13141e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock private void trySetPowerSaveMode(boolean mode) { 13241e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock if (!mPowerManager.setPowerSaveMode(mode)) { 13341e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock if (DEBUG) Log.d(TAG, "Setting mode failed, fallback to current value"); 13441e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock mHandler.post(mUpdateSwitch); 135249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock } 136249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock } 137249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock 138249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock private void updateSwitch() { 13941e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock final boolean mode = mPowerManager.isPowerSaveMode(); 14041e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock if (DEBUG) Log.d(TAG, "updateSwitch: isChecked=" + mSwitch.isChecked() + " mode=" + mode); 14141e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock if (mode == mSwitch.isChecked()) return; 142249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock 143249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock // set listener to null so that that code below doesn't trigger onCheckedChanged() 144249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock if (mValidListener) { 145249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock mSwitchBar.removeOnSwitchChangeListener(this); 146249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock } 14741e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock mSwitch.setChecked(mode); 148249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock if (mValidListener) { 149249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock mSwitchBar.addOnSwitchChangeListener(this); 150249cf50b19c5b1d9ddce0667fb9d439fcdb6f033John Spurlock } 151c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock } 152c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock 15341e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock private final Runnable mUpdateSwitch = new Runnable() { 15441e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock @Override 15541e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock public void run() { 15641e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock updateSwitch(); 15741e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock } 15841e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock }; 15941e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock 1605845d98a8aa0bb75bff6bd30646cdc8867002f99John Spurlock private final Runnable mStartMode = new Runnable() { 161c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock @Override 1625845d98a8aa0bb75bff6bd30646cdc8867002f99John Spurlock public void run() { 1635845d98a8aa0bb75bff6bd30646cdc8867002f99John Spurlock AsyncTask.execute(new Runnable() { 1645845d98a8aa0bb75bff6bd30646cdc8867002f99John Spurlock @Override 1655845d98a8aa0bb75bff6bd30646cdc8867002f99John Spurlock public void run() { 16641e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock if (DEBUG) Log.d(TAG, "Starting low power mode from settings"); 16741e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock trySetPowerSaveMode(true); 1685845d98a8aa0bb75bff6bd30646cdc8867002f99John Spurlock } 1695845d98a8aa0bb75bff6bd30646cdc8867002f99John Spurlock }); 170c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock } 1715845d98a8aa0bb75bff6bd30646cdc8867002f99John Spurlock }; 172c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock 17341e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock private final class Receiver extends BroadcastReceiver { 17441e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock private boolean mRegistered; 17541e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock 17641e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock @Override 17741e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock public void onReceive(Context context, Intent intent) { 17841e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock if (DEBUG) Log.d(TAG, "Received " + intent.getAction()); 17941e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock mHandler.post(mUpdateSwitch); 18041e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock } 18141e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock 18241e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock public void setListening(boolean listening) { 18341e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock if (listening && !mRegistered) { 18441e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock mContext.registerReceiver(this, new IntentFilter(ACTION_POWER_SAVE_MODE_CHANGING)); 18541e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock mRegistered = true; 18641e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock } else if (!listening && mRegistered) { 18741e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock mContext.unregisterReceiver(this); 18841e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock mRegistered = false; 18941e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock } 19041e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock } 19141e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock } 19241e8351448ccebbc0ffdf1caf811c1074e8ee988John Spurlock 193c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock private final class SettingsObserver extends ContentObserver { 194c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock private final Uri LOW_POWER_MODE_TRIGGER_LEVEL_URI 195c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock = Global.getUriFor(Global.LOW_POWER_MODE_TRIGGER_LEVEL); 196c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock 197c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock public SettingsObserver(Handler handler) { 198c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock super(handler); 199c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock } 200c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock 201c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock @Override 202c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock public void onChange(boolean selfChange, Uri uri) { 203c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock if (LOW_POWER_MODE_TRIGGER_LEVEL_URI.equals(uri)) { 2045845d98a8aa0bb75bff6bd30646cdc8867002f99John Spurlock mTriggerPref.update(mContext); 205c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock } 206c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock } 207c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock 208c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock public void setListening(boolean listening) { 209c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock final ContentResolver cr = getContentResolver(); 210c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock if (listening) { 211c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock cr.registerContentObserver(LOW_POWER_MODE_TRIGGER_LEVEL_URI, false, this); 212c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock } else { 213c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock cr.unregisterContentObserver(this); 214c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock } 215c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock } 216c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock } 217c7f8e8ce69e80ea495472aade5f8569c6583c5e8John Spurlock} 218