GoogleApiClient inside the recycleViewAdapter?

0

The app has a fragment with recyclerview and FAB . Clicking FAB opens a dialog where the user enters addresses, among other information.

The address input fields are of type AutoCompleteTextView and receive address suggestions as the user enters the information.

This was done using GoogleApiClient with GEO_DATA_API , according to the following code:

public class RecyclerFragment extends Fragment
        implements AlertDialog.OnClickListener,
        GoogleApiClient.OnConnectionFailedListener,
        GoogleApiClient.ConnectionCallbacks {

    private static final String TAG = "RecyclerFragment";
    RecyclerView recyclerView;
    RunDbHelper runDbHelper;
    RecyclerViewAdapter recyclerViewAdapter;
    private OnOkButtonListener mCallback;

    private GoogleApiClient mGoogleApiClient;
    private PlaceArrayAdapter mPlaceArrayAdapter;

    private static final int GOOGLE_API_CLIENT_ID = 0;

    private static final LatLngBounds BOUNDS_MOUNTAIN_VIEW = new LatLngBounds(
            new LatLng(37.398160, -122.180831), new LatLng(37.430610, -121.972090));
    private static LatLngBounds BOUNDS_AUTOCOMPLETE;

    public RecyclerFragment() {
        this.mCallback = null;
    }

    public void setAutocompleteBounds(LatLng centerLatLng){
        if (centerLatLng!= null && !centerLatLng.toString().isEmpty()){
            Double latSW, lngSW, lngNE, latNE;
            latSW = centerLatLng.latitude-.5;
            lngSW = centerLatLng.latitude-.5;
            lngNE = centerLatLng.latitude+.5;
            latNE = centerLatLng.latitude+.5;
            BOUNDS_AUTOCOMPLETE = new LatLngBounds(
                    new LatLng(latSW, lngSW), new LatLng(latNE, lngNE));
        } else {
            BOUNDS_AUTOCOMPLETE = new LatLngBounds(
                    new LatLng(37.398160, -122.180831), new LatLng(37.430610, -121.972090));
        }
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.fragment_recycler, container, false);
        try {
            mGoogleApiClient = new GoogleApiClient.Builder(getContext())
                    .addApi(Places.GEO_DATA_API)
                    .enableAutoManage(getActivity(), GOOGLE_API_CLIENT_ID, this)
                    .addConnectionCallbacks(this)
                    .build();
        } catch ( Exception e) {
            Log.i(TAG, "onCreateView: " + e);
        }

        mPlaceArrayAdapter = new PlaceArrayAdapter(getContext(), android.R.layout.simple_list_item_1,
                BOUNDS_AUTOCOMPLETE, null);

        runDbHelper = RunDbHelper.getInstance(getContext());
        List<RunData> mList = runDbHelper.getAllRuns();
        recyclerViewAdapter = new RecyclerViewAdapter(getContext(), mList);
        recyclerView = (RecyclerView) view.findViewById(R.id.rvRunList);
        recyclerView.setHasFixedSize(true);
        recyclerView.setAdapter(recyclerViewAdapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

        FloatingActionButton fab = (FloatingActionButton) view.findViewById(R.id.fabAdd);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Data entry dialog to add runs
                dialogInsertRun();
            }
        });     
        return view;
    }

    public void dialogInsertRun() {

        // Get the Activity for layout inflater as this dialog runs inside a fragment
        LayoutInflater inflater = LayoutInflater.from(getActivity());
        final View view = inflater.inflate(R.layout.dialog_new_run, null);

        // Dialog Builder
        AlertDialog.Builder addRunDialog = new AlertDialog.Builder(getActivity());
        addRunDialog.setTitle(R.string.dialog_insert_run_title)
                .setView(view);

        // Data entry field objects
        final AutoCompleteTextView collectAddressACTV = (AutoCompleteTextView) view.findViewById(R.id.actv_new_collect_address);
        final EditText collectPersonEditText = (EditText) view.findViewById(R.id.new_collect_person);

        collectAddressACTV.setThreshold(3);
        collectAddressACTV.setOnItemClickListener(mAutocompleteClickListener);
        collectAddressACTV.setAdapter(mPlaceArrayAdapter);

        addRunDialog.setPositiveButton(R.string.button_positive, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int id) {
                RunData runData = new RunData();
                runData.collect_person = getStringOrEmpty(collectPersonEditText);
                runData.collect_address = getStringOrEmpty(collectAddressACTV);

                if (!(runData.collect_address.isEmpty() && runData.delivery_address.isEmpty())) {

                    runData = runDbHelper.insertRun(runData, getActivity());
                    if (runData != null) {                          
                        recyclerViewAdapter = new RecyclerViewAdapter(getActivity(), runDbHelper.getAllRuns());
                        recyclerView.setAdapter(recyclerViewAdapter);
                        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
                        mCallback.addMarkersToMap(runData);
                    }
                } else {
                    Toast.makeText(getActivity(), R.string.dialog_insert_run_toast_nowhere, Toast.LENGTH_LONG).show();
                }
            }
        });

        addRunDialog.setNegativeButton(R.string.button_negative, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                dialog.cancel();
            }
        });

        addRunDialog.create();
        addRunDialog.show();
    }

    private String getStringOrEmpty(EditText editText) {
        String mString = editText.getText().toString();
        mString = (mString == null || mString.isEmpty() ? "" : mString);
        return mString;
    }


    @Override
    public void onConnected(@Nullable Bundle bundle) {
        mPlaceArrayAdapter.setGoogleApiClient(mGoogleApiClient);
        Log.i(TAG, "Google Places API connected.");
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        Log.e(TAG, "Google Places API connection failed with error code: "
                + connectionResult.getErrorCode());

        Toast.makeText(getContext(),
                "Google Places API connection failed with error code:" +
                        connectionResult.getErrorCode(),
                Toast.LENGTH_LONG).show();
    }

    @Override
    public void onConnectionSuspended(int i) {
        mPlaceArrayAdapter.setGoogleApiClient(null);
        Log.e(TAG, "Google Places API connection suspended.");
    }

    private AdapterView.OnItemClickListener mAutocompleteClickListener
            = new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            final PlaceArrayAdapter.PlaceAutocomplete item = mPlaceArrayAdapter.getItem(position);
            final String placeId = String.valueOf(item.placeId);
            Log.i(TAG, "Selected: " + item.description);
            PendingResult<PlaceBuffer> placeResult = Places.GeoDataApi
                    .getPlaceById(mGoogleApiClient, placeId);
            placeResult.setResultCallback(mUpdatePlaceDetailsCallback);
            Log.i(TAG, "Fetching details for ID: " + item.placeId);
        }
    };

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        if(mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
            mGoogleApiClient.stopAutoManage(getActivity());
            mGoogleApiClient.disconnect();
        }
    }   

    private ResultCallback<PlaceBuffer> mUpdatePlaceDetailsCallback
            = new ResultCallback<PlaceBuffer>() {
        @Override
        public void onResult(PlaceBuffer places) {
            if (!places.getStatus().isSuccess()) {
                Log.e(TAG, "Place query did not complete. Error: " +
                        places.getStatus().toString());
                return;
            }
            // Selecting the first object buffer.

        }
    };
}

