public class DependencyContainer {

    static AppComponent appComponent;

    public static void initialize(Application app) {
        appComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(app))
                .build();
    }

    @NonNull
    public static FragmentComponent getComponent(@NonNull Fragment fragment) {
        return getComponent(fragment.getContext())
                .plus(new FragmentModule(fragment));
    }

    @NonNull
    public static ContextComponent getComponent(@NonNull Context context) {
        return appComponent.plus(new ContextModule(context));
    }
} @ParametersAreNonnullByDefault
public class LocationMemoAddedEvent {

    public final LocationMemo memo;

    public LocationMemoAddedEvent(LocationMemo memo) {
        this.memo = memo;
    }
} @ParametersAreNonnullByDefault
public class LocationMemoRemovedEvent {

    public final LocationMemo memo;

    public LocationMemoRemovedEvent(LocationMemo memo) {
        this.memo = memo;
    }

} @ParametersAreNonnullByDefault
public class AddressAutocompleAdapter extends ArrayAdapter<CharSequence> {

    static final StyleSpan BOLD = new StyleSpan(Typeface.BOLD);

    final PlaceEngine placeEngine;

    public AddressAutocompleAdapter(Context context, PlaceEngine placeEngine) {
        super(context, R.layout.widget_autocomplete, android.R.id.text1);
        this.placeEngine = placeEngine;
    }

    List<CharSequence> convertToSpannedList(Iterable<AutocompletePrediction> predictions) {
        List<CharSequence> list = new ArrayList<>();

        for (AutocompletePrediction prediction : predictions) {
            list.add(prediction.getFullText(BOLD));
        }
        return list;
    }

    boolean isCompleteInput(@Nullable CharSequence s) {
        if (TextUtils.isEmpty(s)) {
            return false;
        }
        for (int i = 0, length = s.length(); i < length; i++) {
            char c = s.charAt(i);
            if (c >= 'ａ' && c <= 'ｚ') { // zenkaku alphabet
                return false;
            }
        }
        return true;
    }

    void performQuery(final String query) {
        Timber.d("performQuery: %s", query);

        placeEngine.queryAutocompletion(query)
                .subscribe(new Subscriber<Iterable<AutocompletePrediction>>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {
                        Timber.w(e, "failed to queryAutocompletion queryAutocompletion for: %s", query);
                    }

                    @Override
                    public void onNext(Iterable<AutocompletePrediction> predictions) {
                        clear();
                        addAll(convertToSpannedList(predictions));
                    }
                });

    }

    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(@Nullable CharSequence constraint) {
                if (isCompleteInput(constraint)) {
                    assert constraint != null;
                    performQuery(constraint.toString());
                }
                return new FilterResults();
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                notifyDataSetChanged();
            }
        };
    }
} @Table("addresses")
@ParametersAreNonnullByDefault
public class LocationMemo implements Serializable, Comparable<LocationMemo> {

    @SerializedName("id")
    @PrimaryKey(autoincrement = true)
    public long id;

    @SerializedName("address")
    @Column
    @NonNull
    public String address;

    @SerializedName("note")
    @Column
    @NonNull
    public String note;

    @SerializedName("latitude")
    @Column
    public double latitude;

    @SerializedName("longitude")
    @Column
    public double longitude;

    @SerializedName("radius")
    @Column
    public double radius;

    @SerializedName("draw_circle")
    @Column(defaultExpr = "1")
    public boolean drawCircle;

    @SerializedName("marker_hue")
    @Column
    public float markerHue;

    @Setter
    public LocationMemo(long id, @NonNull String address, @NonNull String note, double latitude, double longitude,
            double radius, boolean drawCircle, float markerHue) {
        this.id = id;
        this.address = address;
        this.note = note;
        this.latitude = latitude;
        this.longitude = longitude;
        this.radius = radius;
        this.drawCircle = drawCircle;
        this.markerHue = markerHue;
    }

