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

Commit 9422b3f0 authored by Jens Doll's avatar Jens Doll Committed by DvTonder
Browse files

Bringing basic pie controls to CyanogenMod. (1/2)

Based on the multiple navigation key bar patch, this commit adds
basic pie controls to CyanogenMod. The implementation is inspired
by Paranoid Android but is completely rewritten.
The aim was to get as little intersection with existing code as
possible and provide a farily easy interface to extent these
controls by new slices (inherited from PieSlice).

The pie controls are only active when expanded screen is turned on.

Based on this commit there is another one that adds more optimizations
for Phone UI to pie controls. And there is a seperate commit for
the settings app, too.

Patch Set 2:
* Cyanogen Code Style.
* Some simplifications in PieSysInfo.java

Patch Set 3:
* Fixed NPE when pie controls are completely deactivated
* Preserve state of menu button

Patch Set 4:
* Fix display of Wifi SSID
* Some cleanup and coding style changes

Patch Set 5:
* Fix NPE when not connected to Wifi
* Remove AM/PM from time display (overlayed the other info)
* Fix menu button handling

Patch Set 6:
* Remove trailing whitespace in 12 hour time format

Patch Set 7:
* Tune colors
* Improve battery level and operator printout
* Remove some unused imports

Patch Set 8:
* Some refactoring on pie trigger management
* Copyright fix
* Make debug on PieLayout less noisy

Patch Set 9:
* Fix NPE one devices with no GSM/CDMA/LTE
* New color proposal
* Haptic feedback now respects system setting

Patch Set 10:
* Factor out haptic feedback into separate method

Patch Set 11:
* Prepare for "always on"
* David's color scheme

