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 
AttractionsGridPagerAdapter( Context context, ArrayList<Attraction> attractions)73     public AttractionsGridPagerAdapter(
74             Context context, ArrayList<Attraction> attractions) {
75         super();
76         mContext = context;
77         mLayoutInflater = LayoutInflater.from(context);
78         mAttractions = attractions;
79     }
80 
setData(ArrayList<Attraction> attractions)81     public void setData(ArrayList<Attraction> attractions) {
82         mAttractions = attractions;
83     }
84 
setInsets(Rect insets)85     public void setInsets(Rect insets) {
86         mInsets = insets;
87     }
88 
89     @Override
getRowCount()90     public int getRowCount() {
91         return (mAttractions != null && mAttractions.size() > 0) ? mAttractions.size() : 1;
92     }
93 
94     @Override
getColumnCount(int i)95     public int getColumnCount(int i) {
96         return GRID_COLUMN_COUNT;
97     }
98 
99     @Override
instantiateItem(ViewGroup container, int row, final int column)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
getBackgroundForPage(int row, int column)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
destroyItem(ViewGroup viewGroup, int row, int column, Object object)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
isViewFromObject(View view, Object object)200     public boolean isViewFromObject(View view, Object object) {
201         return view == object;
202     }
203 
204     @Override
onPageScrolled(int posX, int posY, float posOffsetX, float posOffsetY, int posOffsetPixelsX, int posOffsetPixelsY)205     public void onPageScrolled(int posX, int posY, float posOffsetX, float posOffsetY,
206                                int posOffsetPixelsX, int posOffsetPixelsY) {}
207 
208     @Override
onPageSelected(int row, int col)209     public void onPageSelected(int row, int col) {}
210 
211     @Override
onPageScrollStateChanged(int state)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      */
startAction(Attraction attraction, String pathName, int confirmAnimationType)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      */
getStartActionClickListener(final Attraction attraction, final String pathName, final int confirmAnimationType)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 
setOnChromeFadeListener(OnChromeFadeListener listener)251     public void setOnChromeFadeListener(OnChromeFadeListener listener) {
252         mOnChromeFadeListener = listener;
253     }
254 
255     public interface OnChromeFadeListener {
onChromeFadeIn()256         void onChromeFadeIn();
onChromeFadeOut()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 
add(View newView)276         void add(View newView) {
277             hideViews.add(newView);
278             delayedHide();
279         }
280 
remove(View removeView)281         void remove(View removeView) {
282             hideViews.remove(removeView);
283         }
284 
show()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 
hide()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 
delayedHide()311         void delayedHide() {
312             mHideHandler.removeCallbacks(mHideRunnable);
313             mHideHandler.postDelayed(mHideRunnable, FADE_OUT_DELAY_MS);
314         }
315 
316         @Override
onClick(View v)317         public void onClick(View v) {
318             if (mIsHidden) {
319                 show();
320             } else {
321                 hide();
322             }
323         }
324     }
325 }
326