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

Commit 1823f11d authored by Fabian Kozynski's avatar Fabian Kozynski
Browse files

QSFooterImpl shows multiple carriers

QSFooter now shows up to two subscriptions with corresponding signal
rate graphic.

Modified CarrierTextCallback to pass an info struct.

Pending design guidelines.

Test: manual using DSDS
Bug: 113343186
Change-Id: Ic98d52ae59139132533a86fcc279a4f64c98c91e
parent 02941af5
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
<!--
  ~ Copyright (C) 2019 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
  -->

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/linear_footer_carrier"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:layout_weight="1"
    android:gravity="center_vertical|start"
    android:background="@android:color/transparent"
    android:clickable="false"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:paddingStart="16dp" >

    <include
        layout="@layout/mobile_signal_group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:visibility="gone" />

    <view class="com.android.systemui.qs.QSFooterImpl$QSCarrierText"
        android:id="@+id/qs_carrier_text"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:ellipsize="marquee"
        android:textAppearance="@style/TextAppearance.QS.CarrierInfo"
        android:textColor="?android:attr/textColorPrimary"
        android:textDirection="locale"
        android:singleLine="true" />

</LinearLayout>
 No newline at end of file
+17 −16
Original line number Diff line number Diff line
@@ -42,30 +42,31 @@
        android:gravity="end" >

        <LinearLayout
            android:id="@+id/qs_mobile"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center_vertical|start"
            android:paddingStart="16dp">
            android:orientation="horizontal"
            android:layout_marginEnd="32dp">

            <include
                layout="@layout/mobile_signal_group"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="8dp"
                layout="@layout/qs_footer_carrier"
                android:id="@+id/carrier1" />

            <View
                android:id="@+id/qs_carrier_divider"
                android:layout_width="2dp"
                android:layout_height="match_parent"
                android:layout_marginTop="15dp"
                android:layout_marginBottom="15dp"
                android:background="?android:attr/dividerVertical"
                android:visibility="gone" />

            <com.android.keyguard.CarrierText
                android:id="@+id/qs_carrier_text"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:layout_marginEnd="32dp"
                android:ellipsize="marquee"
                android:textAppearance="@style/TextAppearance.QS.CarrierInfo"
                android:textColor="?android:attr/textColorPrimary"
                android:textDirection="locale"
                android:singleLine="true" />
            <include
                layout="@layout/qs_footer_carrier"
                android:id="@+id/carrier2"
                android:visibility="gone"/>

        </LinearLayout>