Change-Id: I90de7252d8301282d396c29bc9e9943acf7f2766
parent 13fa94eb
Loading
Loading
Loading
Loading
+33 −0
Original line number Original line Diff line number Diff line
@@ -2512,6 +2512,39 @@ public final class Settings {
         */
         */
        public static final String POINTER_SPEED = "pointer_speed";
        public static final String POINTER_SPEED = "pointer_speed";


        /**
         * Whether to enable pie controls on expanded screen?
         * The value is boolean (1 or 0).
         * Default: 0
         * @hide
         */
        public static final String PIE_CONTROLS = "pie_controls";

        /**
         * Display search button in pie?
         * The value is boolean (1 or 0).
         * @hide
         */
        public static final String PIE_SEARCH = "pie_search";

        /**
         * Locations of the pie in the screen.
         * (1<<0) = LEFT
         * (1<<1) = BOTTOM
         * (1<<2) = RIGHT
         * (1<<3) = TOP
         * Default: BOTTOM
         * @hide
         */
        public static final String PIE_GRAVITY = "pie_gravity";

        /**
         * Relative pie size (fraction)
         * Default: 1.0f
         * @hide
         */
        public static final String PIE_SIZE = "pie_size";

        /**
        /**
         * Quick Settings Panel Tiles to Use
         * Quick Settings Panel Tiles to Use
         *
         *
+11 −0
Original line number Original line Diff line number Diff line
@@ -35,4 +35,15 @@
    <!-- the darkening filter applied to notifications -->
    <!-- the darkening filter applied to notifications -->
    <drawable name="notification_icon_area_smoke">#aa000000</drawable>
    <drawable name="notification_icon_area_smoke">#aa000000</drawable>
    <color name="notification_panel_scrim_color">#B0000000</color>
    <color name="notification_panel_scrim_color">#B0000000</color>

    <!-- ==================== pie controls ==================== -->
    <!-- this color is used to darkening the whole screen when pie controls are active -->
    <color name="pie_overlay_color">#cc000000</color>
    <!-- color of an active snap point (inactive are blended with a 0x40 alpha channel) -->
    <color name="pie_snap_color">@android:color/holo_blue_light</color>
    <color name="pie_text_color">#ffffffff</color>
    <color name="pie_foreground_color">#ffffffff</color>
    <color name="pie_background_color">#dd0099cc</color>
    <color name="pie_selected_color">@android:color/holo_blue_light</color>
    <color name="pie_outline_color">#dd0099cc</color>
</resources>
</resources>
+25 −0
Original line number Original line Diff line number Diff line
@@ -216,4 +216,29 @@
    <dimen name="status_bar_battery_cluster_padding">2dip</dimen>
    <dimen name="status_bar_battery_cluster_padding">2dip</dimen>
    <!-- The margin adjusment of the text items of the battery cluster -->
    <!-- The margin adjusment of the text items of the battery cluster -->
    <dimen name="status_bar_battery_cluster_text_margin">-1dip</dimen>
    <dimen name="status_bar_battery_cluster_text_margin">-1dip</dimen>

    <!-- ==================== pie controls ==================== -->
    <!-- Actual width/height of the trigger views placed on the UI -->
    <dimen name="pie_trigger_height">3dp</dimen>
    <!-- The distance a touch event must travel on the surface to trigger the pie control -->
    <dimen name="pie_trigger_distance">8dp</dimen>
    <!-- Padding between the pie controls and the screen border -->
    <dimen name="pie_padding">16dp</dimen>

    <!-- Radius of the snap points a the border of the screen -->
    <dimen name="pie_snap_radius">30dp</dimen>
    <!-- Stroke width of the snap points -->
    <dimen name="pie_snap_outline">4dp</dimen>

    <dimen name="pie_outline">2dp</dimen>
    <!-- Text size of "normal" / small text on the pie control -->
    <dimen name="pie_textsize">12sp</dimen>
    <!-- Minimum size of imageviews placed within pie items -->
    <dimen name="pie_item_size">30dp</dimen>

    <dimen name="pie_navbar_radius">70dp</dimen>
    <dimen name="pie_navbar_height">85dp</dimen>

    <dimen name="pie_sysinfo_radius">165dp</dimen>
    <dimen name="pie_sysinfo_height">85sp</dimen>
</resources>
</resources>
+9 −0
Original line number Original line Diff line number Diff line
@@ -576,4 +576,13 @@
    <string name="navbar_menu_always_button">Menu (alwaysShow) button</string>
    <string name="navbar_menu_always_button">Menu (alwaysShow) button</string>
    <string name="navbar_menu_big_button">Menu button</string>
    <string name="navbar_menu_big_button">Menu button</string>


    <!-- Battery status in pie controls -->
    <string name="pie_battery_status_charging">Charging (<xliff:g id="percent">%d</xliff:g>%%)</string>
    <string name="pie_battery_status_full">Charged</string>
    <string name="pie_battery_status_discharging"><xliff:g id="percent">%d</xliff:g>%% remaining</string>

    <!-- Phone status in pie controls -->
    <string name="pie_phone_status_no_service">No service</string>
    <string name="pie_phone_status_airplane_mode">Airplane mode on</string>
    <string name="pie_phone_status_emergency_only">Emergency calls only</string>
</resources>
</resources>
+240 −1
Original line number Original line Diff line number Diff line
@@ -28,7 +28,10 @@ import com.android.systemui.SystemUI;
import com.android.systemui.recent.RecentTasksLoader;
import com.android.systemui.recent.RecentTasksLoader;
import com.android.systemui.recent.RecentsActivity;
import com.android.systemui.recent.RecentsActivity;
import com.android.systemui.recent.TaskDescription;
import com.android.systemui.recent.TaskDescription;
import com.android.systemui.statusbar.pie.PieLayout;
import com.android.systemui.statusbar.policy.NotificationRowLayout;
import com.android.systemui.statusbar.policy.NotificationRowLayout;
import com.android.systemui.statusbar.policy.PieController;
import com.android.systemui.statusbar.policy.PieController.Position;
import com.android.systemui.statusbar.tablet.StatusBarPanel;
import com.android.systemui.statusbar.tablet.StatusBarPanel;


import android.app.ActivityManager;
import android.app.ActivityManager;
@@ -39,9 +42,11 @@ import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.app.TaskStackBuilder;
import android.content.ActivityNotFoundException;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Configuration;
@@ -49,6 +54,7 @@ import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Rect;
import android.net.Uri;
import android.net.Uri;
import android.os.Build;
import android.os.Build;
@@ -86,6 +92,7 @@ public abstract class BaseStatusBar extends SystemUI implements
        CommandQueue.Callbacks {
        CommandQueue.Callbacks {
    public static final String TAG = "StatusBar";
    public static final String TAG = "StatusBar";
    public static final boolean DEBUG = false;
    public static final boolean DEBUG = false;
    public static final boolean DEBUG_INPUT = false;
    public static final boolean MULTIUSER_DEBUG = false;
    public static final boolean MULTIUSER_DEBUG = false;


    protected static final int MSG_TOGGLE_RECENTS_PANEL = 1020;
    protected static final int MSG_TOGGLE_RECENTS_PANEL = 1020;
@@ -154,6 +161,83 @@ public abstract class BaseStatusBar extends SystemUI implements
    private ArrayList<NavigationBarCallback> mNavigationCallbacks =
    private ArrayList<NavigationBarCallback> mNavigationCallbacks =
            new ArrayList<NavigationBarCallback>();
            new ArrayList<NavigationBarCallback>();


    // Pie Control
    protected PieController mPieController;
    protected PieLayout mPieContainer;
    private int mPieTriggerSlots;
    private View[] mPieTrigger = new View[Position.values().length];
    private PieSettingsObserver mSettingsObserver;

    private View.OnTouchListener mPieTriggerOnTouchHandler = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            final int action = event.getAction();
            final PieController.Tracker tracker = (PieController.Tracker)v.getTag();

            if (tracker == null) {
                if (DEBUG_INPUT) {
                    Slog.v(TAG, "Pie trigger onTouch: action: " + action + ", ("
                            + event.getAxisValue(MotionEvent.AXIS_X) + ","
                            + event.getAxisValue(MotionEvent.AXIS_Y) + ") position: NULL returning: false");
                }
                return false;
            }

            if (!mPieController.isShowing()) {
                if (event.getPointerCount() > 1) {
                    if (DEBUG_INPUT) {
                        Slog.v(TAG, "Pie trigger onTouch: action: " + action
                                + ", (to many pointers) position: " + tracker.position.name()
                                + " returning: false");
                    }
                    return false;
                }

                switch (action) {
                    case MotionEvent.ACTION_DOWN:
                        tracker.start(event);
                        break;
                    case MotionEvent.ACTION_MOVE:
                        if (tracker.move(event)) {
                            if (DEBUG) {
                                Slog.v(TAG, "Pie control activated on: ("
                                        + event.getAxisValue(MotionEvent.AXIS_X) + ","
                                        + event.getAxisValue(MotionEvent.AXIS_Y) + ") with position: "
                                        + tracker.position.name());
                            }
                            // send the activation to the controller
                            mPieController.activateFromTrigger(v, event, tracker.position);
                            // forward a spoofed ACTION_DOWN event
                            MotionEvent echo = event.copy();
                            echo.setAction(MotionEvent.ACTION_DOWN);
                            return mPieContainer.onTouch(v, echo);
                        }
                        break;
                    default:
                        // whatever it was, we are giving up on this one
                        tracker.active = false;
                        break;
                }
            } else {
                if (DEBUG_INPUT) {
                    Slog.v(TAG, "Pie trigger onTouch: action: " + action + ", ("
                            + event.getAxisValue(MotionEvent.AXIS_X) + ","
                            + event.getAxisValue(MotionEvent.AXIS_Y)
                            + ") position: " + tracker.position.name() + " delegating");
                }
                return mPieContainer.onTouch(v, event);
            }
            if (DEBUG_INPUT) {
                Slog.v(TAG, "Pie trigger onTouch: action: " + action + ", ("
                        + event.getAxisValue(MotionEvent.AXIS_X) + ","
                        + event.getAxisValue(MotionEvent.AXIS_Y) + ") position: "
                        + tracker.position.name() + " returning: " + tracker.active);
            }
            return tracker.active;
        }

    };

    // UI-specific methods
    // UI-specific methods


    /**
    /**
@@ -310,7 +394,19 @@ public abstract class BaseStatusBar extends SystemUI implements
                    if (true) Slog.v(TAG, "userId " + mCurrentUserId + " is in the house");
                    if (true) Slog.v(TAG, "userId " + mCurrentUserId + " is in the house");
                    userSwitched(mCurrentUserId);
                    userSwitched(mCurrentUserId);
                }
                }
            }}, filter);
            }
        }, filter);

        mPieController = new PieController(mContext);
        mPieController.attachTo(this);
        addNavigationBarCallback(mPieController);

        mSettingsObserver = new PieSettingsObserver(new Handler());

        // this calls attachPie() implicitly
        mSettingsObserver.onChange(true);

        mSettingsObserver.observe();
    }
    }


    public void userSwitched(int newUserId) {
    public void userSwitched(int newUserId) {
@@ -1206,4 +1302,147 @@ public abstract class BaseStatusBar extends SystemUI implements
            callback.setDisabledFlags(disabledFlags);
            callback.setDisabledFlags(disabledFlags);
        }
        }
    }
    }

    // Pie Controls

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        if (DEBUG) Slog.d(TAG, "Configuration changed! Update pie triggers");

        attachPie();
    }

    private final class PieSettingsObserver extends ContentObserver {
        PieSettingsObserver(Handler handler) {
            super(handler);
        }

        void observe() {
            ContentResolver resolver = mContext.getContentResolver();
            resolver.registerContentObserver(Settings.System.getUriFor(
                    Settings.System.PIE_CONTROLS), false, this);
            resolver.registerContentObserver(Settings.System.getUriFor(
                    Settings.System.PIE_GRAVITY), false, this);
            resolver.registerContentObserver(Settings.System.getUriFor(
                    Settings.System.EXPANDED_DESKTOP_STATE), false, this);
        }

        @Override
        public void onChange(boolean selfChange) {
            mPieTriggerSlots = Settings.System.getInt(mContext.getContentResolver(),
                    Settings.System.PIE_GRAVITY, Position.BOTTOM.FLAG);

            attachPie();
        }
    }

    private boolean isPieEnabled() {
        boolean expanded = Settings.System.getInt(mContext.getContentResolver(),
                Settings.System.EXPANDED_DESKTOP_STATE, 0) == 1;
        int pie = Settings.System.getInt(mContext.getContentResolver(),
                Settings.System.PIE_CONTROLS, 0);

        return (pie == 1 && expanded) || pie == 2;
    }

    private void attachPie() {
        if (isPieEnabled()) {

            // Create our container, if it does not exist already
            if (mPieContainer == null) {
                mPieContainer = new PieLayout(mContext);
                WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
                        WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                        | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
                        PixelFormat.TRANSLUCENT);
                // This title is for debugging only. See: dumpsys window
                lp.setTitle("PieControlPanel");
                lp.windowAnimations = android.R.style.Animation;
                lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_BEHIND;

                mWindowManager.addView(mPieContainer, lp);
                mPieController.attachTo(mPieContainer);
            }

            // add or update pie triggers
            if (DEBUG) Slog.d(TAG, "AttachPie with trigger position flags: " + mPieTriggerSlots);

            refreshPieTriggers();

        } else {
            for (int i = 0; i < mPieTrigger.length; i++) {
                if (mPieTrigger[i] != null) {
                    mWindowManager.removeView(mPieTrigger[i]);
                    mPieTrigger[i] = null;
                }
            }
        }
    }

    // This should only be called, when is is clear that the pie controls are active
    private void refreshPieTriggers() {
        for (Position g : Position.values()) {
            View trigger = mPieTrigger[g.INDEX];
            if (trigger == null && (mPieTriggerSlots & g.FLAG) != 0) {
                trigger = new View(mContext);
                trigger.setClickable(false);
                trigger.setLongClickable(false);
                trigger.setTag(mPieController.buildTracker(g));
                trigger.setOnTouchListener(mPieTriggerOnTouchHandler);

                if (DEBUG) {
                    trigger.setVisibility(View.VISIBLE);
                    trigger.setBackgroundColor(0x77ff0000);
                    Slog.d(TAG, "addPieTrigger on " + g.INDEX
                            + " with position: " + g + " : " + trigger.toString());
                }
                mWindowManager.addView(trigger, getPieTriggerLayoutParams(g));
                mPieTrigger[g.INDEX] = trigger;
            } else if (trigger != null && (mPieTriggerSlots & g.FLAG) == 0) {
                mWindowManager.removeView(trigger);
                mPieTrigger[g.INDEX] = null;
            } else if (trigger != null) {
                mWindowManager.updateViewLayout(trigger, getPieTriggerLayoutParams(g));
            }
        }
    }

    private WindowManager.LayoutParams getPieTriggerLayoutParams(Position position) {
        final Resources res = mContext.getResources();

        int width = (int) (res.getDisplayMetrics().widthPixels * 0.8f);
        int height = (int) (res.getDisplayMetrics().heightPixels * 0.8f);
        int triggerThickness = (int) (res.getDimensionPixelSize(R.dimen.pie_trigger_height));
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                (position == Position.TOP || position == Position.BOTTOM
                        ? width : triggerThickness),
                (position == Position.LEFT || position == Position.RIGHT
                        ? height : triggerThickness),
                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                        /* | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM */,
                PixelFormat.TRANSLUCENT);
        // This title is for debugging only. See: dumpsys window
        lp.setTitle("PieTrigger" + position.name());
        if (position == Position.LEFT || position == Position.RIGHT) {
            lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
                    | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
        } else {
            lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
                    | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
        }
        lp.gravity = position.ANDROID_GRAVITY;
        return lp;
    }

}
}
Loading