1 /* 2 * Copyright (C) 2016 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.tv.common.util; 18 19 import android.Manifest; 20 import android.content.Context; 21 import android.content.pm.PackageManager; 22 import android.location.Address; 23 import android.location.Geocoder; 24 import android.location.Location; 25 import android.location.LocationListener; 26 import android.location.LocationManager; 27 import android.os.Bundle; 28 import android.support.annotation.NonNull; 29 import android.text.TextUtils; 30 import android.util.Log; 31 import com.android.tv.common.BuildConfig; 32 33 34 35 36 import java.io.IOException; 37 import java.util.Collections; 38 import java.util.HashSet; 39 import java.util.List; 40 import java.util.Locale; 41 import java.util.Set; 42 43 /** A utility class to get the current location. */ 44 public class LocationUtils { 45 private static final String TAG = "LocationUtils"; 46 private static final boolean DEBUG = false; 47 48 private static final Set<OnUpdateAddressListener> sOnUpdateAddressListeners = 49 Collections.synchronizedSet(new HashSet<>()); 50 51 private static Context sApplicationContext; 52 private static Address sAddress; 53 private static String sCountry; 54 private static IOException sError; 55 56 /** Checks the current location. */ getCurrentAddress(Context context)57 public static synchronized Address getCurrentAddress(Context context) 58 throws IOException, SecurityException { 59 if (sAddress != null) { 60 return sAddress; 61 } 62 if (sError != null) { 63 throw sError; 64 } 65 if (sApplicationContext == null) { 66 sApplicationContext = context.getApplicationContext(); 67 } 68 LocationUtilsHelper.startLocationUpdates(); 69 return null; 70 } 71 72 /** The listener used when address is updated. */ 73 public interface OnUpdateAddressListener { 74 /** 75 * Called when address is updated. 76 * 77 * This listener is removed when this method returns true. 78 * 79 * @return {@code true} if the job has been finished and the listener needs to be removed; 80 * {@code false} otherwise. 81 */ onUpdateAddress(Address address)82 boolean onUpdateAddress(Address address); 83 } 84 85 /** 86 * Add an {@link OnUpdateAddressListener} instance. 87 * 88 * Note that the listener is removed automatically when 89 * {@link OnUpdateAddressListener#onUpdateAddress(Address)} is called and returns {@code true}. 90 */ addOnUpdateAddressListener(OnUpdateAddressListener listener)91 public static void addOnUpdateAddressListener(OnUpdateAddressListener listener) { 92 sOnUpdateAddressListeners.add(listener); 93 } 94 95 /** 96 * Remove an {@link OnUpdateAddressListener} instance if it exists. 97 * 98 * Note that the listener will be removed automatically when 99 * {@link OnUpdateAddressListener#onUpdateAddress(Address)} is called and returns {@code true}. 100 */ removeOnUpdateAddressListener(OnUpdateAddressListener listener)101 public static void removeOnUpdateAddressListener(OnUpdateAddressListener listener) { 102 sOnUpdateAddressListeners.remove(listener); 103 } 104 105 /** Returns the current country. */ 106 @NonNull getCurrentCountry(Context context)107 public static synchronized String getCurrentCountry(Context context) { 108 if (sCountry != null) { 109 return sCountry; 110 } 111 if (TextUtils.isEmpty(sCountry)) { 112 sCountry = context.getResources().getConfiguration().locale.getCountry(); 113 } 114 return sCountry; 115 } 116 updateAddress(Location location)117 private static void updateAddress(Location location) { 118 if (DEBUG) Log.d(TAG, "Updating address with " + location); 119 if (location == null) { 120 return; 121 } 122 Geocoder geocoder = new Geocoder(sApplicationContext, Locale.getDefault()); 123 try { 124 List<Address> addresses = 125 geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1); 126 if (addresses != null && !addresses.isEmpty()) { 127 sAddress = addresses.get(0); 128 if (DEBUG) Log.d(TAG, "Got " + sAddress); 129 try { 130 PostalCodeUtils.updatePostalCode(sApplicationContext); 131 } catch (Exception e) { 132 // Do nothing 133 } 134 Set<OnUpdateAddressListener> listenersToRemove = new HashSet<>(); 135 synchronized (sOnUpdateAddressListeners) { 136 for (OnUpdateAddressListener listener : sOnUpdateAddressListeners) { 137 if (listener.onUpdateAddress(sAddress)) { 138 listenersToRemove.add(listener); 139 } 140 } 141 for (OnUpdateAddressListener listener : listenersToRemove) { 142 removeOnUpdateAddressListener(listener); 143 } 144 } 145 } else { 146 if (DEBUG) Log.d(TAG, "No address returned"); 147 } 148 sError = null; 149 } catch (IOException e) { 150 Log.w(TAG, "Error in updating address", e); 151 sError = e; 152 } 153 } 154 LocationUtils()155 private LocationUtils() {} 156 157 private static class LocationUtilsHelper { 158 private static final LocationListener LOCATION_LISTENER = 159 new LocationListener() { 160 @Override 161 public void onLocationChanged(Location location) { 162 updateAddress(location); 163 } 164 165 @Override 166 public void onStatusChanged(String provider, int status, Bundle extras) {} 167 168 @Override 169 public void onProviderEnabled(String provider) {} 170 171 @Override 172 public void onProviderDisabled(String provider) {} 173 }; 174 175 private static LocationManager sLocationManager; 176 startLocationUpdates()177 public static void startLocationUpdates() { 178 if (sLocationManager == null) { 179 sLocationManager = 180 (LocationManager) 181 sApplicationContext.getSystemService(Context.LOCATION_SERVICE); 182 try { 183 sLocationManager.requestLocationUpdates( 184 LocationManager.NETWORK_PROVIDER, 1000, 10, LOCATION_LISTENER, null); 185 } catch (SecurityException e) { 186 // Enables requesting the location updates again. 187 sLocationManager = null; 188 throw e; 189 } 190 } 191 } 192 } 193 } 194