/* * Copyright (C) 2013 - 2016 Alexander "Evisceration" Martinz * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package org.namelessrom.devicecontrol.modules.info.hardware; import android.Manifest; import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; import android.location.Address; import android.location.Location; import android.os.Build; import android.os.Vibrator; import android.support.annotation.Nullable; import android.support.v4.content.LocalBroadcastManager; import android.util.AttributeSet; import android.widget.FrameLayout; import android.widget.TextView; import com.google.android.gms.location.LocationRequest; import org.namelessrom.devicecontrol.App; import org.namelessrom.devicecontrol.BuildConfig; import org.namelessrom.devicecontrol.R; import org.namelessrom.devicecontrol.activities.BaseActivity; import org.namelessrom.devicecontrol.views.CardTitleView; import java.util.ArrayList; import java.util.List; import pl.charmas.android.reactivelocation.ReactiveLocationProvider; import rx.Observable; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; import rx.functions.Action1; import rx.functions.Func1; import rx.schedulers.Schedulers; import timber.log.Timber; public class GpsView extends CardTitleView { private Observable<String> addressObservable; private Subscription addressSubscription; private TextView statusView; private String unknownLocation; public GpsView(Context context) { super(context); } public GpsView(Context context, AttributeSet attrs) { super(context, attrs); } public GpsView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.M) public GpsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void init(@Nullable AttributeSet attrs) { super.init(attrs); final Context context = getContext(); unknownLocation = context.getString(R.string.gps_unknown_location); statusView = new TextView(context); final FrameLayout content = getContentView(); content.addView(statusView); statusView.setText(R.string.gps_requesting_location); final ReactiveLocationProvider provider = new ReactiveLocationProvider(context); final LocationRequest locationRequest = LocationRequest.create() .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY) .setFastestInterval(1000) .setInterval(10000); // get last known location, to display something quite fast subscribe(provider, provider.getLastKnownLocation()); // then subscribe to get location updates addressObservable = subscribe(provider, provider.getUpdatedLocation(locationRequest)); } private Observable<String> subscribe(final ReactiveLocationProvider provider, Observable<Location> locationObservable) { return locationObservable .flatMap(new LocationObservableFunc1(provider)) .map(new ListAddressFunc1()) .map(new StringAddressFunc1(unknownLocation)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } public void onResume() { final Context context = getContext(); final boolean location = BaseActivity.isGranted(context, Manifest.permission.ACCESS_COARSE_LOCATION) && BaseActivity.isGranted(context, Manifest.permission.ACCESS_FINE_LOCATION); if (!location) { final Intent intent = new Intent(BaseActivity.ACTION_REQUEST_PERMISSION); final ArrayList<String> permissions = new ArrayList<>(2); permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION); permissions.add(Manifest.permission.ACCESS_FINE_LOCATION); intent.putStringArrayListExtra(BaseActivity.EXTRA_PERMISSIONS, permissions); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); } addressSubscription = addressObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Action1<String>() { @Override public void call(String s) { if (BuildConfig.DEBUG) { final Vibrator vibrator = App.get().getVibrator(); vibrator.cancel(); vibrator.vibrate(75); } statusView.setText(s); } }, new ErrorHandler(statusView)); } public void onPause() { if (addressSubscription != null) { addressSubscription.unsubscribe(); } } private static class ErrorHandler implements Action1<Throwable> { private final TextView statusView; public ErrorHandler(TextView statusView) { this.statusView = statusView; } @Override public void call(Throwable throwable) { Timber.d(throwable, "Error occurred when requesting location"); if (statusView != null) { statusView.setText(R.string.gps_requesting_location_error); } } } private static class LocationObservableFunc1 implements Func1<Location, Observable<List<Address>>> { private final ReactiveLocationProvider provider; public LocationObservableFunc1(ReactiveLocationProvider provider) { this.provider = provider; } @Override public Observable<List<Address>> call(Location location) { return provider.getReverseGeocodeObservable(location.getLatitude(), location.getLongitude(), 1); } } private static class ListAddressFunc1 implements Func1<List<Address>, Address> { @Override public Address call(List<Address> addresses) { if (addresses == null || addresses.isEmpty()) { return null; } return addresses.get(0); } } private static class StringAddressFunc1 implements Func1<Address, String> { private final String unknownLocation; public StringAddressFunc1(String unknownLocation) { this.unknownLocation = unknownLocation; } @Override public String call(Address address) { if (address == null) { return unknownLocation; } // collect all address lines final StringBuilder sb = new StringBuilder(); for (int i = 0; i <= address.getMaxAddressLineIndex(); i++) { sb.append(address.getAddressLine(i)).append('\n'); } return sb.toString(); } } }