/* * Copyright 2015 Google Inc. All rights reserved. * * 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.example.android.xyztouristattractions.ui; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.support.wearable.activity.ConfirmationActivity; import android.support.wearable.view.ActionPage; import android.support.wearable.view.CardFrame; import android.support.wearable.view.CardScrollView; import android.support.wearable.view.GridPagerAdapter; import android.support.wearable.view.GridViewPager; import android.text.TextUtils; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import com.example.android.xyztouristattractions.R; import com.example.android.xyztouristattractions.common.Attraction; import com.example.android.xyztouristattractions.common.Constants; import com.example.android.xyztouristattractions.service.UtilityService; import java.util.ArrayList; /** * This adapter backs the main GridViewPager component found in * {@link com.example.android.xyztouristattractions.ui.AttractionsActivity}. */ public class AttractionsGridPagerAdapter extends GridPagerAdapter implements GridViewPager.OnPageChangeListener { public static final int FADE_IN_TIME_MS = 250; public static final int FADE_OUT_TIME_MS = 500; private static final int GRID_COLUMN_COUNT = 5; private static final int FADE_OUT_DELAY_MS = 1500; private static final int PAGER_PRIMARY_IMAGE_COLUMN = 0; private static final int PAGER_SECONDARY_IMAGE_COLUMN = 1; private static final int PAGER_DESCRIPTION_COLUMN = 2; private static final int PAGER_NAVIGATE_ACTION_COLUMN = 3; private static final int PAGER_OPEN_ACTION_COLUMN = 4; private Context mContext; private LayoutInflater mLayoutInflater; private ArrayList<Attraction> mAttractions; private Rect mInsets = new Rect(); private DelayedHide mDelayedHide = new DelayedHide(); private OnChromeFadeListener mOnChromeFadeListener; public AttractionsGridPagerAdapter( Context context, ArrayList<Attraction> attractions) { super(); mContext = context; mLayoutInflater = LayoutInflater.from(context); mAttractions = attractions; } public void setData(ArrayList<Attraction> attractions) { mAttractions = attractions; } public void setInsets(Rect insets) { mInsets = insets; } @Override public int getRowCount() { return (mAttractions != null && mAttractions.size() > 0) ? mAttractions.size() : 1; } @Override public int getColumnCount(int i) { return GRID_COLUMN_COUNT; } @Override public Object instantiateItem(ViewGroup container, int row, final int column) { if (mAttractions != null && mAttractions.size() > 0) { final Attraction attraction = mAttractions.get(row); switch (column) { case PAGER_PRIMARY_IMAGE_COLUMN: case PAGER_SECONDARY_IMAGE_COLUMN: // Two pages of full screen images, one with the attraction name // and one with the distance to the attraction final View view = mLayoutInflater.inflate( R.layout.gridpager_fullscreen_image, container, false); ImageView imageView = (ImageView) view.findViewById(R.id.imageView); TextView textView = (TextView) view.findViewById(R.id.textView); FrameLayout overlayTextLayout = (FrameLayout) view.findViewById(R.id.overlaytext); mDelayedHide.add(overlayTextLayout); view.setOnClickListener(mDelayedHide); FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) textView.getLayoutParams(); params.bottomMargin = params.bottomMargin + mInsets.bottom; params.leftMargin = mInsets.left; params.rightMargin = mInsets.right; textView.setLayoutParams(params); if (column == PAGER_PRIMARY_IMAGE_COLUMN) { imageView.setImageBitmap(attraction.image); textView.setText(attraction.name); } else { imageView.setImageBitmap(attraction.secondaryImage); if (TextUtils.isEmpty(attraction.distance)) { overlayTextLayout.setVisibility(View.GONE); } else { textView.setText(mContext.getString( R.string.map_caption, attraction.distance)); } } container.addView(view); return view; case PAGER_DESCRIPTION_COLUMN: // The description card page CardScrollView cardScrollView = (CardScrollView) mLayoutInflater.inflate( R.layout.gridpager_card, container, false); TextView descTextView = (TextView) cardScrollView.findViewById(R.id.textView); descTextView.setText(attraction.description); cardScrollView.setCardGravity(Gravity.BOTTOM); cardScrollView.setExpansionEnabled(true); cardScrollView.setExpansionDirection(CardFrame.EXPAND_DOWN); cardScrollView.setExpansionFactor(10); container.addView(cardScrollView); return cardScrollView; case PAGER_NAVIGATE_ACTION_COLUMN: // The navigate action final ActionPage navActionPage = (ActionPage) mLayoutInflater.inflate( R.layout.gridpager_action, container, false); navActionPage.setOnClickListener(getStartActionClickListener( attraction, Constants.START_NAVIGATION_PATH, ConfirmationActivity.SUCCESS_ANIMATION)); navActionPage.setImageResource(R.drawable.ic_full_directions_walking); navActionPage.setText(mContext.getString(R.string.action_navigate)); container.addView(navActionPage); return navActionPage; case PAGER_OPEN_ACTION_COLUMN: // The "open on device" action final ActionPage openActionPage = (ActionPage) mLayoutInflater.inflate( R.layout.gridpager_action, container, false); openActionPage.setOnClickListener(getStartActionClickListener( attraction, Constants.START_ATTRACTION_PATH, ConfirmationActivity.OPEN_ON_PHONE_ANIMATION)); openActionPage.setImageResource(R.drawable.ic_full_openonphone); openActionPage.setText(mContext.getString(R.string.action_open)); container.addView(openActionPage); return openActionPage; } } return new View(mContext); } @Override public Drawable getBackgroundForPage(int row, int column) { if (column == 0) { return new ColorDrawable(0); // Empty black drawable } if (mAttractions.size() > 0 && mAttractions.get(row).image != null) { return new BitmapDrawable(mContext.getResources(), mAttractions.get(row).image); } return super.getBackgroundForPage(row, column); } @Override public void destroyItem(ViewGroup viewGroup, int row, int column, Object object) { mDelayedHide.remove((View) object); viewGroup.removeView((View)object); } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public void onPageScrolled(int posX, int posY, float posOffsetX, float posOffsetY, int posOffsetPixelsX, int posOffsetPixelsY) {} @Override public void onPageSelected(int row, int col) {} @Override public void onPageScrollStateChanged(int state) { mDelayedHide.show(); } /** * Use the Wear Message API to execute an action. Clears local and remote notifications and * also runs a confirmation animation before finishing the Wear activity. * * @param attraction The attraction to start the action on * @param pathName The Wear Message API pathname * @param confirmAnimationType The confirmation animation type from ConfirmationActivity */ private void startAction(Attraction attraction, String pathName, int confirmAnimationType) { Intent intent = new Intent(mContext, ConfirmationActivity.class); intent.putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE, confirmAnimationType); intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); mContext.startActivity(intent); UtilityService.clearNotification(mContext); UtilityService.clearRemoteNotifications(mContext); UtilityService.startDeviceActivity(mContext, pathName, attraction.name, attraction.city); ((Activity)mContext).finish(); } /** * Helper method to generate the OnClickListener for the attraction actions. */ private View.OnClickListener getStartActionClickListener(final Attraction attraction, final String pathName, final int confirmAnimationType) { View.OnClickListener clickListener = new View.OnClickListener() { @Override public void onClick(View v) { startAction(attraction, pathName, confirmAnimationType); } }; return clickListener; } public void setOnChromeFadeListener(OnChromeFadeListener listener) { mOnChromeFadeListener = listener; } public interface OnChromeFadeListener { void onChromeFadeIn(); void onChromeFadeOut(); } /** * Helper class to fade out views based on a delay and fade them back in if needed as well. */ private class DelayedHide implements View.OnClickListener { ArrayList<View> hideViews = new ArrayList<View>(GRID_COLUMN_COUNT); Handler mHideHandler = new Handler(); boolean mIsHidden = false; Runnable mHideRunnable = new Runnable() { @Override public void run() { hide(); } }; void add(View newView) { hideViews.add(newView); delayedHide(); } void remove(View removeView) { hideViews.remove(removeView); } void show() { mIsHidden = false; if (mOnChromeFadeListener != null) { mOnChromeFadeListener.onChromeFadeIn(); } for (View view : hideViews) { if (view != null) { view.animate().alpha(1).setDuration(FADE_IN_TIME_MS).start(); } } delayedHide(); } void hide() { mIsHidden = true; mHideHandler.removeCallbacks(mHideRunnable); if (mOnChromeFadeListener != null) { mOnChromeFadeListener.onChromeFadeOut(); } for (int i=0; i<hideViews.size(); i++) { if (hideViews.get(i) != null) { hideViews.get(i).animate().alpha(0).setDuration(FADE_OUT_TIME_MS).start(); } } } void delayedHide() { mHideHandler.removeCallbacks(mHideRunnable); mHideHandler.postDelayed(mHideRunnable, FADE_OUT_DELAY_MS); } @Override public void onClick(View v) { if (mIsHidden) { show(); } else { hide(); } } } }