Home | History | Annotate | Download | only in qs
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.systemui.qs;
     17 
     18 import android.content.Context;
     19 import android.content.res.TypedArray;
     20 import android.graphics.drawable.Drawable;
     21 import android.graphics.drawable.RippleDrawable;
     22 import android.os.Handler;
     23 import android.os.Looper;
     24 import android.os.Message;
     25 import android.text.TextUtils;
     26 import android.view.View;
     27 import android.view.accessibility.AccessibilityEvent;
     28 import android.view.accessibility.AccessibilityNodeInfo;
     29 import android.widget.LinearLayout;
     30 import android.widget.Switch;
     31 
     32 import com.android.systemui.R;
     33 
     34 public class QSTileBaseView extends LinearLayout {
     35 
     36     private final H mHandler = new H();
     37     private QSIconView mIcon;
     38     private RippleDrawable mRipple;
     39     private Drawable mTileBackground;
     40     private String mAccessibilityClass;
     41     private boolean mTileState;
     42     private boolean mCollapsedView;
     43 
     44     public QSTileBaseView(Context context, QSIconView icon) {
     45         this(context, icon, false);
     46     }
     47 
     48     public QSTileBaseView(Context context, QSIconView icon, boolean collapsedView) {
     49         super(context);
     50         mIcon = icon;
     51         addView(mIcon);
     52 
     53         mTileBackground = newTileBackground();
     54         if (mTileBackground instanceof RippleDrawable) {
     55             setRipple((RippleDrawable) mTileBackground);
     56         }
     57         setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
     58         setBackground(mTileBackground);
     59 
     60         // Default to Quick Tile padding, and QSTileView will specify its own padding.
     61         int padding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
     62         setPadding(0, padding, 0, padding);
     63         setClipChildren(false);
     64         setClipToPadding(false);
     65         mCollapsedView = collapsedView;
     66         setFocusable(true);
     67     }
     68 
     69     private Drawable newTileBackground() {
     70         final int[] attrs = new int[] { android.R.attr.selectableItemBackgroundBorderless };
     71         final TypedArray ta = mContext.obtainStyledAttributes(attrs);
     72         final Drawable d = ta.getDrawable(0);
     73         ta.recycle();
     74         return d;
     75     }
     76 
     77     private void setRipple(RippleDrawable tileBackground) {
     78         mRipple = tileBackground;
     79         if (getWidth() != 0) {
     80             updateRippleSize(getWidth(), getHeight());
     81         }
     82     }
     83 
     84     private void updateRippleSize(int width, int height) {
     85         // center the touch feedback on the center of the icon, and dial it down a bit
     86         final int cx = width / 2;
     87         final int cy = height / 2;
     88         final int rad = (int)(mIcon.getHeight() * .85f);
     89         mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
     90     }
     91 
     92     public void init(OnClickListener click, OnLongClickListener longClick) {
     93         setClickable(true);
     94         setOnClickListener(click);
     95         setOnLongClickListener(longClick);
     96     }
     97 
     98     @Override
     99     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    100         super.onLayout(changed, l, t, r, b);
    101         final int w = getMeasuredWidth();
    102         final int h = getMeasuredHeight();
    103 
    104         if (mRipple != null) {
    105             updateRippleSize(w, h);
    106         }
    107     }
    108 
    109     @Override
    110     public boolean hasOverlappingRendering() {
    111         // Avoid layers for this layout - we don't need them.
    112         return false;
    113     }
    114 
    115     /**
    116      * Update the accessibility order for this view.
    117      *
    118      * @param previousView the view which should be before this one
    119      * @return the last view in this view which is accessible
    120      */
    121     public View updateAccessibilityOrder(View previousView) {
    122         setAccessibilityTraversalAfter(previousView.getId());
    123         return this;
    124     }
    125 
    126     public void onStateChanged(QSTile.State state) {
    127         mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget();
    128     }
    129 
    130     protected void handleStateChanged(QSTile.State state) {
    131         mIcon.setIcon(state);
    132         if (mCollapsedView && !TextUtils.isEmpty(state.minimalContentDescription)) {
    133             setContentDescription(state.minimalContentDescription);
    134         } else {
    135             setContentDescription(state.contentDescription);
    136         }
    137         if (mCollapsedView) {
    138             mAccessibilityClass = state.minimalAccessibilityClassName;
    139         } else {
    140             mAccessibilityClass = state.expandedAccessibilityClassName;
    141         }
    142         if (state instanceof QSTile.BooleanState) {
    143             mTileState = ((QSTile.BooleanState) state).value;
    144         }
    145     }
    146 
    147     public QSIconView getIcon() {
    148         return mIcon;
    149     }
    150 
    151     @Override
    152     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
    153         super.onInitializeAccessibilityEvent(event);
    154         if (!TextUtils.isEmpty(mAccessibilityClass)) {
    155             event.setClassName(mAccessibilityClass);
    156             if (Switch.class.getName().equals(mAccessibilityClass)) {
    157                 String label = getResources()
    158                         .getString(!mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
    159                 event.setContentDescription(label);
    160                 event.setChecked(!mTileState);
    161             }
    162         }
    163     }
    164 
    165     @Override
    166     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
    167         super.onInitializeAccessibilityNodeInfo(info);
    168         if (!TextUtils.isEmpty(mAccessibilityClass)) {
    169             info.setClassName(mAccessibilityClass);
    170             if (Switch.class.getName().equals(mAccessibilityClass)) {
    171                 String label = getResources()
    172                         .getString(mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
    173                 info.setText(label);
    174                 info.setChecked(mTileState);
    175                 info.setCheckable(true);
    176             }
    177         }
    178     }
    179 
    180     private class H extends Handler {
    181         private static final int STATE_CHANGED = 1;
    182         public H() {
    183             super(Looper.getMainLooper());
    184         }
    185 
    186         @Override
    187         public void handleMessage(Message msg) {
    188             if (msg.what == STATE_CHANGED) {
    189                 handleStateChanged((QSTile.State) msg.obj);
    190             }
    191         }
    192     }
    193 }
    194