| /* |
| * 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. |
| * |
| * <p>The country will be detected in order of reliability, like |
| * |
| * <ul> |
| * <li>Mobile network |
| * <li>Location |
| * <li>SIM's country |
| * <li>Phone's locale |
| * </ul> |
| * |
| * <p>Call the {@link #detectCountry()} to get the available country immediately. |
| * |
| * <p>To be notified of the future country change, use the {@link #addCountryListener} |
| * |
| * <p> |
| * |
| * @hide |
| */ |
| @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) |
| @SystemService(Context.COUNTRY_DETECTOR) |
| public class CountryDetector { |
| |
| /** |
| * The class to wrap the ICountryListener.Stub , CountryListener & {@code Consumer<Country>} |
| * objects together. |
| * |
| * <p>The CountryListener will be notified through the Handler Executor once the country changed |
| * and detected. |
| * |
| * <p>{@code Consumer<Country>} 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<Country> mListener; |
| private final Executor mExecutor; |
| |
| ListenerTransport(Consumer<Country> 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<Consumer<Country>, 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<Country> 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<Country> consumer) { |
| synchronized (mListeners) { |
| ListenerTransport transport = mListeners.remove(consumer); |
| if (transport != null) { |
| try { |
| mService.removeCountryListener(transport); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| } |
| } |
| } |