After entering the information, they go to the database and pro recyclerview, forming a list of cardviews.

Each cardview has an edit button. At the touch of a button, a dialog opens with the form. editing. At this time, I'd like to use the same address suggestion feature.

All editing is ready. Only this GoogleApiClient will work ...

The recyclerview adapter code is this:

class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder>
        implements AlertDialog.OnClickListener,
        GoogleApiClient.OnConnectionFailedListener,
        GoogleApiClient.ConnectionCallbacks{

    private static final String TAG = "RecyclerViewAdapter";
    private static String mCollect, mDelivery, mRunEnded, mUnknownDestiny, mIndeterminate;

    private GoogleApiClient mGoogleApiClient;
    private PlaceArrayAdapter mPlaceArrayAdapter;

    private static final int GOOGLE_API_CLIENT_ID = 1;
    private Context context;
    private List<RunData> dataList = new ArrayList<>();
    private LayoutInflater inflater;
    private RunDbHelper runDbHelper;

    private static final LatLngBounds BOUNDS_MOUNTAIN_VIEW = new LatLngBounds(
            new LatLng(37.398160, -122.180831), new LatLng(37.430610, -121.972090));
    private static LatLngBounds BOUNDS_AUTOCOMPLETE;

    RecyclerViewAdapter(Context context, List<RunData> dataList1) {

        mCollect = context.getString(R.string.str_collect).trim();
        mDelivery = context.getString(R.string.str_delivery).trim();
        mIndeterminate = context.getString(R.string.str_indeterminate);
        mRunEnded = context.getString(R.string.this_run_ended);
        mUnknownDestiny = context.getString(R.string.unknown_destiny);

        this.context = context;
        this.dataList = dataList1;
        this.runDbHelper = RunDbHelper.getInstance(this.context);
        inflater = LayoutInflater.from(context);
    }

    @Override
    public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.fragment_recycler_row, parent, false);
        try {
            mGoogleApiClient = new GoogleApiClient.Builder(context)
                    .addApi(Places.GEO_DATA_API)
                    .enableAutoManage(XXXXXXXXXXXXXXXXXX, GOOGLE_API_CLIENT_ID, this) // nada do que tentei colocar aqui no lugar do XXXXXXX funcionou!
                    .addConnectionCallbacks(this)
                    .build();
        } catch ( Exception e) {
            Log.i(TAG, "onCreateView: " + e);
        }

        mPlaceArrayAdapter = new PlaceArrayAdapter(context, android.R.layout.simple_list_item_1,
                BOUNDS_AUTOCOMPLETE, null);

        return new RecyclerViewHolder(view);
    }


    // Parse data from dataList to holder and setup all Views
    // Here is part of the magic
    @Override
    public void onBindViewHolder(final RecyclerViewHolder holder, final int position) {

        // holding regular views
        holder.runID.setText(dataList.get(position).run_id);

        holder.collectPerson.setText(dataList.get(position).collect_person);
        holder.collectAddress.setText(dataList.get(position).collect_address);

        // this booleans help set some views on/off accordingly
        Boolean collected = dataList.get(position).parcel_collected.equals("1");
        Boolean collectAddressIsEmpty = dataList.get(position).collect_address.isEmpty();

        holder.ivEdit.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                RunData runData = new RunData();
                runData.run_id = holder.runID.getText().toString();              
                runData.collect_person = holder.collectPerson.getText().toString();
                runData.collect_address = holder.collectAddress.getText().toString();       
                dialogEditRun(runData, position);
            }
        });    
    }

    String makeSureLatLng(String lat, String lng) {
        return (lat == null || lng == null || lat.isEmpty() || lng.isEmpty() ? null : lat + "," + lng);
    }

    boolean isUsable(TextView textView) {
        return (!(textView == null || textView.getText().toString().isEmpty()));
    }

    @Override
    public int getItemCount() {
        return dataList.size();
    }

    public void dialogEditRun(RunData runData, int position) {

        // Get the Activity for layout inflater as this dialog runs inside a fragment
        LayoutInflater inflater = LayoutInflater.from(context);
        final View inflaterView = inflater.inflate(R.layout.dialog_edit_run, null);
        // Data entry field objects
        final String mRunID;

        final AutoCompleteTextView collectAddressACTV = (AutoCompleteTextView) inflaterView.findViewById(R.id.actv_edit_collect_address);
        final EditText collectPersonEditText = (EditText) inflaterView.findViewById(R.id.edit_collect_person);
        final TextView parcel_collected = (TextView) inflaterView.findViewById(R.id.editDelivered);         

        mRunID = runData.run_id;        

        parcel_collected.setText(runData.parcel_collected);
        collectAddressACTV.setText(runData.collect_address);
        collectPersonEditText.setText(runData.collect_person);


        // Set directions into recyclerViewAdapter for autocomplete
        collectAddressACTV.setThreshold(3);
        collectAddressACTV.setOnItemClickListener(mAutocompleteClickListener);
        collectAddressACTV.setAdapter(mPlaceArrayAdapter);

        // Dialog Builder
        AlertDialog.Builder editRunDialog = new AlertDialog.Builder(context);
        editRunDialog.setTitle(R.string.dialog_update_run_title).setView(inflaterView);
        editRunDialog.setPositiveButton(R.string.button_positive, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int id) {

                RunData runData = new RunData();

                int position = (int) run_date.getTag();
                runData.run_id = mRunID; 
                runData.collect_person = getStringOrEmpty(collectPersonEditText);
                runData.collect_address = getStringOrEmpty(collectAddressACTV);                              

                if (!runData.collect_address.isEmpty() ){
                    // try to update, if success update recycler.
                    if (runDbHelper.updateRun(runData, context)){

                        // atualiza o recyclerview
                        dataList.remove(position);
                        notifyItemRemoved(position);
                        dataList.add(position,runData);
                        notifyItemRangeChanged(position, dataList.size());
                        notifyItemInserted(position);

                    } else {
                        Toast.makeText(context, "Not updated record", Toast.LENGTH_SHORT).show();
                    }
                } else {
                    Toast.makeText(context, R.string.dialog_insert_run_toast_nowhere, Toast.LENGTH_SHORT).show();
                }
            }

        });

        editRunDialog.setNegativeButton(R.string.button_negative, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                dialog.cancel();
            }
        });

        editRunDialog.create();
        editRunDialog.show();
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        mPlaceArrayAdapter.setGoogleApiClient(mGoogleApiClient);
        Log.i(TAG, "Google Places API connected.");
    }

    @Override
    public void onConnectionSuspended(int i) {
        mPlaceArrayAdapter.setGoogleApiClient(null);
        Log.e(TAG, "Google Places API connection suspended.");
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        Log.e(TAG, "Google Places API connection failed with error code: "
                + connectionResult.getErrorCode());

        Toast.makeText(context,
                "Google Places API connection failed with error code:" +
                        connectionResult.getErrorCode(),
                Toast.LENGTH_LONG).show();
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {

    }

    class RecyclerViewHolder extends RecyclerView.ViewHolder {
        TextView runID, collectPerson, collectAddress;
        TextView collectLat, collectLng;
        ImageView ivEdit;
        SwitchCompat swcCollect;

        RecyclerViewHolder(View rowView) {
            super(rowView);
            runID = (TextView) rowView.findViewById(R.id.runId);                
            ivEdit = (ImageView) rowView.findViewById(R.id.ivEdit);
            collectPerson = (TextView) rowView.findViewById(R.id.collectPerson);
            collectAddress = (TextView) rowView.findViewById(R.id.collectAddress);
        }
    }

    private AdapterView.OnItemClickListener mAutocompleteClickListener
            = new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            final PlaceArrayAdapter.PlaceAutocomplete item = mPlaceArrayAdapter.getItem(position);
            final String placeId = String.valueOf(item.placeId);
            Log.i(TAG, "Selected: " + item.description);
            PendingResult<PlaceBuffer> placeResult = Places.GeoDataApi
                    .getPlaceById(mGoogleApiClient, placeId);
            placeResult.setResultCallback(mUpdatePlaceDetailsCallback);
            Log.i(TAG, "Fetching details for ID: " + item.placeId);
        }
    };

    private ResultCallback<PlaceBuffer> mUpdatePlaceDetailsCallback
            = new ResultCallback<PlaceBuffer>() {
        @Override
        public void onResult(PlaceBuffer places) {
            if (!places.getStatus().isSuccess()) {
                Log.e(TAG, "Place query did not complete. Error: " +
                        places.getStatus().toString());
                return;
            }
            // Selecting the first object buffer.
        }
    };
}

