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

Commit 1e6eb17a authored by John Spurlock's avatar John Spurlock
Browse files

QS: Introduce cast control panel.

Make cast controller more granular, at the route level, and
bind a common detail item panel to the available routes.

Fix tile icon, add connecting label.

Remove dialog presentation, no longer needed.

Add the new empty state, and add controller state to dump.

Found a bug in keyguard when in presentation mode, added
a workaround - don't crash when no owner info in keyguard
status view.

Bug:15833210
Change-Id: I19afec390eac2260bccf4a7064065b666b7bceca
parent f6e2fcc2
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -86,7 +86,7 @@ public class KeyguardStatusView extends GridLayout {
    private void setEnableMarquee(boolean enabled) {
    private void setEnableMarquee(boolean enabled) {
        if (DEBUG) Log.v(TAG, (enabled ? "Enable" : "Disable") + " transport text marquee");
        if (DEBUG) Log.v(TAG, (enabled ? "Enable" : "Disable") + " transport text marquee");
        if (mAlarmStatusView != null) mAlarmStatusView.setSelected(enabled);
        if (mAlarmStatusView != null) mAlarmStatusView.setSelected(enabled);
        mOwnerInfo.setSelected(enabled);
        if (mOwnerInfo != null) mOwnerInfo.setSelected(enabled);
    }
    }


    @Override
    @Override
@@ -131,6 +131,7 @@ public class KeyguardStatusView extends GridLayout {
    }
    }


    private void updateOwnerInfo() {
    private void updateOwnerInfo() {
        if (mOwnerInfo == null) return;
        String ownerInfo = getOwnerInfo();
        String ownerInfo = getOwnerInfo();
        if (!TextUtils.isEmpty(ownerInfo)) {
        if (!TextUtils.isEmpty(ownerInfo)) {
            mOwnerInfo.setVisibility(View.VISIBLE);
            mOwnerInfo.setVisibility(View.VISIBLE);
+28 −0
Original line number Original line Diff line number Diff line
<!--
Copyright (C) 2014 The Android Open Source Project

   Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

         http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" >
    <size
        android:width="56dp"
        android:height="56dp"/>

    <viewport
        android:viewportWidth="48.0"
        android:viewportHeight="48.0"/>

    <path
        android:fill="@color/qs_detail_empty"
        android:pathData="M42.0,6.0L6.0,6.0c-2.2,0.0 -4.0,1.8 -4.0,4.0l0.0,6.0l4.0,0.0l0.0,-6.0l36.0,0.0l0.0,28.0L28.0,38.0l0.0,4.0l14.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L46.0,10.0C46.0,7.8 44.2,6.0 42.0,6.0zM2.0,36.0l0.0,6.0l6.0,0.0C8.0,38.7 5.3,36.0 2.0,36.0zM2.0,28.0l0.0,4.0c5.5,0.0 10.0,4.5 10.0,10.0l4.0,0.0C16.0,34.3 9.7,28.0 2.0,28.0zM2.0,20.0l0.0,4.0c9.9,0.0 18.0,8.1 18.0,18.0l4.0,0.0C24.0,29.8 14.1,20.0 2.0,20.0z"/>
</vector>
+1 −1
Original line number Original line Diff line number Diff line
@@ -24,5 +24,5 @@ Copyright (C) 2014 The Android Open Source Project


    <path
    <path
        android:fill="#FFFFFFFF"
        android:fill="#FFFFFFFF"
        android:pathData="M42.0,6.0L6.0,6.0c-2.2,0.0 -4.0,1.8 -4.0,4.0l0.0,6.0l4.0,0.0l0.0,-6.0l36.0,0.0l0.0,28.0L28.0,38.0l0.0,4.0l14.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L46.0,10.0C46.0,7.8 44.2,6.0 42.0,6.0zM2.0,36.0l0.0,6.0l6.0,0.0C8.0,38.7 5.3,36.0 2.0,36.0zM2.0,28.0l0.0,4.0c5.5,0.0 10.0,4.5 10.0,10.0l4.0,0.0C16.0,34.3 9.7,28.0 2.0,28.0zM2.0,20.0l0.0,4.0c9.9,0.0 18.0,8.1 18.0,18.0l4.0,0.0C24.0,29.8 14.1,20.0 2.0,20.0z"/>
        android:pathData="M2.0,36.0l0.0,6.0l6.0,0.0C8.0,38.7 5.3,36.0 2.0,36.0zM2.0,28.0l0.0,4.0c5.5,0.0 10.0,4.5 10.0,10.0l4.0,0.0C16.0,34.3 9.7,28.0 2.0,28.0zM38.0,14.0L10.0,14.0l0.0,3.3c7.9,2.6 14.2,8.8 16.7,16.7L38.0,34.0L38.0,14.0zM2.0,20.0l0.0,4.0c9.9,0.0 18.0,8.1 18.0,18.0l4.0,0.0C24.0,29.8 14.1,20.0 2.0,20.0zM42.0,6.0L6.0,6.0c-2.2,0.0 -4.0,1.8 -4.0,4.0l0.0,6.0l4.0,0.0l0.0,-6.0l36.0,0.0l0.0,28.0L28.0,38.0l0.0,4.0l14.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L46.0,10.0C46.0,7.8 44.2,6.0 42.0,6.0z"/>
</vector>
</vector>
+10 −2
Original line number Original line Diff line number Diff line
@@ -526,8 +526,16 @@
    <string name="quick_settings_wifi_off_label">Wi-Fi Off</string>
    <string name="quick_settings_wifi_off_label">Wi-Fi Off</string>
    <!-- QuickSettings: Wifi detail panel, text when there are no items [CHAR LIMIT=NONE] -->
    <!-- QuickSettings: Wifi detail panel, text when there are no items [CHAR LIMIT=NONE] -->
    <string name="quick_settings_wifi_detail_empty_text">No saved networks available</string>
    <string name="quick_settings_wifi_detail_empty_text">No saved networks available</string>
    <!-- QuickSettings: Remote display [CHAR LIMIT=NONE] -->
    <!-- QuickSettings: Cast title [CHAR LIMIT=NONE] -->
    <string name="quick_settings_remote_display_no_connection_label">Cast screen</string>
    <string name="quick_settings_cast_title">Cast screen</string>
    <!-- QuickSettings: Cast detail panel, status text when casting [CHAR LIMIT=NONE] -->
    <string name="quick_settings_casting">Casting</string>
    <!-- QuickSettings: Cast detail panel, default device name [CHAR LIMIT=NONE] -->
    <string name="quick_settings_cast_device_default_name">Unnamed device</string>
     <!-- QuickSettings: Cast detail panel, default device description [CHAR LIMIT=NONE] -->
    <string name="quick_settings_cast_device_default_description">Ready to cast</string>
    <!-- QuickSettings: Cast detail panel, text when there are no items [CHAR LIMIT=NONE] -->
    <string name="quick_settings_cast_detail_empty_text">No devices available</string>
    <!-- QuickSettings: Brightness dialog title [CHAR LIMIT=NONE] -->
    <!-- QuickSettings: Brightness dialog title [CHAR LIMIT=NONE] -->
    <string name="quick_settings_brightness_dialog_title">Brightness</string>
    <string name="quick_settings_brightness_dialog_title">Brightness</string>
    <!-- QuickSettings: Brightness dialog auto brightness button [CHAR LIMIT=NONE] -->
    <!-- QuickSettings: Brightness dialog auto brightness button [CHAR LIMIT=NONE] -->
+142 −42
Original line number Original line Diff line number Diff line
@@ -16,17 +16,23 @@


package com.android.systemui.qs.tiles;
package com.android.systemui.qs.tiles;


import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.media.MediaRouter;
import android.provider.Settings;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;


import com.android.internal.app.MediaRouteDialogPresenter;
import com.android.systemui.R;
import com.android.systemui.R;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;

import java.util.LinkedHashMap;
import java.util.Set;


/** Quick settings tile: Cast **/
/** Quick settings tile: Cast **/
public class CastTile extends QSTile<QSTile.BooleanState> {
public class CastTile extends QSTile<QSTile.BooleanState> {
@@ -34,10 +40,17 @@ public class CastTile extends QSTile<QSTile.BooleanState> {
            new Intent(Settings.ACTION_WIFI_DISPLAY_SETTINGS);
            new Intent(Settings.ACTION_WIFI_DISPLAY_SETTINGS);


    private final CastController mController;
    private final CastController mController;
    private final CastDetailAdapter mDetailAdapter;


    public CastTile(Host host) {
    public CastTile(Host host) {
        super(host);
        super(host);
        mController = host.getCastController();
        mController = host.getCastController();
        mDetailAdapter = new CastDetailAdapter();
    }

    @Override
    public DetailAdapter getDetailAdapter() {
        return mDetailAdapter;
    }
    }


    @Override
    @Override
@@ -48,12 +61,13 @@ public class CastTile extends QSTile<QSTile.BooleanState> {
    @Override
    @Override
    public void setListening(boolean listening) {
    public void setListening(boolean listening) {
        if (mController == null) return;
        if (mController == null) return;
        if (DEBUG) Log.d(TAG, "setListening " + listening);
        if (listening) {
        if (listening) {
            mController.addCallback(mCallback);
            mController.addCallback(mCallback);
        } else {
        } else {
            mController.setDiscovering(false);
            mController.removeCallback(mCallback);
            mController.removeCallback(mCallback);
        }
        }
        mController.setDiscovering(listening);
    }
    }


    @Override
    @Override
@@ -65,61 +79,147 @@ public class CastTile extends QSTile<QSTile.BooleanState> {


    @Override
    @Override
    protected void handleClick() {
    protected void handleClick() {
        mHandler.post(new Runnable() {
        showDetail(true);
            public void run() {
                mHost.collapsePanels();
                mUiHandler.post(mShowDialog);
            }
        });
    }
    }


    @Override
    @Override
    protected void handleUpdateState(BooleanState state, Object arg) {
    protected void handleUpdateState(BooleanState state, Object arg) {
        state.visible = true;
        state.visible = true;
        state.label = mContext
        state.label = mContext.getString(R.string.quick_settings_cast_title);
                .getString(R.string.quick_settings_remote_display_no_connection_label);
        state.value = false;
        if (arg instanceof CallbackInfo) {
        final Set<CastDevice> devices = mController.getCastDevices();
            final CallbackInfo cb = (CallbackInfo) arg;
        boolean connecting = false;
            if (cb.connectedRouteName != null) {
        for (CastDevice device : devices) {
                state.value = !cb.connecting;
            if (device.state == CastDevice.STATE_CONNECTED) {
                state.value = true;
                state.label = getDeviceName(device);
            } else if (device.state == CastDevice.STATE_CONNECTING) {
                connecting = true;
            }
            }
        }
        }
        if (!state.value && connecting) {
            state.label = mContext.getString(R.string.quick_settings_connecting);
        }
        state.iconId = state.value ? R.drawable.ic_qs_cast_on : R.drawable.ic_qs_cast_off;
        state.iconId = state.value ? R.drawable.ic_qs_cast_on : R.drawable.ic_qs_cast_off;
        mDetailAdapter.updateItems(devices);
    }
    }


    private static class CallbackInfo {
    private String getDeviceName(CastDevice device) {
        boolean enabled;
        return device.name != null ? device.name
        boolean connecting;
                : mContext.getString(R.string.quick_settings_cast_device_default_name);
        String connectedRouteName;
    }
    }


    private final CastController.Callback mCallback = new CastController.Callback() {
    private final CastController.Callback mCallback = new CastController.Callback() {
        @Override
        @Override
        public void onStateChanged(boolean enabled, boolean connecting,
        public void onCastDevicesChanged() {
                String connectedRouteName) {
            refreshState();
            final CallbackInfo info = new CallbackInfo();  // TODO pool
            info.enabled = enabled;
            info.connecting = connecting;
            info.connectedRouteName = connectedRouteName;
            refreshState(info);
        }
        }
    };
    };


    private final Runnable mShowDialog = new Runnable() {
    private final class CastDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
        private Dialog mDialog;
        private final LinkedHashMap<String, CastDevice> mVisibleOrder = new LinkedHashMap<>();

        private QSDetailItems mItems;

        @Override
        public int getTitle() {
            return R.string.quick_settings_cast_title;
        }

        @Override
        public Boolean getToggleState() {
            return null;
        }

        @Override
        public Intent getSettingsIntent() {
            return WIFI_DISPLAY_SETTINGS;
        }

        @Override
        public void setToggleState(boolean state) {
            // noop
        }

        @Override
        @Override
        public void run() {
        public View createDetailView(Context context, View convertView, ViewGroup parent) {
            mDialog = MediaRouteDialogPresenter.createDialog(mContext,
            mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
                    MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
            mItems.setTagSuffix("Cast");
                    new View.OnClickListener() {
            if (convertView == null) {
                if (DEBUG) Log.d(TAG, "addOnAttachStateChangeListener");
                mItems.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
                    @Override
                    @Override
                public void onClick(View v) {
                    public void onViewAttachedToWindow(View v) {
                    mDialog.dismiss();
                        if (DEBUG) Log.d(TAG, "onViewAttachedToWindow");
                    mHost.startSettingsActivity(WIFI_DISPLAY_SETTINGS);
                    }

                    @Override
                    public void onViewDetachedFromWindow(View v) {
                        if (DEBUG) Log.d(TAG, "onViewDetachedFromWindow");
                        mVisibleOrder.clear();
                    }
                    }
                });
                });
            mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
            mDialog.show();
            }
            }
    };
            mItems.setEmptyState(R.drawable.ic_qs_cast_detail_empty,
                    R.string.quick_settings_cast_detail_empty_text);
            mItems.setCallback(this);
            updateItems(mController.getCastDevices());
            mController.setDiscovering(true);
            return mItems;
        }

        private void updateItems(Set<CastDevice> devices) {
            if (mItems == null) return;
            Item[] items = null;
            if (devices != null && !devices.isEmpty()) {
                // if we are connected, simply show that device
                for (CastDevice device : devices) {
                    if (device.state == CastDevice.STATE_CONNECTED) {
                        final Item item = new Item();
                        item.icon = R.drawable.ic_qs_cast_on;
                        item.line1 = getDeviceName(device);
                        item.line2 = mContext.getString(R.string.quick_settings_connected);
                        item.tag = device;
                        item.canDisconnect = true;
                        items = new Item[] { item };
                        break;
                    }
                }
                // otherwise list all available devices, and don't move them around
                if (items == null) {
                    for (CastDevice device : devices) {
                        mVisibleOrder.put(device.id, device);
                    }
                    items = new Item[devices.size()];
                    int i = 0;
                    for (String id : mVisibleOrder.keySet()) {
                        final CastDevice device = mVisibleOrder.get(id);
                        if (!devices.contains(device)) continue;
                        final Item item = new Item();
                        item.icon = R.drawable.ic_qs_cast_off;
                        item.line1 = getDeviceName(device);
                        if (device.state == CastDevice.STATE_CONNECTING) {
                            item.line2 = mContext.getString(R.string.quick_settings_connecting);
                        }
                        item.tag = device;
                        items[i++] = item;
                    }
                }
            }
            mItems.setItems(items);
        }

        @Override
        public void onDetailItemClick(Item item) {
            if (item == null || item.tag == null) return;
            final CastDevice device = (CastDevice) item.tag;
            mController.startCasting(device);
        }

        @Override
        public void onDetailItemDisconnect(Item item) {
            if (item == null || item.tag == null) return;
            mController.stopCasting();
        }
    }
}
}
Loading