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