    public LocationMemo(long id, @NonNull String address, @NonNull String note, @NonNull LatLng location,
            double radius, boolean drawCircle, float markerHue) {
        this(id, address, note, location.latitude, location.longitude, radius, drawCircle, markerHue);
    }

    public LocationMemo(@NonNull String address, @NonNull String note, @NonNull LatLng location,
            double radius, boolean drawCircle, float markerHue) {
        this(0, address, note, location, radius, drawCircle, markerHue);
    }

    public LocationMemo copy() {
        return new LocationMemo(id, address, note, latitude, longitude, radius, drawCircle, markerHue);
    }

    public boolean isPointingSomewhere() {
        return Locations.isSomewhere(latitude, longitude);
    }

    public boolean isPointingNowhere() {
        return Locations.isNowhere(latitude, longitude);
    }

    public LatLng getLatLng() {
        return new LatLng(latitude, longitude);
    }

    public boolean isPersistent() {
        return id > 0;
    }

    public void update(LocationMemo memo) {
        id = memo.id;
        address = memo.address;
        note = memo.note;
        latitude = memo.latitude;
        longitude = memo.longitude;
        radius = memo.radius;
        markerHue = memo.markerHue;
    }

    public void setLatLng(LatLng latLng) {
        latitude = latLng.latitude;
        longitude = latLng.longitude;
    }

    @Override
    public boolean equals(@Nullable Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        LocationMemo that = (LocationMemo) o;

        return id == that.id;
    }

    public boolean contentEquals(LocationMemo memo) {
        return toString().equals(memo.toString());
    }

    @Override
    public int hashCode() {
        return (int) (id % Integer.MAX_VALUE);
    }

    @Override
    public int compareTo(LocationMemo another) {
        return another.address.compareTo(this.address);
    }

    @Override
    public String toString() {
        return "LocationMemo{" +
                "id=" + id +
                ", address='" + address + '\'' +
                ", note='" + note + '\'' +
                ", latitude=" + latitude +
                ", longitude=" + longitude +
                ", radius=" + radius +
                ", drawCircle=" + drawCircle +
                ", markerHue=" + markerHue +
                '}';
    }
} @ParametersAreNonnullByDefault
@Singleton
public class LocationMemoManager implements Iterable<LocationMemo> {

    public static final String kDefaultDrawCircle = "default_draw_circle"; // boolean

    final LocationMemo_Relation relation;

    int count;

    @Inject
    public LocationMemoManager(OrmaDatabase orma) {
        relation = orma.relationOfLocationMemo()
                .orderByIdAsc();
        count = relation.count();
    }

    public int count() {
        assert count == relation.count();
        return count;
    }

    public LocationMemo get(int i) {
        return relation.get(i);
    }

    public LocationMemo newMemo(Context context, MarkerHueAllocator markerHueAllocator, LatLng latLng) {
        double radius = Double.parseDouble(context.getString(R.string.default_radius));
        return new LocationMemo("", "", latLng, radius, getDefaultDrawCircle(context), markerHueAllocator.allocate());
    }

    public static boolean getDefaultDrawCircle(Context context) {
        return new Prefs(context).get(kDefaultDrawCircle, true);
    }

    public static void setDefaultDrawCircle(Context context, boolean value) {
        new Prefs(context).put(kDefaultDrawCircle, value);
    }

    public void upsert(LocationMemo memo) {
        assert memo.id != -1;
        if (memo.id == 0) {
            memo.id = relation.inserter().execute(memo);
        } else {
            relation.upserter().execute(memo);
        }
        count = relation.count();
    }

    public void remove(LocationMemo memo) {
        relation.deleter().idEq(memo.id).execute();
        memo.id = -1;
        count = relation.count();
    }

    public void clear() {
        relation.deleter().execute();
        count = 0;
    }

    @Override
    public Iterator<LocationMemo> iterator() {
        return relation.iterator();
    }
} @ParametersAreNonnullByDefault
public class PlaceEngine {

    final Context context;

    final GoogleApiClient googleApiClient;

