/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package android.location; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.SystemService; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Build; import android.os.Handler; import android.os.HandlerExecutor; import android.os.Looper; import android.os.RemoteException; import android.util.Log; import java.util.HashMap; import java.util.concurrent.Executor; import java.util.function.Consumer; /** * This class provides access to the system country detector service. This service allows * applications to obtain the country that the user is in. * *

The country will be detected in order of reliability, like * *

* *

Call the {@link #detectCountry()} to get the available country immediately. * *

To be notified of the future country change, use the {@link #addCountryListener} * *

* * @hide */ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) @SystemService(Context.COUNTRY_DETECTOR) public class CountryDetector { /** * The class to wrap the ICountryListener.Stub , CountryListener & {@code Consumer} * objects together. * *

The CountryListener will be notified through the Handler Executor once the country changed * and detected. * *

{@code Consumer} callback interface is notified through the specific executor * once the country changed and detected. */ private static final class ListenerTransport extends ICountryListener.Stub { private final Consumer mListener; private final Executor mExecutor; ListenerTransport(Consumer consumer, Executor executor) { mListener = consumer; mExecutor = executor; } public void onCountryDetected(final Country country) { mExecutor.execute(() -> mListener.accept(country)); } } private static final String TAG = "CountryDetector"; private final ICountryDetector mService; private final HashMap, ListenerTransport> mListeners; /** * @hide - hide this constructor because it has a parameter of type ICountryDetector, which is a * system private class. The right way to create an instance of this class is using the * factory Context.getSystemService. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public CountryDetector(ICountryDetector service) { mService = service; mListeners = new HashMap<>(); } /** * Start detecting the country that the user is in. * * @return the country if it is available immediately, otherwise null will be returned. * @hide */ @UnsupportedAppUsage public Country detectCountry() { try { return mService.detectCountry(); } catch (RemoteException e) { Log.e(TAG, "detectCountry: RemoteException", e); return null; } } /** * Add a listener to receive the notification when the country is detected or changed. * * @param listener will be called when the country is detected or changed. * @param looper a Looper object whose message queue will be used to implement the callback * mechanism. If looper is null then the callbacks will be called on the main thread. * @hide * @deprecated client using this api should use {@link * #registerCountryDetectorCallback(Executor, Consumer)} } */ @UnsupportedAppUsage @Deprecated public void addCountryListener(@NonNull CountryListener listener, @Nullable Looper looper) { Handler handler = looper != null ? new Handler(looper) : new Handler(); registerCountryDetectorCallback(new HandlerExecutor(handler), listener); } /** * Remove the listener * * @hide * @deprecated client using this api should use {@link * #unregisterCountryDetectorCallback(Consumer)} */ @UnsupportedAppUsage @Deprecated public void removeCountryListener(CountryListener listener) { unregisterCountryDetectorCallback(listener); } /** * Add a callback interface, to be notified when country code is added or changes. * * @param executor The callback executor for the response. * @param consumer {@link Consumer} callback to receive the country code when changed/detected */ public void registerCountryDetectorCallback( @NonNull Executor executor, @NonNull Consumer consumer) { synchronized (mListeners) { unregisterCountryDetectorCallback(consumer); ListenerTransport transport = new ListenerTransport(consumer, executor); try { mService.addCountryListener(transport); mListeners.put(consumer, transport); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } /** Remove the callback subscribed to Update country code */ public void unregisterCountryDetectorCallback(@NonNull Consumer consumer) { synchronized (mListeners) { ListenerTransport transport = mListeners.remove(consumer); if (transport != null) { try { mService.removeCountryListener(transport); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } } }