Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b1f83e44 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick Committed by Android (Google) Code Review
Browse files

Merge "Make the Power Control widget more responsive."

parents 50d4a681 94ea6e21
Loading
Loading
Loading
Loading
+343 −101
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
            new ComponentName("com.android.settings",
                    "com.android.settings.widget.SettingsAppWidgetProvider");

    private static LocalBluetoothManager mLocalBluetoothManager = null;
    private static LocalBluetoothManager sLocalBluetoothManager = null;

    private static final int BUTTON_WIFI = 0;
    private static final int BUTTON_BRIGHTNESS = 1;
@@ -58,9 +58,16 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
    private static final int BUTTON_GPS = 3;
    private static final int BUTTON_BLUETOOTH = 4;

    // This widget keeps track of two sets of states:
    // "3-state": STATE_DISABLED, STATE_ENABLED, STATE_INTERMEDIATE
    // "5-state": STATE_DISABLED, STATE_ENABLED, STATE_TURNING_ON, STATE_TURNING_OFF, STATE_UNKNOWN
    private static final int STATE_DISABLED = 0;
    private static final int STATE_ENABLED = 1;
    private static final int STATE_INTERMEDIATE = 2;
    private static final int STATE_TURNING_ON = 2;
    private static final int STATE_TURNING_OFF = 3;
    private static final int STATE_UNKNOWN = 4;
    private static final int STATE_INTERMEDIATE = 5;


    /**
     * Minimum and maximum brightnesses.  Don't go to 0 since that makes the display unusable
@@ -69,6 +76,270 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
    private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
    private static final int DEFAULT_BACKLIGHT = (int) (android.os.Power.BRIGHTNESS_ON * 0.4f);

    private static final StateTracker sWifiState = new WifiStateTracker();
    private static final StateTracker sBluetoothState = new BluetoothStateTracker();

    /**
     * The state machine for Wifi and Bluetooth toggling, tracking
     * reality versus the user's intent.
     *
     * This is necessary because reality moves relatively slowly
     * (turning on & off radio drivers), compared to user's
     * expectations.
     */
    private abstract static class StateTracker {
        // Is the state in the process of changing?
        private boolean mInTransition = false;
        private Boolean mActualState = null;  // initially not set
        private Boolean mIntendedState = null;  // initially not set

        // Did a toggle request arrive while a state update was
        // already in-flight?  If so, the mIntendedState needs to be
        // requested when the other one is done, unless we happened to
        // arrive at that state already.
        private boolean mDeferredStateChangeRequestNeeded = false;

        /**
         * User pressed a button to change the state.  Something
         * should immediately appear to the user afterwards, even if
         * we effectively do nothing.  Their press must be heard.
         */
        public final void toggleState(Context context) {
            int currentState = getTriState(context);
            boolean newState = false;
            switch (currentState) {
                case STATE_ENABLED:
                    newState = false;
                    break;
                case STATE_DISABLED:
                    newState = true;
                    break;
                case STATE_INTERMEDIATE:
                    if (mIntendedState != null) {
                        newState = !mIntendedState;
                    }
                    break;
            }
            mIntendedState = newState;
            if (mInTransition) {
                // We don't send off a transition request if we're
                // already transitioning.  Makes our state tracking
                // easier, and is probably nicer on lower levels.
                // (even though they should be able to take it...)
                mDeferredStateChangeRequestNeeded = true;
            } else {
                mInTransition = true;
                boolean showToast = newState;  // only show Toast on the up transition
                requestStateChange(context, newState, showToast);
            }
        }

        /**
         * Update internal state from a broadcast state change.
         */
        public abstract void onActualStateChange(Context context, Intent intent);

        /**
         * Sets the value that we're now in.  To be called from onActualStateChange.
         *
         * @param newState one of STATE_DISABLED, STATE_ENABLED, STATE_TURNING_ON,
         *                 STATE_TURNING_OFF, STATE_UNKNOWN
         */
        protected final void setCurrentState(Context context, int newState) {
            boolean wasInTransition = mInTransition;
            switch (newState) {
                case STATE_DISABLED:
                    mInTransition = false;
                    mActualState = false;
                    break;
                case STATE_ENABLED:
                    mInTransition = false;
                    mActualState = true;
                    break;
                case STATE_TURNING_ON:
                    mInTransition = true;
                    mActualState = false;
                    break;
                case STATE_TURNING_OFF:
                    mInTransition = true;
                    mActualState = true;
                    break;
            }

            if (wasInTransition && !mInTransition) {
                if (mDeferredStateChangeRequestNeeded) {
                    Log.v(TAG, "processing deferred state change");
                    if (mActualState != null && mIntendedState != null &&
                        mIntendedState.equals(mActualState)) {
                        Log.v(TAG, "... but intended state matches, so no changes.");
                    } else if (mIntendedState != null) {
                        mInTransition = true;
                        requestStateChange(context, mIntendedState, false /* no toast */);
                    }
                    mDeferredStateChangeRequestNeeded = false;
                }
            }
        }


        /**
         * If we're in a transition mode, this returns true if we're
         * transitioning towards being enabled.
         */
        public final boolean isTurningOn() {
            return mIntendedState != null && mIntendedState;
        }

        /**
         * Returns simplified 3-state value from underlying 5-state.
         *
         * @param context
         * @return STATE_ENABLED, STATE_DISABLED, or STATE_INTERMEDIATE
         */
        public final int getTriState(Context context) {
            switch (getActualState(context)) {
                case STATE_DISABLED:
                    return STATE_DISABLED;
                case STATE_ENABLED:
                    return STATE_ENABLED;
                default:
                    return STATE_INTERMEDIATE;
            }
        }

        /**
         * Gets underlying actual state.
         *
         * @param context
         * @return STATE_ENABLED, STATE_DISABLED, STATE_ENABLING, STATE_DISABLING,
         *         or or STATE_UNKNOWN.
         */
        public abstract int getActualState(Context context);

        /**
         * Actually make the desired change to the underlying radio
         * API.
         */
        protected abstract void requestStateChange(Context context,
                                                   boolean desiredState, boolean withToast);
    }

    /**
     * Subclass of StateTracker to get/set Wifi state.
     */
    private static final class WifiStateTracker extends StateTracker {
        @Override
        public int getActualState(Context context) {
            WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
            if (wifiManager != null) {
                return wifiStateToFiveState(wifiManager.getWifiState());
            }
            return STATE_UNKNOWN;
        }

        @Override
        protected void requestStateChange(Context context,
                                          boolean desiredState, boolean withToast) {
            WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
            if (wifiManager == null) {
                Log.d(TAG, "No wifiManager.");
                return;
            }
            if (withToast) {
                Toast.makeText(context, R.string.gadget_toggle_wifi, Toast.LENGTH_SHORT).show();
            }
            wifiManager.setWifiEnabled(desiredState);
        }

        @Override
        public void onActualStateChange(Context context, Intent intent) {
            if (!WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
                return;
            }
            int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, -1);
            setCurrentState(context, wifiStateToFiveState(wifiState));
        }

        /**
         * Converts WifiManager's state values into our
         * Wifi/Bluetooth-common state values.
         */
        private static int wifiStateToFiveState(int wifiState) {
            switch (wifiState) {
                case WifiManager.WIFI_STATE_DISABLED:
                    return STATE_DISABLED;
                case WifiManager.WIFI_STATE_ENABLED:
                    return STATE_ENABLED;
                case WifiManager.WIFI_STATE_DISABLING:
                    return STATE_TURNING_OFF;
                case WifiManager.WIFI_STATE_ENABLING:
                    return STATE_TURNING_ON;
                default:
                    return STATE_UNKNOWN;
            }
        }
    }

    /**
     * Subclass of StateTracker to get/set Bluetooth state.
     */
    private static final class BluetoothStateTracker extends StateTracker {

        @Override
        public int getActualState(Context context) {
            if (sLocalBluetoothManager == null) {
                sLocalBluetoothManager = LocalBluetoothManager.getInstance(context);
                if (sLocalBluetoothManager == null) {
                    return STATE_UNKNOWN;  // On emulator?
                }
            }
            return bluetoothStateToFiveState(sLocalBluetoothManager.getBluetoothState());
        }

        @Override
        protected void requestStateChange(Context context,
                                          boolean desiredState, boolean withToast) {
            if (sLocalBluetoothManager == null) {
                Log.d(TAG, "No LocalBluetoothManager");
                return;
            }
            if (withToast) {
                Toast.makeText(context,
                               R.string.gadget_toggle_bluetooth, Toast.LENGTH_SHORT).show();
            }
            sLocalBluetoothManager.setBluetoothEnabled(desiredState);
        }

        @Override
        public void onActualStateChange(Context context, Intent intent) {
            if (!BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
                return;
            }
            int bluetoothState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
            setCurrentState(context, bluetoothStateToFiveState(bluetoothState));
        }

        /**
         * Converts BluetoothAdapter's state values into our
         * Wifi/Bluetooth-common state values.
         */
        private static int bluetoothStateToFiveState(int bluetoothState) {
            switch (bluetoothState) {
                case BluetoothAdapter.STATE_OFF:
                    return STATE_DISABLED;
                case BluetoothAdapter.STATE_ON:
                    return STATE_ENABLED;
                case BluetoothAdapter.STATE_TURNING_ON:
                    return STATE_TURNING_ON;
                case BluetoothAdapter.STATE_TURNING_OFF:
                    return STATE_TURNING_OFF;
                default:
                    return STATE_UNKNOWN;
            }
        }
    }


    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
            int[] appWidgetIds) {
@@ -142,29 +413,53 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
     * @param context
     */
    private static void updateButtons(RemoteViews views, Context context) {
        switch (getWifiState(context)) {
        switch (sWifiState.getTriState(context)) {
            case STATE_DISABLED:
                views.setImageViewResource(R.id.img_wifi, R.drawable.ic_appwidget_settings_wifi_off);
                views.setImageViewResource(R.id.ind_wifi, R.drawable.appwidget_settings_ind_off_l);
                views.setImageViewResource(R.id.img_wifi,
                                           R.drawable.ic_appwidget_settings_wifi_off);
                views.setImageViewResource(R.id.ind_wifi,
                                           R.drawable.appwidget_settings_ind_off_l);
                break;
            case STATE_ENABLED:
                views.setImageViewResource(R.id.img_wifi, R.drawable.ic_appwidget_settings_wifi_on);
                views.setImageViewResource(R.id.ind_wifi, R.drawable.appwidget_settings_ind_on_l);
                views.setImageViewResource(R.id.img_wifi,
                                           R.drawable.ic_appwidget_settings_wifi_on);
                views.setImageViewResource(R.id.ind_wifi,
                                           R.drawable.appwidget_settings_ind_on_l);
                break;
            case STATE_INTERMEDIATE:
                views.setImageViewResource(R.id.img_wifi, R.drawable.ic_appwidget_settings_wifi_off);
                views.setImageViewResource(R.id.ind_wifi, R.drawable.appwidget_settings_ind_mid_l);
                // In the transitional state, the bottom green bar
                // shows the tri-state (on, off, transitioning), but
                // the top dark-gray-or-bright-white logo shows the
                // user's intent.  This is much easier to see in
                // sunlight.
                if (sWifiState.isTurningOn()) {
                    views.setImageViewResource(R.id.img_wifi,
                                               R.drawable.ic_appwidget_settings_wifi_on);
                    views.setImageViewResource(R.id.ind_wifi,
                                               R.drawable.appwidget_settings_ind_mid_l);
                } else {
                    views.setImageViewResource(R.id.img_wifi,
                                               R.drawable.ic_appwidget_settings_wifi_off);
                    views.setImageViewResource(R.id.ind_wifi,
                                               R.drawable.appwidget_settings_ind_off_l);
                }
                break;
        }
        if (getBrightnessMode(context)) {
            views.setImageViewResource(R.id.img_brightness, R.drawable.ic_appwidget_settings_brightness_auto);
            views.setImageViewResource(R.id.ind_brightness, R.drawable.appwidget_settings_ind_on_r);
            views.setImageViewResource(R.id.img_brightness,
                                       R.drawable.ic_appwidget_settings_brightness_auto);
            views.setImageViewResource(R.id.ind_brightness,
                                       R.drawable.appwidget_settings_ind_on_r);
        } else if (getBrightness(context)) {
            views.setImageViewResource(R.id.img_brightness, R.drawable.ic_appwidget_settings_brightness_on);
            views.setImageViewResource(R.id.ind_brightness, R.drawable.appwidget_settings_ind_on_r);
            views.setImageViewResource(R.id.img_brightness,
                                       R.drawable.ic_appwidget_settings_brightness_on);
            views.setImageViewResource(R.id.ind_brightness,
                                       R.drawable.appwidget_settings_ind_on_r);
        } else {
            views.setImageViewResource(R.id.img_brightness, R.drawable.ic_appwidget_settings_brightness_off);
            views.setImageViewResource(R.id.ind_brightness, R.drawable.appwidget_settings_ind_off_r);
            views.setImageViewResource(R.id.img_brightness,
                                       R.drawable.ic_appwidget_settings_brightness_off);
            views.setImageViewResource(R.id.ind_brightness,
                                       R.drawable.appwidget_settings_ind_off_r);
        }
        if (getSync(context)) {
            views.setImageViewResource(R.id.img_sync, R.drawable.ic_appwidget_settings_sync_on);
@@ -180,18 +475,36 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
            views.setImageViewResource(R.id.img_gps, R.drawable.ic_appwidget_settings_gps_off);
            views.setImageViewResource(R.id.ind_gps, R.drawable.appwidget_settings_ind_off_c);
        }
        switch (getBluetoothState(context)) {
        switch (sBluetoothState.getTriState(context)) {
            case STATE_DISABLED:
                views.setImageViewResource(R.id.img_bluetooth, R.drawable.ic_appwidget_settings_bluetooth_off);
                views.setImageViewResource(R.id.ind_bluetooth, R.drawable.appwidget_settings_ind_off_c);
                views.setImageViewResource(R.id.img_bluetooth,
                                           R.drawable.ic_appwidget_settings_bluetooth_off);
                views.setImageViewResource(R.id.ind_bluetooth,
                                           R.drawable.appwidget_settings_ind_off_c);
                break;
            case STATE_ENABLED:
                views.setImageViewResource(R.id.img_bluetooth, R.drawable.ic_appwidget_settings_bluetooth_on);
                views.setImageViewResource(R.id.ind_bluetooth, R.drawable.appwidget_settings_ind_on_c);
                views.setImageViewResource(R.id.img_bluetooth,
                                           R.drawable.ic_appwidget_settings_bluetooth_on);
                views.setImageViewResource(R.id.ind_bluetooth,
                                           R.drawable.appwidget_settings_ind_on_c);
                break;
            case STATE_INTERMEDIATE:
                views.setImageViewResource(R.id.img_bluetooth, R.drawable.ic_appwidget_settings_bluetooth_off);
                views.setImageViewResource(R.id.ind_bluetooth, R.drawable.appwidget_settings_ind_mid_c);
                // In the transitional state, the bottom green bar
                // shows the tri-state (on, off, transitioning), but
                // the top dark-gray-or-bright-white logo shows the
                // user's intent.  This is much easier to see in
                // sunlight.
                if (sBluetoothState.isTurningOn()) {
                    views.setImageViewResource(R.id.img_bluetooth,
                                               R.drawable.ic_appwidget_settings_bluetooth_on);
                    views.setImageViewResource(R.id.ind_bluetooth,
                                               R.drawable.appwidget_settings_ind_mid_c);
                } else {
                    views.setImageViewResource(R.id.img_bluetooth,
                                               R.drawable.ic_appwidget_settings_bluetooth_off);
                    views.setImageViewResource(R.id.ind_bluetooth,
                                               R.drawable.appwidget_settings_ind_off_c);
                }
                break;
        }
    }
@@ -223,11 +536,15 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) {
        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
            sWifiState.onActualStateChange(context, intent);
        } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
            sBluetoothState.onActualStateChange(context, intent);
        } else if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) {
            Uri data = intent.getData();
            int buttonId = Integer.parseInt(data.getSchemeSpecificPart());
            if (buttonId == BUTTON_WIFI) {
                toggleWifi(context);
                sWifiState.toggleState(context);
            } else if (buttonId == BUTTON_BRIGHTNESS) {
                toggleBrightness(context);
            } else if (buttonId == BUTTON_SYNC) {
@@ -235,49 +552,14 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
            } else if (buttonId == BUTTON_GPS) {
                toggleGps(context);
            } else if (buttonId == BUTTON_BLUETOOTH) {
                toggleBluetooth(context);
                sBluetoothState.toggleState(context);
            }
        }

        // State changes fall through
        updateWidget(context);
    }

    /**
     * Gets the state of Wi-Fi
     *
     * @param context
     * @return STATE_ENABLED, STATE_DISABLED, or STATE_INTERMEDIATE
     */
    private static int getWifiState(Context context) {
        WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        int wifiState = wifiManager.getWifiState();
        if (wifiState == WifiManager.WIFI_STATE_DISABLED) {
            return STATE_DISABLED;
        } else if (wifiState == WifiManager.WIFI_STATE_ENABLED) {
            return STATE_ENABLED;
        } else {
            return STATE_INTERMEDIATE;
        }
    }

    /**
     * Toggles the state of Wi-Fi
     *
     * @param context
     */
    private void toggleWifi(Context context) {
        WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        int wifiState = getWifiState(context);
        if (wifiState == STATE_ENABLED) {
            wifiManager.setWifiEnabled(false);
            Toast.makeText(context, R.string.gadget_toggle_wifi, Toast.LENGTH_SHORT).show();
        } else if (wifiState == STATE_DISABLED) {
            wifiManager.setWifiEnabled(true);
            Toast.makeText(context, R.string.gadget_toggle_wifi, Toast.LENGTH_SHORT).show();
        }
        // If wifi is in intermediate state, don't do anything.
    }

    /**
     * Gets the state of background data.
     *
@@ -459,44 +741,4 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
            Log.d(TAG, "toggleBrightness: " + e);
        }
    }

    /**
     * Gets state of bluetooth
     *
     * @param context
     * @return STATE_ENABLED, STATE_DISABLED, or STATE_INTERMEDIATE
     */
    private static int getBluetoothState(Context context) {
        if (mLocalBluetoothManager == null) {
            mLocalBluetoothManager = LocalBluetoothManager.getInstance(context);
            if (mLocalBluetoothManager == null) {
                return STATE_INTERMEDIATE; // On emulator?
            }
        }
        int state = mLocalBluetoothManager.getBluetoothState();
        if (state == BluetoothAdapter.STATE_OFF) {
            return STATE_DISABLED;
        } else if (state == BluetoothAdapter.STATE_ON) {
            return STATE_ENABLED;
        } else {
            return STATE_INTERMEDIATE;
        }
    }

    /**
     * Toggles the state of bluetooth
     *
     * @param context
     */
    private void toggleBluetooth(Context context) {
        int state = getBluetoothState(context);
        if (state == STATE_ENABLED) {
            mLocalBluetoothManager.setBluetoothEnabled(false);
            Toast.makeText(context, R.string.gadget_toggle_bluetooth, Toast.LENGTH_SHORT).show();
        } else if (state == STATE_DISABLED) {
            mLocalBluetoothManager.setBluetoothEnabled(true);
            Toast.makeText(context, R.string.gadget_toggle_bluetooth, Toast.LENGTH_SHORT).show();
        }
        // If bluetooth is in intermediate state, don't do anything.
    }
}