I tried to follow the RecyclerFragment template, but something is wrong and I can not instantiate GoogleApiClient in onCreateView method.

Note that I was using Google Places WebService and with this API it works, but it should not be used in app in production, so I'm switching to Google Places API for Android.

Tomara, can you help me.

    
asked by anonymous 03.02.2017 / 01:12

1 answer

1

After much searching, including SOEN, I found the answer in the documentation. Since there was no attempt to respond and not even a comment, I think I will report what I found because it worked and you may find yourself in a similar situation.

The problem is in this part:

mGoogleApiClient = new GoogleApiClient.Builder(getContext())
                .addApi(Places.GEO_DATA_API)
                .enableAutoManage(getActivity(), GOOGLE_API_CLIENT_ID, this)
                .addConnectionCallbacks(this)
                .build();

Specifically, in this method:

.enableAutoManage(getActivity(), GOOGLE_API_CLIENT_ID, this)

As long as it eases the management of the life cycle of this API client, just like Highlander, there can only be one and so it could not use the second time.

The solution was to use this variation:

    mGoogleApiClient = new GoogleApiClient.Builder(context)
            .addApi(Places.GEO_DATA_API)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build();

In addition, you need to connect and disconnect the client. This is done by calling the connect() and disconnect() methods at the appropriate places and onCreate() and onDestroy() events of the activity or fragment.

    
07.02.2017 / 23:37