Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright 2015 Google Inc. All rights reserved.
      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 
     17 package com.example.android.xyztouristattractions.ui;
     18 
     19 import android.app.Activity;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.graphics.Rect;
     23 import android.graphics.drawable.BitmapDrawable;
     24 import android.graphics.drawable.ColorDrawable;
     25 import android.graphics.drawable.Drawable;
     26 import android.os.Handler;
     27 import android.support.wearable.activity.ConfirmationActivity;
     28 import android.support.wearable.view.ActionPage;
     29 import android.support.wearable.view.CardFrame;
     30 import android.support.wearable.view.CardScrollView;
     31 import android.support.wearable.view.GridPagerAdapter;
     32 import android.support.wearable.view.GridViewPager;
     33 import android.text.TextUtils;
     34 import android.view.Gravity;
     35 import android.view.LayoutInflater;
     36 import android.view.View;
     37 import android.view.ViewGroup;
     38 import android.widget.FrameLayout;
     39 import android.widget.ImageView;
     40 import android.widget.TextView;
     41 
     42 import com.example.android.xyztouristattractions.R;
     43 import com.example.android.xyztouristattractions.common.Attraction;
     44 import com.example.android.xyztouristattractions.common.Constants;
     45 import com.example.android.xyztouristattractions.service.UtilityService;
     46 
     47 import java.util.ArrayList;
     48 
     49 /**
     50  * This adapter backs the main GridViewPager component found in
     51  * {@link com.example.android.xyztouristattractions.ui.AttractionsActivity}.
     52  */
     53 public class AttractionsGridPagerAdapter extends GridPagerAdapter
     54         implements GridViewPager.OnPageChangeListener {
     55 
     56     public static final int FADE_IN_TIME_MS = 250;
     57     public static final int FADE_OUT_TIME_MS = 500;
     58     private static final int GRID_COLUMN_COUNT = 5;
     59     private static final int FADE_OUT_DELAY_MS = 1500;
     60     private static final int PAGER_PRIMARY_IMAGE_COLUMN = 0;
     61     private static final int PAGER_SECONDARY_IMAGE_COLUMN = 1;
     62     private static final int PAGER_DESCRIPTION_COLUMN = 2;
     63     private static final int PAGER_NAVIGATE_ACTION_COLUMN = 3;
     64     private static final int PAGER_OPEN_ACTION_COLUMN = 4;
     65 
     66     private Context mContext;
     67     private LayoutInflater mLayoutInflater;
     68     private ArrayList<Attraction> mAttractions;
     69     private Rect mInsets = new Rect();
     70     private DelayedHide mDelayedHide = new DelayedHide();
     71     private OnChromeFadeListener mOnChromeFadeListener;
     72 
     73     public AttractionsGridPagerAdapter(
     74             Context context, ArrayList<Attraction> attractions) {
     75         super();
     76         mContext = context;
     77         mLayoutInflater = LayoutInflater.from(context);
     78         mAttractions = attractions;
     79     }
     80 
     81     public void setData(ArrayList<Attraction> attractions) {
     82         mAttractions = attractions;
     83     }
     84 
     85     public void setInsets(Rect insets) {
     86         mInsets = insets;
     87     }
     88 
     89     @Override
     90     public int getRowCount() {
     91         return (mAttractions != null && mAttractions.size() > 0) ? mAttractions.size() : 1;
     92     }
     93 
     94     @Override
     95     public int getColumnCount(int i) {
     96         return GRID_COLUMN_COUNT;
     97     }
     98 
     99     @Override
    100     public Object instantiateItem(ViewGroup container, int row, final int column) {
    101         if (mAttractions != null && mAttractions.size() > 0) {
    102             final Attraction attraction = mAttractions.get(row);
    103             switch (column) {
    104                 case PAGER_PRIMARY_IMAGE_COLUMN:
    105                 case PAGER_SECONDARY_IMAGE_COLUMN:
    106                     // Two pages of full screen images, one with the attraction name
    107                     // and one with the distance to the attraction
    108                     final View view = mLayoutInflater.inflate(
    109                             R.layout.gridpager_fullscreen_image, container, false);
    110                     ImageView imageView = (ImageView) view.findViewById(R.id.imageView);
    111                     TextView textView = (TextView) view.findViewById(R.id.textView);
    112                     FrameLayout overlayTextLayout =
    113                             (FrameLayout) view.findViewById(R.id.overlaytext);
    114 
    115                     mDelayedHide.add(overlayTextLayout);
    116                     view.setOnClickListener(mDelayedHide);
    117 
    118                     FrameLayout.LayoutParams params =
    119                             (FrameLayout.LayoutParams) textView.getLayoutParams();
    120                     params.bottomMargin = params.bottomMargin + mInsets.bottom;
    121                     params.leftMargin = mInsets.left;
    122                     params.rightMargin = mInsets.right;
    123                     textView.setLayoutParams(params);
    124 
    125                     if (column == PAGER_PRIMARY_IMAGE_COLUMN) {
    126                         imageView.setImageBitmap(attraction.image);
    127                         textView.setText(attraction.name);
    128                     } else {
    129                         imageView.setImageBitmap(attraction.secondaryImage);
    130                         if (TextUtils.isEmpty(attraction.distance)) {
    131                             overlayTextLayout.setVisibility(View.GONE);
    132                         } else {
    133                             textView.setText(mContext.getString(
    134                                     R.string.map_caption, attraction.distance));
    135                         }
    136                     }
    137                     container.addView(view);
    138                     return view;
    139                 case PAGER_DESCRIPTION_COLUMN:
    140                     // The description card page
    141                     CardScrollView cardScrollView = (CardScrollView) mLayoutInflater.inflate(
    142                             R.layout.gridpager_card, container, false);
    143                     TextView descTextView = (TextView) cardScrollView.findViewById(R.id.textView);
    144                     descTextView.setText(attraction.description);
    145                     cardScrollView.setCardGravity(Gravity.BOTTOM);
    146                     cardScrollView.setExpansionEnabled(true);
    147                     cardScrollView.setExpansionDirection(CardFrame.EXPAND_DOWN);
    148                     cardScrollView.setExpansionFactor(10);
    149                     container.addView(cardScrollView);
    150                     return cardScrollView;
    151                 case PAGER_NAVIGATE_ACTION_COLUMN:
    152                     // The navigate action
    153                     final ActionPage navActionPage = (ActionPage) mLayoutInflater.inflate(
    154                             R.layout.gridpager_action, container, false);
    155 
    156                     navActionPage.setOnClickListener(getStartActionClickListener(
    157                             attraction, Constants.START_NAVIGATION_PATH,
    158                             ConfirmationActivity.SUCCESS_ANIMATION));
    159                     navActionPage.setImageResource(R.drawable.ic_full_directions_walking);
    160                     navActionPage.setText(mContext.getString(R.string.action_navigate));
    161 
    162                     container.addView(navActionPage);
    163                     return navActionPage;
    164                 case PAGER_OPEN_ACTION_COLUMN:
    165                     // The "open on device" action
    166                     final ActionPage openActionPage = (ActionPage) mLayoutInflater.inflate(
    167                             R.layout.gridpager_action, container, false);
    168 
    169                     openActionPage.setOnClickListener(getStartActionClickListener(
    170                             attraction, Constants.START_ATTRACTION_PATH,
    171                             ConfirmationActivity.OPEN_ON_PHONE_ANIMATION));
    172                     openActionPage.setImageResource(R.drawable.ic_full_openonphone);
    173                     openActionPage.setText(mContext.getString(R.string.action_open));
    174 
    175                     container.addView(openActionPage);
    176                     return openActionPage;
    177             }
    178         }
    179         return new View(mContext);
    180     }
    181 
    182     @Override
    183     public Drawable getBackgroundForPage(int row, int column) {
    184         if (column == 0) {
    185             return new ColorDrawable(0); // Empty black drawable
    186         }
    187         if (mAttractions.size() > 0 && mAttractions.get(row).image != null) {
    188             return new BitmapDrawable(mContext.getResources(), mAttractions.get(row).image);
    189         }
    190         return super.getBackgroundForPage(row, column);
    191     }
    192 
    193     @Override
    194     public void destroyItem(ViewGroup viewGroup, int row, int column, Object object) {
    195         mDelayedHide.remove((View) object);
    196         viewGroup.removeView((View)object);
    197     }
    198 
    199     @Override
    200     public boolean isViewFromObject(View view, Object object) {
    201         return view == object;
    202     }
    203 
    204     @Override
    205     public void onPageScrolled(int posX, int posY, float posOffsetX, float posOffsetY,
    206                                int posOffsetPixelsX, int posOffsetPixelsY) {}
    207 
    208     @Override
    209     public void onPageSelected(int row, int col) {}
    210 
    211     @Override
    212     public void onPageScrollStateChanged(int state) {
    213         mDelayedHide.show();
    214     }
    215 
    216     /**
    217      * Use the Wear Message API to execute an action. Clears local and remote notifications and
    218      * also runs a confirmation animation before finishing the Wear activity.
    219      *
    220      * @param attraction The attraction to start the action on
    221      * @param pathName The Wear Message API pathname
    222      * @param confirmAnimationType The confirmation animation type from ConfirmationActivity
    223      */
    224     private void startAction(Attraction attraction, String pathName, int confirmAnimationType) {
    225         Intent intent = new Intent(mContext, ConfirmationActivity.class);
    226         intent.putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE, confirmAnimationType);
    227         intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
    228         mContext.startActivity(intent);
    229 
    230         UtilityService.clearNotification(mContext);
    231         UtilityService.clearRemoteNotifications(mContext);
    232         UtilityService.startDeviceActivity(mContext, pathName, attraction.name, attraction.city);
    233 
    234         ((Activity)mContext).finish();
    235     }
    236 
    237     /**
    238      * Helper method to generate the OnClickListener for the attraction actions.
    239      */
    240     private View.OnClickListener getStartActionClickListener(final Attraction attraction,
    241             final String pathName, final int confirmAnimationType) {
    242         View.OnClickListener clickListener = new View.OnClickListener() {
    243             @Override
    244             public void onClick(View v) {
    245                 startAction(attraction, pathName, confirmAnimationType);
    246             }
    247         };
    248         return clickListener;
    249     }
    250 
    251     public void setOnChromeFadeListener(OnChromeFadeListener listener) {
    252         mOnChromeFadeListener = listener;
    253     }
    254 
    255     public interface OnChromeFadeListener {
    256         void onChromeFadeIn();
    257         void onChromeFadeOut();
    258     }
    259 
    260     /**
    261      * Helper class to fade out views based on a delay and fade them back in if needed as well.
    262      */
    263     private class DelayedHide implements View.OnClickListener {
    264 
    265         ArrayList<View> hideViews = new ArrayList<View>(GRID_COLUMN_COUNT);
    266         Handler mHideHandler = new Handler();
    267         boolean mIsHidden = false;
    268 
    269         Runnable mHideRunnable = new Runnable() {
    270             @Override
    271             public void run() {
    272                 hide();
    273             }
    274         };
    275 
    276         void add(View newView) {
    277             hideViews.add(newView);
    278             delayedHide();
    279         }
    280 
    281         void remove(View removeView) {
    282             hideViews.remove(removeView);
    283         }
    284 
    285         void show() {
    286             mIsHidden = false;
    287             if (mOnChromeFadeListener != null) {
    288                 mOnChromeFadeListener.onChromeFadeIn();
    289             }
    290             for (View view : hideViews) {
    291                 if (view != null) {
    292                     view.animate().alpha(1).setDuration(FADE_IN_TIME_MS).start();
    293                 }
    294             }
    295             delayedHide();
    296         }
    297 
    298         void hide() {
    299             mIsHidden = true;
    300             mHideHandler.removeCallbacks(mHideRunnable);
    301             if (mOnChromeFadeListener != null) {
    302                 mOnChromeFadeListener.onChromeFadeOut();
    303             }
    304             for (int i=0; i<hideViews.size(); i++) {
    305                 if (hideViews.get(i) != null) {
    306                     hideViews.get(i).animate().alpha(0).setDuration(FADE_OUT_TIME_MS).start();
    307                 }
    308             }
    309         }
    310 
    311         void delayedHide() {
    312             mHideHandler.removeCallbacks(mHideRunnable);
    313             mHideHandler.postDelayed(mHideRunnable, FADE_OUT_DELAY_MS);
    314         }
    315 
    316         @Override
    317         public void onClick(View v) {
    318             if (mIsHidden) {
    319                 show();
    320             } else {
    321                 hide();
    322             }
    323         }
    324     }
    325 }
    326