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.graphics.Rect;
22 import android.net.Uri;
23 import android.os.AsyncTask;
24 import android.os.Bundle;
25 import android.support.v4.view.GestureDetectorCompat;
26 import android.support.wearable.view.DismissOverlayView;
27 import android.support.wearable.view.DotsPageIndicator;
28 import android.support.wearable.view.GridViewPager;
29 import android.util.Log;
30 import android.view.GestureDetector;
31 import android.view.MotionEvent;
32 import android.view.View;
33 import android.view.WindowInsets;
34 import android.widget.FrameLayout;
35 import android.widget.ProgressBar;
36 
37 import com.example.android.xyztouristattractions.R;
38 import com.example.android.xyztouristattractions.common.Attraction;
39 import com.example.android.xyztouristattractions.common.Constants;
40 import com.example.android.xyztouristattractions.common.Utils;
41 import com.example.android.xyztouristattractions.service.UtilityService;
42 import com.google.android.gms.common.ConnectionResult;
43 import com.google.android.gms.common.api.GoogleApiClient;
44 import com.google.android.gms.maps.model.LatLng;
45 import com.google.android.gms.wearable.DataApi;
46 import com.google.android.gms.wearable.DataMap;
47 import com.google.android.gms.wearable.DataMapItem;
48 import com.google.android.gms.wearable.Wearable;
49 
50 import java.util.ArrayList;
51 import java.util.Iterator;
52 import java.util.List;
53 import java.util.concurrent.TimeUnit;
54 
55 /**
56  * The main Wear activity that displays nearby attractions in a
57  * {@link android.support.wearable.view.GridViewPager}. Each row shows
58  * one attraction and each column shows information or actions for that
59  * particular attraction.
60  */
61 public class AttractionsActivity extends Activity
62         implements AttractionsGridPagerAdapter.OnChromeFadeListener {
63     private static final String TAG = AttractionsActivity.class.getSimpleName();
64 
65     private GestureDetectorCompat mGestureDetector;
66     private DismissOverlayView mDismissOverlayView;
67     private GridViewPager mGridViewPager;
68     private AttractionsGridPagerAdapter mAdapter;
69     private DotsPageIndicator mDotsPageIndicator;
70     private ProgressBar mProgressBar;
71     private Rect mInsets = new Rect(0, 0, 0, 0);
72 
73     private ArrayList<Attraction> mAttractions = new ArrayList<Attraction>();
74 
75     @Override
onCreate(Bundle savedInstanceState)76     protected void onCreate(Bundle savedInstanceState) {
77         super.onCreate(savedInstanceState);
78 
79         setContentView(R.layout.activity_main);
80         final FrameLayout topFrameLayout = (FrameLayout) findViewById(R.id.topFrameLayout);
81         mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
82         mGridViewPager = (GridViewPager) findViewById(R.id.gridViewPager);
83         mDotsPageIndicator = (DotsPageIndicator) findViewById(R.id.dotsPageIndicator);
84         mAdapter = new AttractionsGridPagerAdapter(this, mAttractions);
85         mAdapter.setOnChromeFadeListener(this);
86         mGridViewPager.setAdapter(mAdapter);
87         mDotsPageIndicator.setPager(mGridViewPager);
88         mDotsPageIndicator.setOnPageChangeListener(mAdapter);
89 
90         topFrameLayout.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
91             @Override
92             public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
93                 // Call through to super implementation
94                 insets = topFrameLayout.onApplyWindowInsets(insets);
95 
96                 boolean round = insets.isRound();
97 
98                 // Store system window insets regardless of screen shape
99                 mInsets.set(insets.getSystemWindowInsetLeft(),
100                         insets.getSystemWindowInsetTop(),
101                         insets.getSystemWindowInsetRight(),
102                         insets.getSystemWindowInsetBottom());
103 
104                 if (round) {
105                     // On a round screen calculate the square inset to use.
106                     // Alternatively could use BoxInsetLayout, although calculating
107                     // the inset ourselves lets us position views outside the center
108                     // box. For example, slightly lower on the round screen (by giving
109                     // up some horizontal space).
110                     mInsets = Utils.calculateBottomInsetsOnRoundDevice(
111                             getWindowManager().getDefaultDisplay(), mInsets);
112 
113                     // Boost the dots indicator up by the bottom inset
114                     FrameLayout.LayoutParams params =
115                             (FrameLayout.LayoutParams) mDotsPageIndicator.getLayoutParams();
116                     params.bottomMargin = mInsets.bottom;
117                     mDotsPageIndicator.setLayoutParams(params);
118                 }
119 
120                 mAdapter.setInsets(mInsets);
121                 return insets;
122             }
123         });
124 
125         // Set up the DismissOverlayView
126         mDismissOverlayView = (DismissOverlayView) findViewById(R.id.dismiss_overlay);
127         mDismissOverlayView.setIntroText(getString(R.string.exit_intro_text));
128         mDismissOverlayView.showIntroIfNecessary();
129         mGestureDetector = new GestureDetectorCompat(this, new LongPressListener());
130 
131         Uri attractionsUri = getIntent().getParcelableExtra(Constants.EXTRA_ATTRACTIONS_URI);
132         if (attractionsUri != null) {
133             new FetchDataAsyncTask(this).execute(attractionsUri);
134             UtilityService.clearNotification(this);
135             UtilityService.clearRemoteNotifications(this);
136         } else {
137             finish();
138         }
139     }
140 
141     @Override
dispatchTouchEvent(MotionEvent event)142     public boolean dispatchTouchEvent(MotionEvent event) {
143         return mGestureDetector.onTouchEvent(event) || super.dispatchTouchEvent(event);
144     }
145 
146     @Override
onTouchEvent(MotionEvent event)147     public boolean onTouchEvent(MotionEvent event) {
148         return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
149     }
150 
151     @Override
onChromeFadeIn()152     public void onChromeFadeIn() {
153         // As the custom UI chrome fades in, also fade the DotsPageIndicator in
154         mDotsPageIndicator.animate().alpha(1).setDuration(
155                 AttractionsGridPagerAdapter.FADE_IN_TIME_MS).start();
156     }
157 
158     @Override
onChromeFadeOut()159     public void onChromeFadeOut() {
160         // As the custom UI chrome fades out, also fade the DotsPageIndicator out
161         mDotsPageIndicator.animate().alpha(0).setDuration(
162                 AttractionsGridPagerAdapter.FADE_OUT_TIME_MS).start();
163     }
164 
165     private class LongPressListener extends GestureDetector.SimpleOnGestureListener {
166         @Override
onLongPress(MotionEvent event)167         public void onLongPress(MotionEvent event) {
168             mDismissOverlayView.show();
169         }
170     }
171 
172     /**
173      * A background task to load the attraction data via the Wear DataApi.
174      * This can take a second or two sometimes as several images need to
175      * be loaded.
176      */
177     private class FetchDataAsyncTask extends
178             AsyncTask<Uri, Void, ArrayList<Attraction>> {
179 
180         private Context mContext;
181 
FetchDataAsyncTask(Context context)182         public FetchDataAsyncTask(Context context) {
183             mContext = context;
184         }
185 
186         @Override
doInBackground(Uri... params)187         protected ArrayList<Attraction> doInBackground(Uri... params) {
188             mAttractions.clear();
189 
190             // Connect to Play Services and the Wearable API
191             GoogleApiClient googleApiClient = new GoogleApiClient.Builder(mContext)
192                     .addApi(Wearable.API)
193                     .build();
194 
195             ConnectionResult connectionResult = googleApiClient.blockingConnect(
196                     Constants.GOOGLE_API_CLIENT_TIMEOUT_S, TimeUnit.SECONDS);
197 
198             if (!connectionResult.isSuccess() || !googleApiClient.isConnected()) {
199                 Log.e(TAG, String.format(Constants.GOOGLE_API_CLIENT_ERROR_MSG,
200                         connectionResult.getErrorCode()));
201                 return null;
202             }
203 
204             Uri attractionsUri = params[0];
205             DataApi.DataItemResult dataItemResult =
206                     Wearable.DataApi.getDataItem(googleApiClient, attractionsUri).await();
207 
208             if (dataItemResult.getStatus().isSuccess() && dataItemResult.getDataItem() != null) {
209                 DataMapItem dataMapItem = DataMapItem.fromDataItem(dataItemResult.getDataItem());
210                 List<DataMap> attractionsData =
211                         dataMapItem.getDataMap().getDataMapArrayList(Constants.EXTRA_ATTRACTIONS);
212 
213                 // Loop through each attraction, adding them to the list
214                 Iterator<DataMap> itr = attractionsData.iterator();
215                 while (itr.hasNext()) {
216                     DataMap attractionData = itr.next();
217 
218                     Attraction attraction = new Attraction();
219                     attraction.name = attractionData.getString(Constants.EXTRA_TITLE);
220                     attraction.description =
221                             attractionData.getString(Constants.EXTRA_DESCRIPTION);
222                     attraction.city = attractionData.get(Constants.EXTRA_CITY);
223                     attraction.distance =
224                             attractionData.getString(Constants.EXTRA_DISTANCE);
225                     attraction.location = new LatLng(
226                             attractionData.getDouble(Constants.EXTRA_LOCATION_LAT),
227                             attractionData.getDouble(Constants.EXTRA_LOCATION_LNG));
228                     attraction.image = Utils.loadBitmapFromAsset(googleApiClient,
229                             attractionData.getAsset(Constants.EXTRA_IMAGE));
230                     attraction.secondaryImage = Utils.loadBitmapFromAsset(googleApiClient,
231                             attractionData.getAsset(Constants.EXTRA_IMAGE_SECONDARY));
232 
233                     mAttractions.add(attraction);
234                 }
235             }
236 
237             googleApiClient.disconnect();
238 
239             return mAttractions;
240         }
241 
242         @Override
onPostExecute(ArrayList<Attraction> result)243         protected void onPostExecute(ArrayList<Attraction> result) {
244             if (result != null && result.size() > 0) {
245                 // Update UI based on the result of the background processing
246                 mAdapter.setData(result);
247                 mAdapter.notifyDataSetChanged();
248                 mProgressBar.setVisibility(View.GONE);
249                 mDotsPageIndicator.setVisibility(View.VISIBLE);
250                 mGridViewPager.setVisibility(View.VISIBLE);
251             } else {
252                 finish();
253             }
254         }
255     }
256 }
257