+3 −4
Original line number Diff line number Diff line
@@ -42,8 +42,8 @@ public class CarrierText extends TextView {
    private CarrierTextController.CarrierTextCallback mCarrierTextCallback =
            new CarrierTextController.CarrierTextCallback() {
                @Override
                public void updateCarrierText(CharSequence carrierText, boolean simsReady) {
                    setText(carrierText);
                public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
                    setText(info.carrierText);
                }

                @Override
@@ -53,7 +53,7 @@ public class CarrierText extends TextView {

                @Override
                public void finishedWakingUp() {
                    setSelected(mShouldMarquee);
                    setSelected(true);
                }
            };

@@ -85,7 +85,6 @@ public class CarrierText extends TextView {
                mShowMissingSim);
        mShouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
        setSelected(mShouldMarquee); // Allow marquee to work.

    }

    @Override
+30 −24
Original line number Diff line number Diff line
@@ -46,17 +46,11 @@ public class CarrierTextController {
    private static final String TAG = "CarrierTextController";

    private final boolean mIsEmergencyCallCapable;

    private boolean mTelephonyCapable;

    private boolean mShowMissingSim;

    private boolean mShowAirplaneMode;

    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;

    private WifiManager mWifiManager;

    private boolean[] mSimErrorState = new boolean[TelephonyManager.getDefault().getPhoneCount()];
    private CarrierTextCallback mCarrierTextCallback;
    private Context mContext;
@@ -132,10 +126,8 @@ public class CarrierTextController {
    /**
     * Controller that provides updates on text with carriers names or SIM status.
     * Used by {@link CarrierText}.
     * @param context
     *
     * @param separator Separator between different parts of the text
     * @param showAirplaneMode
     * @param showMissingSim
     */
    public CarrierTextController(Context context, CharSequence separator, boolean showAirplaneMode,
            boolean showMissingSim) {
@@ -186,6 +178,7 @@ public class CarrierTextController {
    /**
     * Sets the listening status of this controller. If the callback is null, it is set to
     * not listening
     *
     * @param callback Callback to provide text updates
     */
    public void setListening(CarrierTextCallback callback) {
@@ -199,7 +192,7 @@ public class CarrierTextController {
            } else {
                // Don't listen and clear out the text when the device isn't a phone.
                mKeyguardUpdateMonitor = null;
                callback.updateCarrierText("", false);
                callback.updateCarrierInfo(new CarrierTextCallbackInfo("", null, false, null));
            }
        } else {
            mCarrierTextCallback = null;
@@ -217,9 +210,11 @@ public class CarrierTextController {

        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
        final int numSubs = subs.size();
        final int[] subsIds = new int[numSubs];
        if (DEBUG) Log.d(TAG, "updateCarrierText(): " + numSubs);
        for (int i = 0; i < numSubs; i++) {
            int subId = subs.get(i).getSubscriptionId();
            subsIds[i] = subId;
            IccCardConstants.State simState = mKeyguardUpdateMonitor.getSimState(subId);
            CharSequence carrierName = subs.get(i).getCarrierName();
            CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName);
@@ -294,9 +289,11 @@ public class CarrierTextController {
        }

        if (mCarrierTextCallback != null) {
            mCarrierTextCallback.updateCarrierText(displayText, anySimReadyAndInService);
            mCarrierTextCallback.updateCarrierList(
                    displayText.toString().split(mSeparator.toString()), anySimReadyAndInService);
            mCarrierTextCallback.updateCarrierInfo(new CarrierTextCallbackInfo(
                    displayText,
                    displayText.toString().split(mSeparator.toString()),
                    anySimReadyAndInService,
                    subsIds));
        }

    }
@@ -482,22 +479,31 @@ public class CarrierTextController {
    }

    /**
     * Callback to communicate to Views
     * Data structure for passing information to CarrierTextController subscribers
     */
    public interface CarrierTextCallback {
    public static final class CarrierTextCallbackInfo {
        public final CharSequence carrierText;
        public final CharSequence[] listOfCarriers;
        public final boolean anySimReady;
        public final int[] subscriptionIds;

        CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
                boolean anySimReady, int[] subscriptionIds) {
            this.carrierText = carrierText;
            this.listOfCarriers = listOfCarriers;
            this.anySimReady = anySimReady;
            this.subscriptionIds = subscriptionIds;
        }
    }

    /**
         * Provides an updated list of carrier names
         * @param listOfCarriers
         * @param simsReady Whether at least one SIM is ready and with service
     * Callback to communicate to Views
     */
        default void updateCarrierList(CharSequence[] listOfCarriers, boolean simsReady) {};

    public interface CarrierTextCallback {
        /**
         * Provides an updated full carrier text
         * @param carrierText
         * @param simsReady Whether at least one SIM is ready and with service
         * Provides updated carrier information.
         */
        default void updateCarrierText(CharSequence carrierText, boolean simsReady) {};
        default void updateCarrierInfo(CarrierTextCallbackInfo info) {};

        /**
         * Notifies the View that the device is going to sleep
+142 −43
Original line number Diff line number Diff line
@@ -30,14 +30,18 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Bundle;
import android.os.UserManager;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.Nullable;
@@ -45,7 +49,7 @@ import androidx.annotation.VisibleForTesting;

import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.keyguard.CarrierText;
import com.android.keyguard.CarrierTextController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.Utils;
import com.android.settingslib.drawable.UserIconDrawable;
@@ -68,7 +72,11 @@ import javax.inject.Inject;
import javax.inject.Named;

public class QSFooterImpl extends FrameLayout implements QSFooter,
        OnClickListener, OnUserInfoChangedListener, EmergencyListener, SignalCallback {
        OnClickListener, OnUserInfoChangedListener, EmergencyListener, SignalCallback,
        CarrierTextController.CarrierTextCallback {

    private static final int SIM_SLOTS = 2;
    private static final String TAG = "QSFooterImpl";

    private final ActivityStarter mActivityStarter;
    private final UserInfoController mUserInfoController;
@@ -77,7 +85,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
    private SettingsButton mSettingsButton;
    protected View mSettingsContainer;
    private PageIndicator mPageIndicator;
    private CarrierText mCarrierText;

    private boolean mQsDisabled;
    private QSPanel mQsPanel;
@@ -99,12 +106,20 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,

    private View mActionsContainer;
    private View mDragHandle;
    private View mMobileGroup;
    private ImageView mMobileSignal;
    private ImageView mMobileRoaming;

    private View mCarrierDivider;
    private ViewGroup mMobileFooter;
    private View[] mMobileGroups = new View[SIM_SLOTS];
    private ViewGroup[] mCarrierGroups = new ViewGroup[SIM_SLOTS];
    private TextView[] mCarrierTexts = new TextView[SIM_SLOTS];
    private ImageView[] mMobileSignals = new ImageView[SIM_SLOTS];
    private ImageView[] mMobileRoamings = new ImageView[SIM_SLOTS];
    private final CellSignalState[] mInfos =
            new CellSignalState[]{new CellSignalState(), new CellSignalState()};

    private final int mColorForeground;
    private final CellSignalState mInfo = new CellSignalState();
    private OnClickListener mExpandClickListener;
    private CarrierTextController mCarrierTextController;

    @Inject
    public QSFooterImpl(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
@@ -134,10 +149,20 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
        mSettingsContainer = findViewById(R.id.settings_button_container);
        mSettingsButton.setOnClickListener(this);

        mMobileGroup = findViewById(R.id.mobile_combo);
        mMobileSignal = findViewById(R.id.mobile_signal);
        mMobileRoaming = findViewById(R.id.mobile_roaming);
        mCarrierText = findViewById(R.id.qs_carrier_text);
        mMobileFooter = findViewById(R.id.qs_mobile);
        mCarrierGroups[0] = findViewById(R.id.carrier1);
        mCarrierGroups[1] = findViewById(R.id.carrier2);

        for (int i = 0; i < SIM_SLOTS; i++) {
            mMobileGroups[i] = mCarrierGroups[i].findViewById(R.id.mobile_combo);
            mMobileSignals[i] = mCarrierGroups[i].findViewById(R.id.mobile_signal);
            mMobileRoamings[i] = mCarrierGroups[i].findViewById(R.id.mobile_roaming);
            mCarrierTexts[i] = mCarrierGroups[i].findViewById(R.id.qs_carrier_text);
        }
        mCarrierDivider = findViewById(R.id.qs_carrier_divider);
        CharSequence separator = mContext.getString(
                com.android.internal.R.string.kg_text_message_separator);
        mCarrierTextController = new CarrierTextController(mContext, separator, false, false);

        mMultiUserSwitch = findViewById(R.id.multi_user_switch);
        mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
@@ -204,8 +229,8 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
    private TouchAnimator createFooterAnimator() {
        return new TouchAnimator.Builder()
                .addFloat(mDivider, "alpha", 0, 1)
                .addFloat(mCarrierText, "alpha", 0, 0, 1)
                .addFloat(mMobileGroup, "alpha", 0, 1)
                .addFloat(mMobileFooter, "alpha", 0, 0, 1)
                .addFloat(mCarrierDivider, "alpha", 0, 1)
                .addFloat(mActionsContainer, "alpha", 0, 1)
                .addFloat(mDragHandle, "alpha", 1, 0, 0)
                .addFloat(mPageIndicator, "alpha", 0, 1)
@@ -332,10 +357,12 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
                mNetworkController.addEmergencyListener(this);
                mNetworkController.addCallback(this);
            }
            mCarrierTextController.setListening(this);
        } else {
            mUserInfoController.removeCallback(this);
            mNetworkController.removeEmergencyListener(this);
            mNetworkController.removeCallback(this);
            mCarrierTextController.setListening(null);
        }
    }

@@ -358,7 +385,8 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
        if (v == mSettingsButton) {
            if (!mDeviceProvisionedController.isCurrentUserSetup()) {
                // If user isn't setup just unlock the device and dump them back at SUW.
                mActivityStarter.postQSRunnableDismissingKeyguard(() -> { });
                mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
                });
                return;
            }
            MetricsLogger.action(mContext,
@@ -415,32 +443,64 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
    }

    private void handleUpdateState() {
        mMobileGroup.setVisibility(mInfo.visible ? View.VISIBLE : View.GONE);
        if (mInfo.visible) {
            mMobileRoaming.setVisibility(mInfo.roaming ? View.VISIBLE : View.GONE);
            mMobileRoaming.setImageTintList(ColorStateList.valueOf(mColorForeground));
        for (int i = 0; i < SIM_SLOTS; i++) {
            mMobileGroups[i].setVisibility(mInfos[i].visible ? View.VISIBLE : View.GONE);
            if (mInfos[i].visible) {
                mMobileRoamings[i].setVisibility(mInfos[i].roaming ? View.VISIBLE : View.GONE);
                mMobileRoamings[i].setImageTintList(ColorStateList.valueOf(mColorForeground));
                SignalDrawable d = new SignalDrawable(mContext);
                d.setDarkIntensity(QuickStatusBarHeader.getColorIntensity(mColorForeground));
            mMobileSignal.setImageDrawable(d);
            mMobileSignal.setImageLevel(mInfo.mobileSignalIconId);
                mMobileSignals[i].setImageDrawable(d);
                mMobileSignals[i].setImageLevel(mInfos[i].mobileSignalIconId);

                StringBuilder contentDescription = new StringBuilder();
            if (mInfo.contentDescription != null) {
                contentDescription.append(mInfo.contentDescription).append(", ");
                if (mInfos[i].contentDescription != null) {
                    contentDescription.append(mInfos[i].contentDescription).append(", ");
                }
            if (mInfo.roaming) {
                if (mInfos[i].roaming) {
                    contentDescription
                            .append(mContext.getString(R.string.data_connection_roaming))
                            .append(", ");
                }
                // TODO: show mobile data off/no internet text for 5 seconds before carrier text
            if (TextUtils.equals(mInfo.typeContentDescription,
                if (TextUtils.equals(mInfos[i].typeContentDescription,
                        mContext.getString(R.string.data_connection_no_internet))
                || TextUtils.equals(mInfo.typeContentDescription,
                        || TextUtils.equals(mInfos[i].typeContentDescription,
                        mContext.getString(R.string.cell_data_off_content_description))) {
                contentDescription.append(mInfo.typeContentDescription);
                    contentDescription.append(mInfos[i].typeContentDescription);
                }
                mMobileSignals[i].setContentDescription(contentDescription);
            }
        }
            mMobileSignal.setContentDescription(contentDescription);
        mCarrierDivider.setVisibility(
                mInfos[0].visible && mInfos[1].visible ? View.VISIBLE : View.GONE);
    }

    @Override
    public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
        if (info.anySimReady) {
            boolean[] slotSeen = new boolean[SIM_SLOTS];
            for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) {
                int slot = SubscriptionManager.getSlotIndex(info.subscriptionIds[i]);
                mInfos[slot].visible = true;
                slotSeen[slot] = true;
                mCarrierTexts[slot].setText(info.listOfCarriers[i].toString().trim());
                mCarrierGroups[slot].setVisibility(View.VISIBLE);
            }
            for (int i = 0; i < SIM_SLOTS; i++) {
                if (!slotSeen[i]) {
                    mInfos[i].visible = false;
                    mCarrierGroups[i].setVisibility(View.GONE);
                }
            }
            handleUpdateState();
        } else {
            mInfos[0].visible = false;
            mInfos[1].visible = false;
            mCarrierTexts[0].setText(info.carrierText);
            mCarrierGroups[0].setVisibility(View.VISIBLE);
            mCarrierGroups[1].setVisibility(View.GONE);
            handleUpdateState();
        }
    }

@@ -450,18 +510,23 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
            int qsType, boolean activityIn, boolean activityOut,
            String typeContentDescription,
            String description, boolean isWide, int subId, boolean roaming) {
        mInfo.visible = statusIcon.visible;
        mInfo.mobileSignalIconId = statusIcon.icon;
        mInfo.contentDescription = statusIcon.contentDescription;
        mInfo.typeContentDescription = typeContentDescription;
        mInfo.roaming = roaming;
        int slotIndex = SubscriptionManager.getSlotIndex(subId);
        if (slotIndex >= SIM_SLOTS) {
            Log.e(TAG, "setMobileDataIndicators - slot: " + slotIndex);
        }
        mInfos[slotIndex].visible = statusIcon.visible;
        mInfos[slotIndex].mobileSignalIconId = statusIcon.icon;
        mInfos[slotIndex].contentDescription = statusIcon.contentDescription;
        mInfos[slotIndex].typeContentDescription = typeContentDescription;
        mInfos[slotIndex].roaming = roaming;
        handleUpdateState();
    }

    @Override
    public void setNoSims(boolean hasNoSims, boolean simDetected) {
        if (hasNoSims) {
            mInfo.visible = false;
            mInfos[0].visible = false;
            mInfos[1].visible = false;
        }
        handleUpdateState();
    }
@@ -473,4 +538,38 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
        String typeContentDescription;
        boolean roaming;
    }


    /**
     * TextView that changes its ellipsize value with its visibility.
     */
    public static class QSCarrierText extends TextView {
        public QSCarrierText(Context context) {
            super(context);
        }

        public QSCarrierText(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        public QSCarrierText(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }

        public QSCarrierText(Context context, AttributeSet attrs, int defStyleAttr,
                int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
        }

        @Override
        protected void onVisibilityChanged(View changedView, int visibility) {
            super.onVisibilityChanged(changedView, visibility);
            // Only show marquee when visible
            if (visibility == VISIBLE) {
                setEllipsize(TextUtils.TruncateAt.MARQUEE);
            } else {
                setEllipsize(TextUtils.TruncateAt.END);
            }
        }
    }
}