OSDN Git Service

resolve merge conflicts of be23ef368f1a to oc-dev-plus-aosp
authorJason Monk <jmonk@google.com>
Tue, 6 Jun 2017 20:38:00 +0000 (16:38 -0400)
committerJason Monk <jmonk@google.com>
Tue, 6 Jun 2017 20:38:00 +0000 (16:38 -0400)
Test: I solemnly swear I tested this conflict resolution.
Change-Id: I5364727a21f62fc479c9bca6ad7ab2c3b62d1d1b

1  2 
packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java

index d12b16b,0000000..672f2c2
mode 100644,000000..100644
--- /dev/null
@@@ -1,518 -1,0 +1,520 @@@
-         setListening(false);
 +/*
 + * Copyright (C) 2017 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.
 + */
 +
 +package com.android.systemui.qs.tileimpl;
 +
 +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_CLICK;
 +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_LONG_PRESS;
 +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_SECONDARY_CLICK;
 +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_POSITION;
 +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_VALUE;
 +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
 +import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 +
 +import android.R.attr;
 +import android.app.ActivityManager;
 +import android.content.Context;
 +import android.content.Intent;
 +import android.graphics.drawable.Drawable;
 +import android.metrics.LogMaker;
 +import android.os.Handler;
 +import android.os.Looper;
 +import android.os.Message;
 +import android.service.quicksettings.Tile;
 +import android.util.ArraySet;
 +import android.util.Log;
 +import android.util.SparseArray;
 +
 +import com.android.internal.logging.MetricsLogger;
 +import com.android.settingslib.RestrictedLockUtils;
 +import com.android.settingslib.Utils;
 +import com.android.systemui.Dependency;
 +import com.android.systemui.plugins.ActivityStarter;
 +import com.android.systemui.plugins.qs.DetailAdapter;
 +import com.android.systemui.plugins.qs.QSIconView;
 +import com.android.systemui.plugins.qs.QSTile;
 +import com.android.systemui.plugins.qs.QSTile.State;
 +import com.android.systemui.qs.QSHost;
 +
 +import java.util.ArrayList;
 +
 +/**
 + * Base quick-settings tile, extend this to create a new tile.
 + *
 + * State management done on a looper provided by the host.  Tiles should update state in
 + * handleUpdateState.  Callbacks affecting state should use refreshState to trigger another
 + * state update pass on tile looper.
 + */
 +public abstract class QSTileImpl<TState extends State> implements QSTile {
 +    protected final String TAG = "Tile." + getClass().getSimpleName();
 +    protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG);
 +
 +    protected final QSHost mHost;
 +    protected final Context mContext;
 +    protected final H mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
 +    protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
 +    private final ArraySet<Object> mListeners = new ArraySet<>();
 +    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
 +
 +    private final ArrayList<Callback> mCallbacks = new ArrayList<>();
 +    protected TState mState = newTileState();
 +    private TState mTmpState = newTileState();
 +    private boolean mAnnounceNextStateChange;
 +
 +    private String mTileSpec;
 +    private EnforcedAdmin mEnforcedAdmin;
 +    private boolean mShowingDetail;
 +
 +    public abstract TState newTileState();
 +
 +    abstract protected void handleClick();
 +
 +    abstract protected void handleUpdateState(TState state, Object arg);
 +
 +    /**
 +     * Declare the category of this tile.
 +     *
 +     * Categories are defined in {@link com.android.internal.logging.nano.MetricsProto.MetricsEvent}
 +     * by editing frameworks/base/proto/src/metrics_constants.proto.
 +     */
 +    abstract public int getMetricsCategory();
 +
 +    protected QSTileImpl(QSHost host) {
 +        mHost = host;
 +        mContext = host.getContext();
 +    }
 +
 +    /**
 +     * Adds or removes a listening client for the tile. If the tile has one or more
 +     * listening client it will go into the listening state.
 +     */
 +    public void setListening(Object listener, boolean listening) {
 +        if (listening) {
 +            if (mListeners.add(listener) && mListeners.size() == 1) {
 +                if (DEBUG) Log.d(TAG, "setListening " + true);
 +                mHandler.obtainMessage(H.SET_LISTENING, 1, 0).sendToTarget();
 +            }
 +        } else {
 +            if (mListeners.remove(listener) && mListeners.size() == 0) {
 +                if (DEBUG) Log.d(TAG, "setListening " + false);
 +                mHandler.obtainMessage(H.SET_LISTENING, 0, 0).sendToTarget();
 +            }
 +        }
 +    }
 +
 +    public String getTileSpec() {
 +        return mTileSpec;
 +    }
 +
 +    public void setTileSpec(String tileSpec) {
 +        mTileSpec = tileSpec;
 +    }
 +
 +    public QSHost getHost() {
 +        return mHost;
 +    }
 +
 +    public QSIconView createTileView(Context context) {
 +        return new QSIconViewImpl(context);
 +    }
 +
 +    public DetailAdapter getDetailAdapter() {
 +        return null; // optional
 +    }
 +
 +    protected DetailAdapter createDetailAdapter() {
 +        throw new UnsupportedOperationException();
 +    }
 +
 +    /**
 +     * Is a startup check whether this device currently supports this tile.
 +     * Should not be used to conditionally hide tiles.  Only checked on tile
 +     * creation or whether should be shown in edit screen.
 +     */
 +    public boolean isAvailable() {
 +        return true;
 +    }
 +
 +    // safe to call from any thread
 +
 +    public void addCallback(Callback callback) {
 +        mHandler.obtainMessage(H.ADD_CALLBACK, callback).sendToTarget();
 +    }
 +
 +    public void removeCallback(Callback callback) {
 +        mHandler.obtainMessage(H.REMOVE_CALLBACK, callback).sendToTarget();
 +    }
 +
 +    public void removeCallbacks() {
 +        mHandler.sendEmptyMessage(H.REMOVE_CALLBACKS);
 +    }
 +
 +    public void click() {
 +        mMetricsLogger.write(populate(new LogMaker(ACTION_QS_CLICK).setType(TYPE_ACTION)));
 +        mHandler.sendEmptyMessage(H.CLICK);
 +    }
 +
 +    public void secondaryClick() {
 +        mMetricsLogger.write(populate(new LogMaker(ACTION_QS_SECONDARY_CLICK).setType(TYPE_ACTION)));
 +        mHandler.sendEmptyMessage(H.SECONDARY_CLICK);
 +    }
 +
 +    public void longClick() {
 +        mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)));
 +        mHandler.sendEmptyMessage(H.LONG_CLICK);
 +    }
 +
 +    public LogMaker populate(LogMaker logMaker) {
 +        if (mState instanceof BooleanState) {
 +            logMaker.addTaggedData(FIELD_QS_VALUE, ((BooleanState) mState).value ? 1 : 0);
 +        }
 +        return logMaker.setSubtype(getMetricsCategory())
 +                .addTaggedData(FIELD_QS_POSITION, mHost.indexOf(mTileSpec));
 +    }
 +
 +    public void showDetail(boolean show) {
 +        mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0).sendToTarget();
 +    }
 +
 +    public void refreshState() {
 +        refreshState(null);
 +    }
 +
 +    protected final void refreshState(Object arg) {
 +        mHandler.obtainMessage(H.REFRESH_STATE, arg).sendToTarget();
 +    }
 +
 +    public void clearState() {
 +        mHandler.sendEmptyMessage(H.CLEAR_STATE);
 +    }
 +
 +    public void userSwitch(int newUserId) {
 +        mHandler.obtainMessage(H.USER_SWITCH, newUserId, 0).sendToTarget();
 +    }
 +
 +    public void fireToggleStateChanged(boolean state) {
 +        mHandler.obtainMessage(H.TOGGLE_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget();
 +    }
 +
 +    public void fireScanStateChanged(boolean state) {
 +        mHandler.obtainMessage(H.SCAN_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget();
 +    }
 +
 +    public void destroy() {
 +        mHandler.sendEmptyMessage(H.DESTROY);
 +    }
 +
 +    public TState getState() {
 +        return mState;
 +    }
 +
 +    public void setDetailListening(boolean listening) {
 +        // optional
 +    }
 +
 +    // call only on tile worker looper
 +
 +    private void handleAddCallback(Callback callback) {
 +        mCallbacks.add(callback);
 +        callback.onStateChanged(mState);
 +    }
 +
 +    private void handleRemoveCallback(Callback callback) {
 +        mCallbacks.remove(callback);
 +    }
 +
 +    private void handleRemoveCallbacks() {
 +        mCallbacks.clear();
 +    }
 +
 +    protected void handleSecondaryClick() {
 +        // Default to normal click.
 +        handleClick();
 +    }
 +
 +    protected void handleLongClick() {
 +        Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
 +                getLongClickIntent(), 0);
 +    }
 +
 +    public abstract Intent getLongClickIntent();
 +
 +    protected void handleClearState() {
 +        mTmpState = newTileState();
 +        mState = newTileState();
 +    }
 +
 +    protected void handleRefreshState(Object arg) {
 +        handleUpdateState(mTmpState, arg);
 +        final boolean changed = mTmpState.copyTo(mState);
 +        if (changed) {
 +            handleStateChanged();
 +        }
 +    }
 +
 +    private void handleStateChanged() {
 +        boolean delayAnnouncement = shouldAnnouncementBeDelayed();
 +        if (mCallbacks.size() != 0) {
 +            for (int i = 0; i < mCallbacks.size(); i++) {
 +                mCallbacks.get(i).onStateChanged(mState);
 +            }
 +            if (mAnnounceNextStateChange && !delayAnnouncement) {
 +                String announcement = composeChangeAnnouncement();
 +                if (announcement != null) {
 +                    mCallbacks.get(0).onAnnouncementRequested(announcement);
 +                }
 +            }
 +        }
 +        mAnnounceNextStateChange = mAnnounceNextStateChange && delayAnnouncement;
 +    }
 +
 +    protected boolean shouldAnnouncementBeDelayed() {
 +        return false;
 +    }
 +
 +    protected String composeChangeAnnouncement() {
 +        return null;
 +    }
 +
 +    private void handleShowDetail(boolean show) {
 +        mShowingDetail = show;
 +        for (int i = 0; i < mCallbacks.size(); i++) {
 +            mCallbacks.get(i).onShowDetail(show);
 +        }
 +    }
 +
 +    protected boolean isShowingDetail() {
 +        return mShowingDetail;
 +    }
 +
 +    private void handleToggleStateChanged(boolean state) {
 +        for (int i = 0; i < mCallbacks.size(); i++) {
 +            mCallbacks.get(i).onToggleStateChanged(state);
 +        }
 +    }
 +
 +    private void handleScanStateChanged(boolean state) {
 +        for (int i = 0; i < mCallbacks.size(); i++) {
 +            mCallbacks.get(i).onScanStateChanged(state);
 +        }
 +    }
 +
 +    protected void handleUserSwitch(int newUserId) {
 +        handleRefreshState(null);
 +    }
 +
 +    protected abstract void setListening(boolean listening);
 +
 +    protected void handleDestroy() {
++        if (mListeners.size() != 0) {
++            setListening(false);
++        }
 +        mCallbacks.clear();
 +    }
 +
 +    protected void checkIfRestrictionEnforcedByAdminOnly(State state, String userRestriction) {
 +        EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
 +                userRestriction, ActivityManager.getCurrentUser());
 +        if (admin != null && !RestrictedLockUtils.hasBaseUserRestriction(mContext,
 +                userRestriction, ActivityManager.getCurrentUser())) {
 +            state.disabledByPolicy = true;
 +            mEnforcedAdmin = admin;
 +        } else {
 +            state.disabledByPolicy = false;
 +            mEnforcedAdmin = null;
 +        }
 +    }
 +
 +    public abstract CharSequence getTileLabel();
 +
 +    public static int getColorForState(Context context, int state) {
 +        switch (state) {
 +            case Tile.STATE_UNAVAILABLE:
 +                return Utils.getDisabled(context,
 +                        Utils.getColorAttr(context, android.R.attr.colorForeground));
 +            case Tile.STATE_INACTIVE:
 +                return Utils.getColorAttr(context, android.R.attr.textColorHint);
 +            case Tile.STATE_ACTIVE:
 +                return Utils.getColorAttr(context, android.R.attr.textColorPrimary);
 +            default:
 +                Log.e("QSTile", "Invalid state " + state);
 +                return 0;
 +        }
 +    }
 +
 +    protected final class H extends Handler {
 +        private static final int ADD_CALLBACK = 1;
 +        private static final int CLICK = 2;
 +        private static final int SECONDARY_CLICK = 3;
 +        private static final int LONG_CLICK = 4;
 +        private static final int REFRESH_STATE = 5;
 +        private static final int SHOW_DETAIL = 6;
 +        private static final int USER_SWITCH = 7;
 +        private static final int TOGGLE_STATE_CHANGED = 8;
 +        private static final int SCAN_STATE_CHANGED = 9;
 +        private static final int DESTROY = 10;
 +        private static final int CLEAR_STATE = 11;
 +        private static final int REMOVE_CALLBACKS = 12;
 +        private static final int REMOVE_CALLBACK = 13;
 +        private static final int SET_LISTENING = 14;
 +
 +        private H(Looper looper) {
 +            super(looper);
 +        }
 +
 +        @Override
 +        public void handleMessage(Message msg) {
 +            String name = null;
 +            try {
 +                if (msg.what == ADD_CALLBACK) {
 +                    name = "handleAddCallback";
 +                    handleAddCallback((QSTile.Callback) msg.obj);
 +                } else if (msg.what == REMOVE_CALLBACKS) {
 +                    name = "handleRemoveCallbacks";
 +                    handleRemoveCallbacks();
 +                } else if (msg.what == REMOVE_CALLBACK) {
 +                    name = "handleRemoveCallback";
 +                    handleRemoveCallback((QSTile.Callback) msg.obj);
 +                } else if (msg.what == CLICK) {
 +                    name = "handleClick";
 +                    if (mState.disabledByPolicy) {
 +                        Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
 +                                mContext, mEnforcedAdmin);
 +                        Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
 +                                intent, 0);
 +                    } else {
 +                        handleClick();
 +                    }
 +                } else if (msg.what == SECONDARY_CLICK) {
 +                    name = "handleSecondaryClick";
 +                    handleSecondaryClick();
 +                } else if (msg.what == LONG_CLICK) {
 +                    name = "handleLongClick";
 +                    handleLongClick();
 +                } else if (msg.what == REFRESH_STATE) {
 +                    name = "handleRefreshState";
 +                    handleRefreshState(msg.obj);
 +                } else if (msg.what == SHOW_DETAIL) {
 +                    name = "handleShowDetail";
 +                    handleShowDetail(msg.arg1 != 0);
 +                } else if (msg.what == USER_SWITCH) {
 +                    name = "handleUserSwitch";
 +                    handleUserSwitch(msg.arg1);
 +                } else if (msg.what == TOGGLE_STATE_CHANGED) {
 +                    name = "handleToggleStateChanged";
 +                    handleToggleStateChanged(msg.arg1 != 0);
 +                } else if (msg.what == SCAN_STATE_CHANGED) {
 +                    name = "handleScanStateChanged";
 +                    handleScanStateChanged(msg.arg1 != 0);
 +                } else if (msg.what == DESTROY) {
 +                    name = "handleDestroy";
 +                    handleDestroy();
 +                } else if (msg.what == CLEAR_STATE) {
 +                    name = "handleClearState";
 +                    handleClearState();
 +                } else if (msg.what == SET_LISTENING) {
 +                    name = "setListening";
 +                    setListening(msg.arg1 != 0);
 +                } else {
 +                    throw new IllegalArgumentException("Unknown msg: " + msg.what);
 +                }
 +            } catch (Throwable t) {
 +                final String error = "Error in " + name;
 +                Log.w(TAG, error, t);
 +                mHost.warn(error, t);
 +            }
 +        }
 +    }
 +
 +    public static class DrawableIcon extends Icon {
 +        protected final Drawable mDrawable;
 +
 +        public DrawableIcon(Drawable drawable) {
 +            mDrawable = drawable;
 +        }
 +
 +        @Override
 +        public Drawable getDrawable(Context context) {
 +            return mDrawable;
 +        }
 +    }
 +
 +    public static class DrawableIconWithRes extends DrawableIcon {
 +        private final int mId;
 +
 +        public DrawableIconWithRes(Drawable drawable, int id) {
 +            super(drawable);
 +            mId = id;
 +        }
 +
 +        @Override
 +        public boolean equals(Object o) {
 +            return o instanceof DrawableIconWithRes && ((DrawableIconWithRes) o).mId == mId;
 +        }
 +    }
 +
 +    public static class ResourceIcon extends Icon {
 +        private static final SparseArray<Icon> ICONS = new SparseArray<Icon>();
 +
 +        protected final int mResId;
 +
 +        private ResourceIcon(int resId) {
 +            mResId = resId;
 +        }
 +
 +        public static Icon get(int resId) {
 +            Icon icon = ICONS.get(resId);
 +            if (icon == null) {
 +                icon = new ResourceIcon(resId);
 +                ICONS.put(resId, icon);
 +            }
 +            return icon;
 +        }
 +
 +        @Override
 +        public Drawable getDrawable(Context context) {
 +            return context.getDrawable(mResId);
 +        }
 +
 +        @Override
 +        public Drawable getInvisibleDrawable(Context context) {
 +            return context.getDrawable(mResId);
 +        }
 +
 +        @Override
 +        public boolean equals(Object o) {
 +            return o instanceof ResourceIcon && ((ResourceIcon) o).mResId == mResId;
 +        }
 +
 +        @Override
 +        public String toString() {
 +            return String.format("ResourceIcon[resId=0x%08x]", mResId);
 +        }
 +    }
 +
 +    protected class AnimationIcon extends ResourceIcon {
 +        private final int mAnimatedResId;
 +
 +        public AnimationIcon(int resId, int staticResId) {
 +            super(staticResId);
 +            mAnimatedResId = resId;
 +        }
 +
 +        @Override
 +        public Drawable getDrawable(Context context) {
 +            // workaround: get a clean state for every new AVD
 +            return context.getDrawable(mAnimatedResId).getConstantState().newDrawable();
 +        }
 +    }
 +}