Home | History | Annotate | Download | only in wear
      1 package com.android.packageinstaller.permission.ui.wear;
      2 
      3 import android.animation.ObjectAnimator;
      4 import android.animation.PropertyValuesHolder;
      5 import android.content.Context;
      6 import android.graphics.drawable.Drawable;
      7 import android.graphics.drawable.Icon;
      8 import android.os.Handler;
      9 import android.os.Looper;
     10 import android.os.Message;
     11 import android.text.TextUtils;
     12 import android.util.Log;
     13 import android.view.LayoutInflater;
     14 import android.view.View;
     15 import android.view.ViewGroup;
     16 import android.view.ViewTreeObserver;
     17 import android.view.animation.AnimationUtils;
     18 import android.view.animation.Interpolator;
     19 import android.widget.Button;
     20 import android.widget.ImageView;
     21 import android.widget.ScrollView;
     22 import android.widget.TextView;
     23 
     24 import com.android.packageinstaller.R;
     25 
     26 public abstract class ConfirmationViewHandler implements
     27         Handler.Callback,
     28         View.OnClickListener,
     29         ViewTreeObserver.OnScrollChangedListener,
     30         ViewTreeObserver.OnGlobalLayoutListener {
     31     private static final String TAG = "ConfirmationViewHandler";
     32 
     33     public static final int MODE_HORIZONTAL_BUTTONS = 0;
     34     public static final int MODE_VERTICAL_BUTTONS = 1;
     35 
     36     private static final int MSG_SHOW_BUTTON_BAR = 1001;
     37     private static final int MSG_HIDE_BUTTON_BAR = 1002;
     38     private static final long HIDE_ANIM_DURATION = 500;
     39 
     40     private View mRoot;
     41     private TextView mCurrentPageText;
     42     private ImageView mIcon;
     43     private TextView mMessage;
     44     private ScrollView mScrollingContainer;
     45     private ViewGroup mContent;
     46     private ViewGroup mHorizontalButtonBar;
     47     private ViewGroup mVerticalButtonBar;
     48     private Button mVerticalButton1;
     49     private Button mVerticalButton2;
     50     private Button mVerticalButton3;
     51     private View mButtonBarContainer;
     52 
     53     private Context mContext;
     54 
     55     private Handler mHideHandler;
     56     private Interpolator mInterpolator;
     57     private float mButtonBarFloatingHeight;
     58     private ObjectAnimator mButtonBarAnimator;
     59     private float mCurrentTranslation;
     60     private boolean mHiddenBefore;
     61 
     62     // TODO: Move these into a builder
     63     /** In the 2 button layout, this is allow button */
     64     public abstract void onButton1();
     65     /** In the 2 button layout, this is deny button */
     66     public abstract void onButton2();
     67     public abstract void onButton3();
     68     public abstract CharSequence getVerticalButton1Text();
     69     public abstract CharSequence getVerticalButton2Text();
     70     public abstract CharSequence getVerticalButton3Text();
     71     public abstract Drawable getVerticalButton1Icon();
     72     public abstract Drawable getVerticalButton2Icon();
     73     public abstract Drawable getVerticalButton3Icon();
     74     public abstract CharSequence getCurrentPageText();
     75     public abstract Icon getPermissionIcon();
     76     public abstract CharSequence getMessage();
     77 
     78     public ConfirmationViewHandler(Context context) {
     79         mContext = context;
     80     }
     81 
     82     public View createView() {
     83         mRoot = LayoutInflater.from(mContext).inflate(R.layout.confirmation_dialog, null);
     84 
     85         mMessage = (TextView) mRoot.findViewById(R.id.message);
     86         mCurrentPageText = (TextView) mRoot.findViewById(R.id.current_page_text);
     87         mIcon = (ImageView) mRoot.findViewById(R.id.icon);
     88         mButtonBarContainer = mRoot.findViewById(R.id.button_bar_container);
     89         mContent = (ViewGroup) mRoot.findViewById(R.id.content);
     90         mScrollingContainer = (ScrollView) mRoot.findViewById(R.id.scrolling_container);
     91         mHorizontalButtonBar = (ViewGroup) mRoot.findViewById(R.id.horizontal_button_bar);
     92         mVerticalButtonBar = (ViewGroup) mRoot.findViewById(R.id.vertical_button_bar);
     93 
     94         Button horizontalAllow = (Button) mRoot.findViewById(R.id.permission_allow_button);
     95         Button horizontalDeny = (Button) mRoot.findViewById(R.id.permission_deny_button);
     96         horizontalAllow.setOnClickListener(this);
     97         horizontalDeny.setOnClickListener(this);
     98 
     99         mVerticalButton1 = (Button) mRoot.findViewById(R.id.vertical_button1);
    100         mVerticalButton2 = (Button) mRoot.findViewById(R.id.vertical_button2);
    101         mVerticalButton3 = (Button) mRoot.findViewById(R.id.vertical_button3);
    102         mVerticalButton1.setOnClickListener(this);
    103         mVerticalButton2.setOnClickListener(this);
    104         mVerticalButton3.setOnClickListener(this);
    105 
    106         mInterpolator = AnimationUtils.loadInterpolator(mContext,
    107                 android.R.interpolator.fast_out_slow_in);
    108         mButtonBarFloatingHeight = mContext.getResources().getDimension(
    109                 R.dimen.conf_diag_floating_height);
    110         mHideHandler = new Handler(Looper.getMainLooper(), this);
    111 
    112         mScrollingContainer.getViewTreeObserver().addOnScrollChangedListener(this);
    113         mRoot.getViewTreeObserver().addOnGlobalLayoutListener(this);
    114 
    115         return mRoot;
    116     }
    117 
    118     /**
    119      * Child class should override this for other modes.  Call invalidate() to update the UI to the
    120      * new button mode.
    121      * @return The current mode the layout should use for the buttons
    122      */
    123     public int getButtonBarMode() {
    124         return MODE_HORIZONTAL_BUTTONS;
    125     }
    126 
    127     public void invalidate() {
    128         CharSequence currentPageText = getCurrentPageText();
    129         if (!TextUtils.isEmpty(currentPageText)) {
    130             mCurrentPageText.setText(currentPageText);
    131             mCurrentPageText.setVisibility(View.VISIBLE);
    132         } else {
    133             mCurrentPageText.setVisibility(View.GONE);
    134         }
    135 
    136         Icon icon = getPermissionIcon();
    137         if (icon != null) {
    138             mIcon.setImageIcon(icon);
    139             mIcon.setVisibility(View.VISIBLE);
    140         } else {
    141             mIcon.setVisibility(View.GONE);
    142         }
    143         mMessage.setText(getMessage());
    144 
    145         switch (getButtonBarMode()) {
    146             case MODE_HORIZONTAL_BUTTONS:
    147                 mHorizontalButtonBar.setVisibility(View.VISIBLE);
    148                 mVerticalButtonBar.setVisibility(View.GONE);
    149                 break;
    150             case MODE_VERTICAL_BUTTONS:
    151                 mHorizontalButtonBar.setVisibility(View.GONE);
    152                 mVerticalButtonBar.setVisibility(View.VISIBLE);
    153 
    154                 mVerticalButton1.setText(getVerticalButton1Text());
    155                 mVerticalButton2.setText(getVerticalButton2Text());
    156 
    157                 mVerticalButton1.setCompoundDrawablesWithIntrinsicBounds(
    158                         getVerticalButton1Icon(), null, null, null);
    159                 mVerticalButton2.setCompoundDrawablesWithIntrinsicBounds(
    160                         getVerticalButton2Icon(), null, null, null);
    161 
    162                 CharSequence verticalButton3Text = getVerticalButton3Text();
    163                 if (TextUtils.isEmpty(verticalButton3Text)) {
    164                     mVerticalButton3.setVisibility(View.GONE);
    165                 } else {
    166                     mVerticalButton3.setText(getVerticalButton3Text());
    167                     mVerticalButton3.setCompoundDrawablesWithIntrinsicBounds(
    168                             getVerticalButton3Icon(), null, null, null);
    169                 }
    170                 break;
    171         }
    172 
    173         mScrollingContainer.scrollTo(0, 0);
    174 
    175         mHideHandler.removeMessages(MSG_HIDE_BUTTON_BAR);
    176         mHideHandler.removeMessages(MSG_SHOW_BUTTON_BAR);
    177     }
    178 
    179     @Override
    180     public void onGlobalLayout() {
    181         if (Log.isLoggable(TAG, Log.DEBUG)) {
    182             Log.d(TAG, "onGlobalLayout");
    183             Log.d(TAG, "    contentHeight: " + mContent.getHeight());
    184         }
    185 
    186         if (mButtonBarAnimator != null) {
    187             mButtonBarAnimator.cancel();
    188         }
    189 
    190         // In order to fake the buttons peeking at the bottom, need to do set the
    191         // padding properly.
    192         if (mContent.getPaddingBottom() != mButtonBarContainer.getHeight()) {
    193             mContent.setPadding(mContent.getPaddingLeft(), mContent.getPaddingTop(),
    194                     mContent.getPaddingRight(), mButtonBarContainer.getHeight());
    195             if (Log.isLoggable(TAG, Log.DEBUG)) {
    196                 Log.d(TAG, "    set mContent.PaddingBottom: " + mButtonBarContainer.getHeight());
    197             }
    198         }
    199 
    200         mButtonBarContainer.setTranslationY(mButtonBarContainer.getHeight());
    201 
    202         // Give everything a chance to render
    203         mHideHandler.removeMessages(MSG_HIDE_BUTTON_BAR);
    204         mHideHandler.removeMessages(MSG_SHOW_BUTTON_BAR);
    205         mHideHandler.sendEmptyMessageDelayed(MSG_SHOW_BUTTON_BAR, 50);
    206     }
    207 
    208     @Override
    209     public void onClick(View v) {
    210         int id = v.getId();
    211         switch (id) {
    212             case R.id.permission_allow_button:
    213             case R.id.vertical_button1:
    214                 onButton1();
    215                 break;
    216             case R.id.permission_deny_button:
    217             case R.id.vertical_button2:
    218                 onButton2();
    219                 break;
    220             case R.id.vertical_button3:
    221                 onButton3();
    222                 break;
    223         }
    224     }
    225 
    226     @Override
    227     public boolean handleMessage (Message msg) {
    228         switch (msg.what) {
    229             case MSG_SHOW_BUTTON_BAR:
    230                 showButtonBar();
    231                 return true;
    232             case MSG_HIDE_BUTTON_BAR:
    233                 hideButtonBar();
    234                 return true;
    235         }
    236         return false;
    237     }
    238 
    239     @Override
    240     public void onScrollChanged () {
    241         if (Log.isLoggable(TAG, Log.DEBUG)) {
    242             Log.d(TAG, "onScrollChanged");
    243         }
    244         mHideHandler.removeMessages(MSG_HIDE_BUTTON_BAR);
    245         hideButtonBar();
    246     }
    247 
    248     private void showButtonBar() {
    249         if (Log.isLoggable(TAG, Log.DEBUG)) {
    250             Log.d(TAG, "showButtonBar");
    251         }
    252 
    253         // Setup Button animation.
    254         // pop the button bar back to full height, stop all animation
    255         if (mButtonBarAnimator != null) {
    256             mButtonBarAnimator.cancel();
    257         }
    258 
    259         // stop any calls to hide the button bar in the future
    260         mHideHandler.removeMessages(MSG_HIDE_BUTTON_BAR);
    261         mHiddenBefore = false;
    262 
    263         // Evaluate the max height the button bar can go
    264         final int screenHeight = mRoot.getHeight();
    265         final int halfScreenHeight = screenHeight / 2;
    266         final int buttonBarHeight = mButtonBarContainer.getHeight();
    267         final int contentHeight = mContent.getHeight() - buttonBarHeight;
    268         final int buttonBarMaxHeight =
    269                 Math.min(buttonBarHeight, halfScreenHeight);
    270 
    271         if (Log.isLoggable(TAG, Log.DEBUG)) {
    272             Log.d(TAG, "    screenHeight: " + screenHeight);
    273             Log.d(TAG, "    contentHeight: " + contentHeight);
    274             Log.d(TAG, "    buttonBarHeight: " + buttonBarHeight);
    275             Log.d(TAG, "    buttonBarMaxHeight: " + buttonBarMaxHeight);
    276         }
    277 
    278         mButtonBarContainer.setTranslationZ(mButtonBarFloatingHeight);
    279 
    280         // Only hide the button bar if it is occluding the content or the button bar is bigger than
    281         // half the screen
    282         if (contentHeight > (screenHeight - buttonBarHeight)
    283                 || buttonBarHeight > halfScreenHeight) {
    284             mHideHandler.sendEmptyMessageDelayed(MSG_HIDE_BUTTON_BAR, 3000);
    285         }
    286 
    287         generateButtonBarAnimator(buttonBarHeight,
    288                 buttonBarHeight - buttonBarMaxHeight, 0, mButtonBarFloatingHeight, 1000);
    289     }
    290 
    291     private void hideButtonBar() {
    292         if (Log.isLoggable(TAG, Log.DEBUG)) {
    293             Log.d(TAG, "hideButtonBar");
    294         }
    295 
    296         // The desired margin space between the button bar and the bottom of the dialog text
    297         final int topMargin = mContext.getResources().getDimensionPixelSize(
    298                 R.dimen.conf_diag_button_container_top_margin);
    299         final int contentHeight = mContent.getHeight() + topMargin;
    300         final int screenHeight = mRoot.getHeight();
    301         final int buttonBarHeight = mButtonBarContainer.getHeight();
    302 
    303         final int offset = screenHeight + buttonBarHeight
    304                 - contentHeight + Math.max(mScrollingContainer.getScrollY(), 0);
    305         final int translationY = (offset > 0 ?
    306                 mButtonBarContainer.getHeight() - offset : mButtonBarContainer.getHeight());
    307 
    308         if (Log.isLoggable(TAG, Log.DEBUG)) {
    309             Log.d(TAG, "    topMargin: " + topMargin);
    310             Log.d(TAG, "    contentHeight: " + contentHeight);
    311             Log.d(TAG, "    screenHeight: " + screenHeight);
    312             Log.d(TAG, "    offset: " + offset);
    313             Log.d(TAG, "    buttonBarHeight: " + buttonBarHeight);
    314             Log.d(TAG, "    mContent.getPaddingBottom(): " + mContent.getPaddingBottom());
    315             Log.d(TAG, "    mScrollingContainer.getScrollY(): " + mScrollingContainer.getScrollY());
    316             Log.d(TAG, "    translationY: " + translationY);
    317         }
    318 
    319         if (!mHiddenBefore || mButtonBarAnimator == null) {
    320             // Remove previous call to MSG_SHOW_BUTTON_BAR if the user scrolled or something before
    321             // the animation got a chance to play
    322             mHideHandler.removeMessages(MSG_SHOW_BUTTON_BAR);
    323 
    324             if(mButtonBarAnimator != null) {
    325                 mButtonBarAnimator.cancel(); // stop current animation if there is one playing
    326             }
    327 
    328             // hasn't hidden the bar yet, just hide now to the right height
    329             generateButtonBarAnimator(
    330                     mButtonBarContainer.getTranslationY(), translationY,
    331                     mButtonBarFloatingHeight, 0, HIDE_ANIM_DURATION);
    332         } else if (mButtonBarAnimator.isRunning()) {
    333             // we are animating the button bar closing, change to animate to the right place
    334             if (Math.abs(mCurrentTranslation - translationY) > 1e-2f) {
    335                 mButtonBarAnimator.cancel(); // stop current animation
    336 
    337                 if (Math.abs(mButtonBarContainer.getTranslationY() - translationY) > 1e-2f) {
    338                     long duration = Math.max((long) (
    339                             (float) HIDE_ANIM_DURATION
    340                                     * (translationY - mButtonBarContainer.getTranslationY())
    341                                     / mButtonBarContainer.getHeight()), 0);
    342 
    343                     generateButtonBarAnimator(
    344                             mButtonBarContainer.getTranslationY(), translationY,
    345                             mButtonBarFloatingHeight, 0, duration);
    346                 } else {
    347                     mButtonBarContainer.setTranslationY(translationY);
    348                     mButtonBarContainer.setTranslationZ(0);
    349                 }
    350             }
    351         } else {
    352             // not currently animating, have already hidden, snap to the right offset
    353             mButtonBarContainer.setTranslationY(translationY);
    354             mButtonBarContainer.setTranslationZ(0);
    355         }
    356 
    357         mHiddenBefore = true;
    358     }
    359 
    360     private void generateButtonBarAnimator(
    361             float startY, float endY, float startZ, float endZ, long duration) {
    362         if (Log.isLoggable(TAG, Log.DEBUG)) {
    363             Log.d(TAG, "generateButtonBarAnimator");
    364             Log.d(TAG, "    startY: " + startY);
    365             Log.d(TAG, "    endY: " + endY);
    366             Log.d(TAG, "    startZ: " + startZ);
    367             Log.d(TAG, "    endZ: " + endZ);
    368             Log.d(TAG, "    duration: " + duration);
    369         }
    370 
    371         mButtonBarAnimator =
    372                 ObjectAnimator.ofPropertyValuesHolder(
    373                         mButtonBarContainer,
    374                         PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, startY, endY),
    375                         PropertyValuesHolder.ofFloat(View.TRANSLATION_Z, startZ, endZ));
    376         mCurrentTranslation = endY;
    377         mButtonBarAnimator.setDuration(duration);
    378         mButtonBarAnimator.setInterpolator(mInterpolator);
    379         mButtonBarAnimator.start();
    380     }
    381 }
    382