110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian/* 210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * Copyright (C) 2016 The Android Open Source Project 310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * 410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * Licensed under the Apache License, Version 2.0 (the "License"); 510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * you may not use this file except in compliance with the License. 610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * You may obtain a copy of the License at 710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * 810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * http://www.apache.org/licenses/LICENSE-2.0 910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * 1010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * Unless required by applicable law or agreed to in writing, software 1110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * distributed under the License is distributed on an "AS IS" BASIS, 1210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * See the License for the specific language governing permissions and 1410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * limitations under the License. 1510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian */ 1610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 1710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianpackage com.android.dialer.location; 1810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 1910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.app.PendingIntent; 2010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.content.BroadcastReceiver; 2110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.content.Context; 2210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.content.Intent; 2310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.location.Address; 2410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.location.Geocoder; 2510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.location.Location; 2610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.location.LocationManager; 2710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.preference.PreferenceManager; 2810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.support.annotation.NonNull; 2910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.support.annotation.Nullable; 3010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.support.annotation.VisibleForTesting; 313ed0c281a143789ea8187dba60dba6c6b5db9009Eric Erfanianimport android.support.v4.os.UserManagerCompat; 3210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.telephony.TelephonyManager; 3310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport android.text.TextUtils; 3410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport com.android.dialer.common.Assert; 3510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport com.android.dialer.common.LogUtil; 3610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport com.android.dialer.common.concurrent.DialerExecutor.Worker; 372f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanianimport com.android.dialer.common.concurrent.DialerExecutorComponent; 3810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport com.android.dialer.util.PermissionsUtil; 3910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport java.util.List; 4010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport java.util.Locale; 4110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 4210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian/** 4310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * This class is used to detect the country where the user is. It is a simplified version of the 4410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * country detector service in the framework. The sources of country location are queried in the 4510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * following order of reliability: 4610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * 4710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * <ul> 4810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * <li>Mobile network 4910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * <li>Location manager 5010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * <li>SIM's country 5110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * <li>User's default locale 5210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * </ul> 5310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * 5410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * As far as possible this class tries to replicate the behavior of the system's country detector 5510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * service: 1) Order in priority of sources of country location 2) Mobile network information 5610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * provided by CDMA phones is ignored 3) Location information is updated every 12 hours (instead of 5710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * 24 hours in the system) 4) Location updates only uses the {@link 5810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * LocationManager#PASSIVE_PROVIDER} to avoid active use of the GPS 5) If a location is successfully 5910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * obtained and geocoded, we never fall back to use of the SIM's country (for the system, the 6010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * fallback never happens without a reboot) 6) Location is not used if the device does not implement 6110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian * a {@link android.location.Geocoder} 6210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian */ 6310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianpublic class CountryDetector { 6410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private static final String KEY_PREFERENCE_TIME_UPDATED = "preference_time_updated"; 6510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian static final String KEY_PREFERENCE_CURRENT_COUNTRY = "preference_current_country"; 6610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian // Wait 12 hours between updates 6710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private static final long TIME_BETWEEN_UPDATES_MS = 1000L * 60 * 60 * 12; 6810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian // Minimum distance before an update is triggered, in meters. We don't need this to be too 6910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian // exact because all we care about is what country the user is in. 7010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private static final long DISTANCE_BETWEEN_UPDATES_METERS = 5000; 7110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian // Used as a default country code when all the sources of country data have failed in the 7210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian // exceedingly rare event that the device does not have a default locale set for some reason. 7310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private static final String DEFAULT_COUNTRY_ISO = "US"; 7410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 7510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian @VisibleForTesting static CountryDetector sInstance; 7610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 7710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private final TelephonyManager telephonyManager; 7810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private final LocaleProvider localeProvider; 7910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private final Geocoder geocoder; 8010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private final Context appContext; 8110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 8210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian @VisibleForTesting 8310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian CountryDetector( 8410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian Context appContext, 8510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian TelephonyManager telephonyManager, 8610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian LocationManager locationManager, 8710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian LocaleProvider localeProvider, 8810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian Geocoder geocoder) { 8910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian this.telephonyManager = telephonyManager; 9010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian this.localeProvider = localeProvider; 9110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian this.appContext = appContext; 9210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian this.geocoder = geocoder; 9310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 9410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian // If the device does not implement Geocoder there is no point trying to get location updates 9510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian // because we cannot retrieve the country based on the location anyway. 9610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian if (Geocoder.isPresent()) { 9710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian registerForLocationUpdates(appContext, locationManager); 9810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 9910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 10010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 10110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private static void registerForLocationUpdates(Context context, LocationManager locationManager) { 10210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian if (!PermissionsUtil.hasLocationPermissions(context)) { 10310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian LogUtil.w( 10410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian "CountryDetector.registerForLocationUpdates", 10510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian "no location permissions, not registering for location updates"); 10610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return; 10710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 10810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 10910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian LogUtil.i("CountryDetector.registerForLocationUpdates", "registering for location updates"); 11010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 11110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian final Intent activeIntent = new Intent(context, LocationChangedReceiver.class); 11210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian final PendingIntent pendingIntent = 11310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian PendingIntent.getBroadcast(context, 0, activeIntent, PendingIntent.FLAG_UPDATE_CURRENT); 11410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 11510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian locationManager.requestLocationUpdates( 11610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian LocationManager.PASSIVE_PROVIDER, 11710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian TIME_BETWEEN_UPDATES_MS, 11810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian DISTANCE_BETWEEN_UPDATES_METERS, 11910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian pendingIntent); 12010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 12110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 12210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian /** @return the single instance of the {@link CountryDetector} */ 12310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian public static synchronized CountryDetector getInstance(Context context) { 12410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian if (sInstance == null) { 12510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian Context appContext = context.getApplicationContext(); 12610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian sInstance = 12710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian new CountryDetector( 12810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian appContext, 12910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE), 13010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian (LocationManager) context.getSystemService(Context.LOCATION_SERVICE), 13110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian Locale::getDefault, 13210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian new Geocoder(appContext)); 13310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 13410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return sInstance; 13510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 13610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 13710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian public String getCurrentCountryIso() { 13810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian String result = null; 13910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian if (isNetworkCountryCodeAvailable()) { 14010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian result = getNetworkBasedCountryIso(); 14110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 14210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian if (TextUtils.isEmpty(result)) { 14310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian result = getLocationBasedCountryIso(); 14410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 14510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian if (TextUtils.isEmpty(result)) { 14610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian result = getSimBasedCountryIso(); 14710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 14810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian if (TextUtils.isEmpty(result)) { 14910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian result = getLocaleBasedCountryIso(); 15010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 15110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian if (TextUtils.isEmpty(result)) { 15210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian result = DEFAULT_COUNTRY_ISO; 15310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 15410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return result.toUpperCase(Locale.US); 15510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 15610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 15710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian /** @return the country code of the current telephony network the user is connected to. */ 15810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private String getNetworkBasedCountryIso() { 15910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return telephonyManager.getNetworkCountryIso(); 16010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 16110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 16210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian /** @return the geocoded country code detected by the {@link LocationManager}. */ 1633ed0c281a143789ea8187dba60dba6c6b5db9009Eric Erfanian @Nullable 16410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private String getLocationBasedCountryIso() { 1653ed0c281a143789ea8187dba60dba6c6b5db9009Eric Erfanian if (!Geocoder.isPresent() 1663ed0c281a143789ea8187dba60dba6c6b5db9009Eric Erfanian || !PermissionsUtil.hasLocationPermissions(appContext) 1673ed0c281a143789ea8187dba60dba6c6b5db9009Eric Erfanian || !UserManagerCompat.isUserUnlocked(appContext)) { 16810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return null; 16910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 17010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return PreferenceManager.getDefaultSharedPreferences(appContext) 17110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian .getString(KEY_PREFERENCE_CURRENT_COUNTRY, null); 17210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 17310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 17410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian /** @return the country code of the SIM card currently inserted in the device. */ 17510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private String getSimBasedCountryIso() { 17610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return telephonyManager.getSimCountryIso(); 17710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 17810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 17910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian /** @return the country code of the user's currently selected locale. */ 18010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private String getLocaleBasedCountryIso() { 18110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian Locale defaultLocale = localeProvider.getLocale(); 18210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian if (defaultLocale != null) { 18310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return defaultLocale.getCountry(); 18410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 18510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return null; 18610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 18710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 18810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private boolean isNetworkCountryCodeAvailable() { 18910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian // On CDMA TelephonyManager.getNetworkCountryIso() just returns the SIM's country code. 19010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian // In this case, we want to ignore the value returned and fallback to location instead. 19110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM; 19210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 19310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 19410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian /** Interface for accessing the current locale. */ 19510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian interface LocaleProvider { 19610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian Locale getLocale(); 19710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 19810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 19910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian public static class LocationChangedReceiver extends BroadcastReceiver { 20010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 20110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian @Override 20210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian public void onReceive(final Context context, Intent intent) { 20310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian if (!intent.hasExtra(LocationManager.KEY_LOCATION_CHANGED)) { 20410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return; 20510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 20610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 20710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian final Location location = 20810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian (Location) intent.getExtras().get(LocationManager.KEY_LOCATION_CHANGED); 20910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 21010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian // TODO: rething how we access the gecoder here, right now we have to set the static instance 21110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian // of CountryDetector to make this work for tests which is weird 21210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian // (see CountryDetectorTest.locationChangedBroadcast_GeocodesLocation) 21310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian processLocationUpdate(context, CountryDetector.getInstance(context).geocoder, location); 21410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 21510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 21610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 21710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private static void processLocationUpdate( 21810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian Context appContext, Geocoder geocoder, Location location) { 2192f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian DialerExecutorComponent.get(appContext) 2202f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian .dialerExecutorFactory() 2212f1c7586bcce334ca69022eb8dc6d8965ceb6a05Eric Erfanian .createNonUiTaskBuilder(new GeocodeCountryWorker(geocoder)) 22210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian .onSuccess( 22310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian country -> { 22410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian if (country == null) { 22510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return; 22610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 22710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 22810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian PreferenceManager.getDefaultSharedPreferences(appContext) 22910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian .edit() 23010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian .putLong(CountryDetector.KEY_PREFERENCE_TIME_UPDATED, System.currentTimeMillis()) 23110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian .putString(CountryDetector.KEY_PREFERENCE_CURRENT_COUNTRY, country) 23210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian .apply(); 23310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian }) 23410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian .onFailure( 23510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian throwable -> 23610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian LogUtil.w( 23710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian "CountryDetector.processLocationUpdate", 23810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian "exception occurred when getting geocoded country from location", 23910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian throwable)) 24010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian .build() 24110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian .executeParallel(location); 24210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 24310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 24410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian /** Worker that given a {@link Location} returns an ISO 3166-1 two letter country code. */ 24510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian private static class GeocodeCountryWorker implements Worker<Location, String> { 24610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian @NonNull private final Geocoder geocoder; 24710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 24810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian GeocodeCountryWorker(@NonNull Geocoder geocoder) { 24910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian this.geocoder = Assert.isNotNull(geocoder); 25010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 25110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 25210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian /** @return the ISO 3166-1 two letter country code if geocoded, else null */ 25310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian @Nullable 25410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian @Override 25510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian public String doInBackground(@Nullable Location location) throws Throwable { 25610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian if (location == null) { 25710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return null; 25810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 25910b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian 26010b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian List<Address> addresses = 26110b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1); 26210b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian if (addresses != null && !addresses.isEmpty()) { 26310b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return addresses.get(0).getCountryCode(); 26410b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 26510b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian return null; 26610b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 26710b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian } 26810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian} 269