    final BehaviorSubject<LocationChangedEvent> locationChangedSubject = BehaviorSubject.create();

    final Geocoder geocoder;

    LatLng location = Locations.NOWHERE;

    @Inject
    public PlaceEngine(Context context, Geocoder geocoder, GoogleApiClient googleApiClient) {
        this.context = context;
        this.geocoder = geocoder;
        this.googleApiClient = googleApiClient;

        googleApiClient.registerConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
            @Override
            public void onConnected(@Nullable Bundle bundle) {
                handleLastLocation();
            }

            @Override
            public void onConnectionSuspended(int i) {
                Timber.i("GoogleApiClient connection suspended");
            }
        });
        googleApiClient.registerConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
            @Override
            public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
                Timber.w("GoogleApiClient connection failed: %s", connectionResult.getErrorMessage());
            }
        });
    }

    public Observable<LocationChangedEvent> getMyLocationChangedObservable() {
        return locationChangedSubject;
    }

    private void handleLastLocation() {
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            guessCurrentLocation();
            return;
        }
        Location currentLocation = LocationServices.FusedLocationApi.getLastLocation(googleApiClient);
        if (currentLocation != null) {
            castMyLocation(Locations.createLatLng(currentLocation), true);
        } else {
            guessCurrentLocation();
        }
    }

    private void guessCurrentLocation() {
        TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);

        Observable.just(
                new Locale("", telephony.getNetworkCountryIso()).getDisplayCountry(),
                new Locale("", telephony.getSimCountryIso()).getDisplayCountry(),
                TimeZone.getDefault().getID(),
                Locale.getDefault().getDisplayCountry()
        )
                .onBackpressureBuffer(1)
                .filter(new Func1<String, Boolean>() {
                    @Override
                    public Boolean call(String s) {
                        return !s.isEmpty();
                    }
                })
                .flatMap(new Func1<String, Observable<LatLng>>() {
                    @Override
                    public Observable<LatLng> call(String hint) {
                        return getLocationFromAddress(hint);
                    }
                })
                .filter(new Func1<LatLng, Boolean>() {
                    @Override
                    public Boolean call(LatLng latLng) {
                        return Locations.isSomewhere(latLng);
                    }
                })
                .first()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<LatLng>() {
                    @Override
                    public void call(LatLng latLng) {
                        castMyLocation(latLng, false);
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        Timber.w(throwable, "no location found in guessCurrentLocation");
                    }
                });
    }

    private void castMyLocation(LatLng latLng, boolean accurate) {
        locationChangedSubject.onNext(new LocationChangedEvent(latLng, accurate));
    }

    public void start() {
        googleApiClient.connect();
    }

    public void stop() {
        googleApiClient.disconnect();
    }

    public void setLocation(@NonNull LatLng latLng) {
        this.location = latLng;
    }

    public Observable<Iterable<AutocompletePrediction>> queryAutocompletion(final String s) {
        if (!googleApiClient.isConnected()) {
            Timber.w("GoogleApiClient is not connected");
            return Observable.empty();
        }

        return Observable.create(new Observable.OnSubscribe<Iterable<AutocompletePrediction>>() {
            @Override
            public void call(final Subscriber<? super Iterable<AutocompletePrediction>> subscriber) {
                LatLngBounds bounds = LatLngBounds.builder()
                        .include(location)
                        .build();

                final PendingResult<AutocompletePredictionBuffer> result = Places.GeoDataApi
                        .getAutocompletePredictions(googleApiClient, s, bounds, null);

                result.setResultCallback(new ResultCallback<AutocompletePredictionBuffer>() {
                    @Override
                    public void onResult(AutocompletePredictionBuffer autocompletePredictions) {
                        subscriber.onNext(autocompletePredictions);
                        autocompletePredictions.release();
                        subscriber.onCompleted();
                    }
                });
            }
        });
    }

    // Geocoding

    @DebugLog
    public Observable<String> getAddressFromLocation(final LatLng latLng) {
        return Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                final List<Address> addresses;
                try {
                    addresses = geocoder.getFromLocation(latLng.latitude, latLng.longitude, 1);
                } catch (IOException e) {
                    subscriber.onError(new GeocodingException(e));
                    return;
                }

                if (addresses.isEmpty()) {
                    subscriber.onError(new GeocodingException("No addresses found for: " + latLng));
                } else {
                    final Address address = addresses.get(0);

                    final List<String> names = new ArrayList<>();
                    for (int i = Math.min(1, address.getMaxAddressLineIndex()); i <= address.getMaxAddressLineIndex(); i++) {
                        names.add(address.getAddressLine(i));
                    }

                    subscriber.onNext(TextUtils.join(" ", names));
                    subscriber.onCompleted();
                }
            }
        });
    }

    @DebugLog
    public Observable<LatLng> getLocationFromAddress(final String address) {
        return Observable.create(new Observable.OnSubscribe<LatLng>() {
            @Override
            public void call(Subscriber<? super LatLng> subscriber) {
                final List<Address> addresses;
                try {
                    addresses = geocoder.getFromLocationName(address, 1);
                } catch (IOException e) {
                    subscriber.onError(new GeocodingException(e));
                    return;
                }

                if (addresses.isEmpty()) {
                    subscriber.onError(new GeocodingException("No location found for: " + address));
                } else {
                    Address address = addresses.get(0);
                    subscriber.onNext(Locations.createLatLng(address));
                    subscriber.onCompleted();
                }
            }
        });
    }

    public static class GeocodingException extends Exception {

        public GeocodingException(String detailMessage) {
            super(detailMessage);
        }

        public GeocodingException(Throwable throwable) {
            super(throwable);
        }
    }
} public class Assets {

    @Inject
    DisplayMetrics displayMetrics;

    @Inject
    public Assets() {
    }

    @ColorInt
    public int hueToColor(float hue) {
        float[] hsv = {hue, 1.0f, 1.0f};
        return Color.HSVToColor(hsv);
    }

    @ColorInt
    public int makeDark(@ColorInt int color, @FloatRange(from = 0.0, to = 1.0) float factor) {
        int a = Color.alpha(color);
        int r = Color.red(color);
        int g = Color.green(color);
        int b = Color.blue(color);

        return Color.argb(a,
                Math.max((int) (r * factor), 0),
                Math.max((int) (g * factor), 0),
                Math.max((int) (b * factor), 0));
    }

    public Drawable createMarkerDrawable(float hue) {
        int color = hueToColor(hue);
        GradientDrawable drawable = new GradientDrawable();
        drawable.setShape(GradientDrawable.OVAL);
        drawable.setColor(color);
        drawable.setStroke(dpToPx(1), makeDark(color, 0.7f));
        return drawable;
    }


    public int dpToPx(int dp) {
        return (int) (dp * displayMetrics.density);
    }

} public class Intents {

    private static String createGoogleMapUri(double latitude, double longitude) {
        return String.format(Locale.getDefault(), "https://www.google.co.jp/maps/?q=%g,%g", latitude, longitude);
    }

    public static Intent createStreetViewIntent(double latitude, double longitude) {
        // https://developers.google.com/maps/documentation/android-api/intents
        Uri uri = Uri.parse(String.format(Locale.getDefault(), "google.streetview:cbll=%g,%g",latitude, longitude));
        return new Intent(Intent.ACTION_VIEW, uri);
    }

    public static Intent createShareTextIntent(CharSequence title, CharSequence text) {
        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType("text/plain");
        intent.putExtra(Intent.EXTRA_TEXT, text);
        return Intent.createChooser(intent, title);
    }

    public static Intent createShareLocationMemoIntent(Context context, LocationMemo memo) {
        StringBuilder textToShare = new StringBuilder();
        textToShare.append(memo.address);
        textToShare.append('\n');
        if (memo.note.length() > 0) {
            textToShare.append(memo.note);
            textToShare.append('\n');
        }

        textToShare.append(createGoogleMapUri(memo.latitude, memo.longitude));
        return createShareTextIntent(context.getString(R.string.share_location_memo_title, memo.address), textToShare);
    }

    public static Intent createOpenWithMapIntent(LocationMemo memo) {
        Uri gmmIntentUri = Uri.parse(String.format(Locale.getDefault(),
                "geo:%g,%g?q=%s",memo.latitude, memo.longitude, Uri.encode(memo.address)));
        Intent intent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
        //intent.setPackage("com.google.android.apps.maps");
        return intent;
    }
} public class Locations {

    public static final LatLng NOWHERE = new LatLng(0, 0);

    public static boolean isSomewhere(double latitude, double longitude) {
        return latitude > 0 && longitude > 0;
    }

    public static boolean isNowhere(double latitude, double longitude) {
        return !isSomewhere(latitude, longitude);
    }

    public static boolean isSomewhere(@NonNull LatLng latLng) {
        return isSomewhere(latLng.latitude, latLng.longitude);
    }

    public static boolean isNowhere(@NonNull LatLng latLng) {
        return !isSomewhere(latLng);
    }

    public static LatLng createLatLng(Location location) {
        return new LatLng(location.getLatitude(), location.getLongitude());
    }

    public static LatLng createLatLng(Address address) {
        return new LatLng(address.getLatitude(), address.getLongitude());
    }
} public class MarkerHueAllocator {

    static final String kMarkerHue = "marker_hue";

    static float INITIAL_HUE = BitmapDescriptorFactory.HUE_RED;

    final Prefs prefs;

    @Inject
    public MarkerHueAllocator(Prefs prefs) {
        this.prefs = prefs;
    }

    public void reset() {
        prefs.put(kMarkerHue, INITIAL_HUE);
    }

    public float allocate() {
        float hue = prefs.get(kMarkerHue, INITIAL_HUE);
        prefs.put(kMarkerHue, (hue + 30.0f) % 360.0f);
        return hue;
    }
}
import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.Tracker;
import com.google.android.gms.maps.model.LatLng;
import com.cookpad.android.rxt4a.schedulers.AndroidSchedulers;
import com.github.gfx.hankei_n.R;
import com.github.gfx.hankei_n.databinding.DialogEditLocationMemoBinding;
import com.github.gfx.hankei_n.dependency.DependencyContainer;
import com.github.gfx.hankei_n.event.LocationMemoAddedEvent;
import com.github.gfx.hankei_n.event.LocationMemoRemovedEvent;
import com.github.gfx.hankei_n.model.AddressAutocompleAdapter;
import com.github.gfx.hankei_n.model.LocationMemo;
import com.github.gfx.hankei_n.model.LocationMemoManager;
import com.github.gfx.hankei_n.model.PlaceEngine;
import com.github.gfx.hankei_n.toolbox.Assets;
import com.github.gfx.hankei_n.toolbox.Intents;
import com.github.gfx.hankei_n.toolbox.Locations;
import com.github.gfx.hankei_n.toolbox.MarkerHueAllocator;
import com.jakewharton.rxbinding.widget.RxTextView;
import com.jakewharton.rxbinding.widget.TextViewAfterTextChangeEvent;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.BottomSheetBehavior;
import android.support.design.widget.BottomSheetDialogFragment;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.Toast;
import java.util.Locale;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.inject.Inject;
import rx.functions.Action1;
import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject;
import timber.log.Timber;
package com.github.gfx.hankei_n.fragment;







@ParametersAreNonnullByDefault
public class EditLocationMemoFragment extends BottomSheetDialogFragment {

    static final String TAG = EditLocationMemoFragment.class.getSimpleName();

    static final String kLocationMemo = "location_memo";

    @Inject
    LocationMemoManager memos;

    @Inject
    MarkerHueAllocator markerHueAllocator;

    @Inject
    PlaceEngine placeEngine;

    @Inject
    Assets assets;