1 /* 2 * Copyright (C) 2017 The Android Open Source Project 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.android.incallui.calllocation.impl; 18 19 import android.animation.LayoutTransition; 20 import android.content.Context; 21 import android.graphics.drawable.Drawable; 22 import android.location.Location; 23 import android.os.Bundle; 24 import android.os.Handler; 25 import android.text.TextUtils; 26 import android.view.LayoutInflater; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.widget.ImageView; 30 import android.widget.TextView; 31 import android.widget.ViewAnimator; 32 import com.android.dialer.common.LogUtil; 33 import com.android.dialer.logging.DialerImpression; 34 import com.android.dialer.logging.Logger; 35 import com.android.incallui.baseui.BaseFragment; 36 import java.util.Objects; 37 import java.util.concurrent.TimeUnit; 38 39 /** 40 * Fragment which shows location during E911 calls, to supplement the user with accurate location 41 * information in case the user is asked for their location by the emergency responder. 42 * 43 * <p>If location data is inaccurate, stale, or unavailable, this should not be shown. 44 */ 45 public class LocationFragment extends BaseFragment<LocationPresenter, LocationPresenter.LocationUi> 46 implements LocationPresenter.LocationUi { 47 48 private static final String ADDRESS_DELIMITER = ","; 49 50 // Indexes used to animate fading between views 51 private static final int LOADING_VIEW_INDEX = 0; 52 private static final int LOCATION_VIEW_INDEX = 1; 53 private static final long TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5); 54 55 private ViewAnimator viewAnimator; 56 private ImageView locationMap; 57 private TextView addressLine1; 58 private TextView addressLine2; 59 private TextView latLongLine; 60 private Location location; 61 private ViewGroup locationLayout; 62 63 private boolean isMapSet; 64 private boolean isAddressSet; 65 private boolean isLocationSet; 66 private boolean hasTimeoutStarted; 67 68 private final Handler handler = new Handler(); 69 private final Runnable dataTimeoutRunnable = 70 () -> { 71 LogUtil.i( 72 "LocationFragment.dataTimeoutRunnable", 73 "timed out so animate any future layout changes"); 74 locationLayout.setLayoutTransition(new LayoutTransition()); 75 showLocationNow(); 76 }; 77 78 @Override createPresenter()79 public LocationPresenter createPresenter() { 80 return new LocationPresenter(); 81 } 82 83 @Override getUi()84 public LocationPresenter.LocationUi getUi() { 85 return this; 86 } 87 88 @Override onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)89 public View onCreateView( 90 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 91 LogUtil.enterBlock("LocationFragment.onCreateView"); 92 final View view = inflater.inflate(R.layout.location_fragment, container, false); 93 viewAnimator = (ViewAnimator) view.findViewById(R.id.location_view_animator); 94 locationMap = (ImageView) view.findViewById(R.id.location_map); 95 addressLine1 = (TextView) view.findViewById(R.id.address_line_one); 96 addressLine2 = (TextView) view.findViewById(R.id.address_line_two); 97 latLongLine = (TextView) view.findViewById(R.id.lat_long_line); 98 locationLayout = (ViewGroup) view.findViewById(R.id.location_layout); 99 return view; 100 } 101 102 @Override onDestroy()103 public void onDestroy() { 104 super.onDestroy(); 105 handler.removeCallbacks(dataTimeoutRunnable); 106 } 107 108 @Override setMap(Drawable mapImage)109 public void setMap(Drawable mapImage) { 110 LogUtil.enterBlock("LocationFragment.setMap"); 111 isMapSet = true; 112 locationMap.setVisibility(View.VISIBLE); 113 locationMap.setImageDrawable(mapImage); 114 displayWhenReady(); 115 Logger.get(getContext()).logImpression(DialerImpression.Type.EMERGENCY_GOT_MAP); 116 } 117 118 @Override setAddress(String address)119 public void setAddress(String address) { 120 LogUtil.i("LocationFragment.setAddress", address); 121 isAddressSet = true; 122 addressLine1.setVisibility(View.VISIBLE); 123 addressLine2.setVisibility(View.VISIBLE); 124 if (TextUtils.isEmpty(address)) { 125 addressLine1.setText(null); 126 addressLine2.setText(null); 127 } else { 128 129 // Split the address after the first delimiter for display, if present. 130 // For example, "1600 Amphitheatre Parkway, Mountain View, CA 94043" 131 // => "1600 Amphitheatre Parkway" 132 // => "Mountain View, CA 94043" 133 int splitIndex = address.indexOf(ADDRESS_DELIMITER); 134 if (splitIndex >= 0) { 135 updateText(addressLine1, address.substring(0, splitIndex).trim()); 136 updateText(addressLine2, address.substring(splitIndex + 1).trim()); 137 } else { 138 updateText(addressLine1, address); 139 updateText(addressLine2, null); 140 } 141 142 Logger.get(getContext()).logImpression(DialerImpression.Type.EMERGENCY_GOT_ADDRESS); 143 } 144 displayWhenReady(); 145 } 146 147 @Override setLocation(Location location)148 public void setLocation(Location location) { 149 LogUtil.i("LocationFragment.setLocation", String.valueOf(location)); 150 isLocationSet = true; 151 this.location = location; 152 153 if (location != null) { 154 latLongLine.setVisibility(View.VISIBLE); 155 latLongLine.setText( 156 getContext() 157 .getString( 158 R.string.lat_long_format, location.getLatitude(), location.getLongitude())); 159 160 Logger.get(getContext()).logImpression(DialerImpression.Type.EMERGENCY_GOT_LOCATION); 161 } 162 displayWhenReady(); 163 } 164 displayWhenReady()165 private void displayWhenReady() { 166 // Show the location if all data has loaded, otherwise prime the timeout 167 if (isMapSet && isAddressSet && isLocationSet) { 168 showLocationNow(); 169 } else if (!hasTimeoutStarted) { 170 handler.postDelayed(dataTimeoutRunnable, TIMEOUT_MILLIS); 171 hasTimeoutStarted = true; 172 } 173 } 174 showLocationNow()175 private void showLocationNow() { 176 handler.removeCallbacks(dataTimeoutRunnable); 177 if (viewAnimator.getDisplayedChild() != LOCATION_VIEW_INDEX) { 178 viewAnimator.setDisplayedChild(LOCATION_VIEW_INDEX); 179 viewAnimator.setOnClickListener(v -> launchMap()); 180 } 181 } 182 183 @Override getContext()184 public Context getContext() { 185 return getActivity(); 186 } 187 launchMap()188 private void launchMap() { 189 if (location != null) { 190 startActivity( 191 LocationUrlBuilder.getShowMapIntent( 192 location, addressLine1.getText(), addressLine2.getText())); 193 194 Logger.get(getContext()).logImpression(DialerImpression.Type.EMERGENCY_LAUNCHED_MAP); 195 } 196 } 197 updateText(TextView view, String text)198 private static void updateText(TextView view, String text) { 199 if (!Objects.equals(text, view.getText())) { 200 view.setText(text); 201 } 202 } 203 } 204