how do i implement android mapbox android sdk successfully in fragment - mapbox

I am using mapbox in a fragment with bottom navigation, when i exit and resume the app or when i change tabs rapidly, the app crashes. this is the error i get
10-07 22:20:36.046 21867-21886/com.dropexpress.driver.dropexpressdriver E/Mbgl-FileSource: Failed to read the storage key:
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.os.Bundle.getBoolean(java.lang.String, boolean)' on a null object reference
at com.mapbox.mapboxsdk.storage.FileSource.getCachePath(FileSource.java:88)
at com.mapbox.mapboxsdk.storage.FileSource$FileDirsPathsTask.doInBackground(FileSource.java:165)
at com.mapbox.mapboxsdk.storage.FileSource$FileDirsPathsTask.doInBackground(FileSource.java:155)
at android.os.AsyncTask$2.call(AsyncTask.java:304)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
the below code always fails when i exit the app and resume, or when i change tabs rapidly, i am using bottom navigation.
Steps to reproduce
here is my fragment code
package com.dropexpress.driver.dropexpressdriver.fragments;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.dropexpress.driver.dropexpressdriver.R;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.maps.MapView;
public class HomeFragment extends Fragment {
private MapView mapView;
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
private String mParam1;
private String mParam2;
private OnFragmentInteractionListener mListener;
public HomeFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #param param1 Parameter 1.
* #param param2 Parameter 2.
* #return A new instance of fragment HomeFragment.
*/
// TODO: Rename and change types and number of parameters
public static HomeFragment newInstance(String param1, String param2) {
HomeFragment fragment = new HomeFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_home, container, false);
Mapbox.getInstance(requireActivity(), "pk.eyJ1Ijoic3ludGF4bHRkIiwiYSI6ImNqaDJxNnhzbDAwNnMyeHF3dGlqODZsYjcifQ.pcz6BWpzCHeZ6hQg4AH9ww");
mapView = (MapView) view.findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
return view;
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
#Override
public void onResume() {
super.onResume();
mapView.onResume();
}
#Override
public void onPause() {
super.onPause();
mapView.onPause();
}
#Override
public void onStop() {
super.onStop();
mapView.onStop();
}
#Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}
Android versions: 5.0 +
Device models: motorola g5
Mapbox SDK versions: 6.5.0

Put your Mapbox.getInstance before inflating your layout.
Mapbox.getInstance(requireActivity(),"Your Map Key");
View view = inflater.inflate(R.layout.fragment_home, container, false);
mapView = (MapView) view.findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
I hope this helps you.

i opened this issue in github, you check their response here
https://github.com/mapbox/mapbox-gl-native/issues/13044#issuecomment-427861016
this was their response:
I can see 2 issues with the provided code:
You are not calling MapView#onDestroy, this has to be called from you fragment's #onDestroyView.
MapView#onCreate should be called from fragment's #onViewCreatedinstead of #onCreateView.
I applied the changes and it worked!

Related

Change 2nd parameter of method 'PlaceAutocompleteAdapter' from 'GeoDataClient' to 'GoogleApiClient'

i ma making AutoCompleteTextView in my adapter for which when i make object of PlaceAutocompleteAdapter when i pass GoogleApiClient object as second parameter to my PlaceAutocompleteAdapter it gives me red error and sys to pass GEODataClient object instead of GoogleApiClient
while the code for my Fragment containing map is this
package com.example.anonymous.ghar_ka_khana;
import android.app.FragmentManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.app.Fragment;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.places.Places;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import android.Manifest;
import android.widget.AutoCompleteTextView;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.Executor;
public class GoogleFragment extends Fragment implements OnMapReadyCallback,LocationListener,GoogleApiClient.OnConnectionFailedListener {
private MapFragment m;
private GoogleMap mMap;
FusedLocationProviderClient fusedLocationProviderClient;
private int REQUEST_PERMISSIONS_REQUEST_CODE = 125;
protected Location mLastLocation;
private AutoCompleteTextView autoCompleteTextView;
private PlaceAutocompleteAdapter autocompleteAdapter;
private String mLatitiudelabel;
private String mLongitudelabel;
private GoogleApiClient mGoogleApiClient;
private static LatLngBounds Lat_Long_Bounds = new LatLngBounds(new LatLng(-40,-168),new LatLng(71,136));
View v;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
Log.i("pahlywala", "onCreateView");
v = inflater.inflate(R.layout.fragment_google, container, false);
mGoogleApiClient = new GoogleApiClient
.Builder(getActivity())
.addApi(Places.GEO_DATA_API)
.addApi(Places.PLACE_DETECTION_API)
.enableAutoManage(new DrawerActivity(), this)
.build();
autoCompleteTextView = (AutoCompleteTextView)v.findViewById(R.id.mapsearch);
autocompleteAdapter = new PlaceAutocompleteAdapter(getActivity(),mGoogleApiClient,Lat_Long_Bounds,null);
autoCompleteTextView.setAdapter(autocompleteAdapter);
return v;
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mLatitiudelabel = "Latitude";
mLongitudelabel = "Longitude";
FragmentManager fragmentManager = getActivity().getFragmentManager();
final MapFragment myMapFragment = (MapFragment) fragmentManager.findFragmentById(R.id.googlemap);
myMapFragment.getMapAsync(this);
Log.i("pahlywalass", "onViewCreated");
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(getActivity());
}
#Override
public void onMapReady(GoogleMap googleMap)
{
mMap=googleMap;
mMap.addMarker(new MarkerOptions()
.position(new LatLng(0, 0))
.title("Marker"));
}
#Override
public void onLocationChanged(Location location) {
}
#Override
public void onStart() {
super.onStart();
if (!checkPermissions()) {
requestPermissions();
} else {
getLastLocation();
}
}
private boolean checkPermissions() {
int permissionState = ActivityCompat.checkSelfPermission(getActivity(),
Manifest.permission.ACCESS_FINE_LOCATION);
return permissionState == PackageManager.PERMISSION_GRANTED;
}
private void requestPermissions() {
boolean shouldProvideRationale =
ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),
Manifest.permission.ACCESS_FINE_LOCATION);
// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i("displaying permission", "Displaying permission rationale to provide additional context.");
/* showSnackbar("warning", android.R.string.ok,
new View.OnClickListener() {
#Override
public void onClick(View view) {
// Request permission
startLocationPermissionRequest();
}
});*/
} else {
Log.i("requesting permission", "Requesting permission");
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
startLocationPermissionRequest();
}
}
private void startLocationPermissionRequest() {
ActivityCompat.requestPermissions(new DrawerActivity(),
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
Log.i("onRequestPerm", "onRequestPermissionResult");
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
if (grantResults.length <= 0) {
// If user interaction was interrupted, the permission request is cancelled and you
// receive empty arrays.
Log.i("onRequestPerm", "User interaction was cancelled.");
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted.
getLastLocation();
} else {
// Permission denied.
// Notify the user via a SnackBar that they have rejected a core permission for the
// app, which makes the Activity useless. In a real app, core permissions would
// typically be best requested during a welcome-screen flow.
// Additionally, it is important to remember that a permission might have been
// rejected without asking the user for permission (device policy or "Never ask
// again" prompts). Therefore, a user interface affordance is typically implemented
// when permissions are denied. Otherwise, your app could appear unresponsive to
// touches or interactions which have required permissions.
/*showSnackbar(R.string.textwarn, R.string.settings,
new View.OnClickListener() {
#Override
public void onClick(View view) {
// Build intent that displays the App settings screen.
Intent intent = new Intent();
intent.setAction(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package",
BuildConfig.APPLICATION_ID, null);
intent.setData(uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
});*/
}
}
}
private void getLastLocation() {
if (ActivityCompat.checkSelfPermission(getActivity(), android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getActivity(), android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
fusedLocationProviderClient.getLastLocation()
.addOnCompleteListener(getActivity(), new OnCompleteListener<Location>() {
#Override
public void onComplete(#NonNull Task<Location> task) {
if (task.isSuccessful() && task.getResult() != null) {
mLastLocation = task.getResult();
Log.i("Longitutde", mLastLocation.getLongitude()+"");
Log.i("Longitutde", mLastLocation.getLatitude()+"");
LatLng cur_Latlng=new LatLng(mLastLocation.getLatitude(),mLastLocation.getLongitude()); // giving your marker to zoom to your location area.
mMap.addMarker(new MarkerOptions()
.position(cur_Latlng)
.title("umair"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(cur_Latlng));
mMap.animateCamera(CameraUpdateFactory.zoomTo(12));
Geocoder geocoder;
List<Address> addresses = null;
geocoder = new Geocoder(getActivity(), Locale.getDefault());
try {
addresses = geocoder.getFromLocation(mLastLocation.getLatitude(),mLastLocation.getLongitude(), 1); // Here 1 represent max location result to returned, by documents it recommended 1 to 5
} catch (IOException e) {
e.printStackTrace();
}
String address = addresses.get(0).getAddressLine(0); // If any additional address line present than only, check with max available address lines by getMaxAddressLineIndex()
String city = addresses.get(0).getLocality();
/*String state = addresses.get(0).getAdminArea();
String country = addresses.get(0).getCountryName();
String postalCode = addresses.get(0).getPostalCode();
String knownName = addresses.get(0).getFeatureName();*/
Log.i("pat", ""+city);
}
else {
Log.i("getlasts", "getLastLocation:exception", task.getException());
}
}
});
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
}
while the code for PlaceAutoCompleteAdapter is following
package com.example.anonymous.ghar_ka_khana;
/**
* Created by Anonymous on 2/19/2018.
*/
import android.content.Context;
import android.graphics.Typeface;
import android.text.style.CharacterStyle;
import android.text.style.StyleSpan;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.common.data.DataBufferUtils;
import com.google.android.gms.location.places.AutocompleteFilter;
import com.google.android.gms.location.places.AutocompletePrediction;
import com.google.android.gms.location.places.AutocompletePredictionBufferResponse;
import com.google.android.gms.location.places.GeoDataClient;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.tasks.RuntimeExecutionException;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.Tasks;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Adapter that handles Autocomplete requests from the Places Geo Data Client.
* {#link AutocompletePrediction} results from the API are frozen and stored directly in this
* adapter. (See {#link AutocompletePrediction#freeze()}.)
*/
public class PlaceAutocompleteAdapter
extends ArrayAdapter<AutocompletePrediction> implements Filterable {
private static final String TAG = "PlaceAutocompleteAd";
private static final CharacterStyle STYLE_BOLD = new StyleSpan(Typeface.BOLD);
/**
* Current results returned by this adapter.
*/
private ArrayList<AutocompletePrediction> mResultList;
/**
* Handles autocomplete requests.
*/
private GeoDataClient mGeoDataClient;
/**
* The bounds used for Places Geo Data autocomplete API requests.
*/
private LatLngBounds mBounds;
/**
* The autocomplete filter used to restrict queries to a specific set of place types.
*/
private AutocompleteFilter mPlaceFilter;
/**
* Initializes with a resource for text rows and autocomplete query bounds.
*
* #see android.widget.ArrayAdapter#ArrayAdapter(android.content.Context, int)
*/
public PlaceAutocompleteAdapter(Context context, GeoDataClient geoDataClient,
LatLngBounds bounds, AutocompleteFilter filter) {
super(context, android.R.layout.simple_expandable_list_item_2, android.R.id.text1);
mGeoDataClient = geoDataClient;
mBounds = bounds;
mPlaceFilter = filter;
}
/**
* Sets the bounds for all subsequent queries.
*/
public void setBounds(LatLngBounds bounds) {
mBounds = bounds;
}
/**
* Returns the number of results received in the last autocomplete query.
*/
#Override
public int getCount() {
return mResultList.size();
}
/**
* Returns an item from the last autocomplete query.
*/
#Override
public AutocompletePrediction getItem(int position) {
return mResultList.get(position);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = super.getView(position, convertView, parent);
// Sets the primary and secondary text for a row.
// Note that getPrimaryText() and getSecondaryText() return a CharSequence that may contain
// styling based on the given CharacterStyle.
AutocompletePrediction item = getItem(position);
TextView textView1 = (TextView) row.findViewById(android.R.id.text1);
TextView textView2 = (TextView) row.findViewById(android.R.id.text2);
textView1.setText(item.getPrimaryText(STYLE_BOLD));
textView2.setText(item.getSecondaryText(STYLE_BOLD));
return row;
}
/**
* Returns the filter for the current set of autocomplete results.
*/
#Override
public Filter getFilter() {
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
// We need a separate list to store the results, since
// this is run asynchronously.
ArrayList<AutocompletePrediction> filterData = new ArrayList<>();
// Skip the autocomplete query if no constraints are given.
if (constraint != null) {
// Query the autocomplete API for the (constraint) search string.
filterData = getAutocomplete(constraint);
}
results.values = filterData;
if (filterData != null) {
results.count = filterData.size();
} else {
results.count = 0;
}
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results != null && results.count > 0) {
// The API returned at least one result, update the data.
mResultList = (ArrayList<AutocompletePrediction>) results.values;
notifyDataSetChanged();
} else {
// The API did not return any results, invalidate the data set.
notifyDataSetInvalidated();
}
}
#Override
public CharSequence convertResultToString(Object resultValue) {
// Override this method to display a readable result in the AutocompleteTextView
// when clicked.
if (resultValue instanceof AutocompletePrediction) {
return ((AutocompletePrediction) resultValue).getFullText(null);
} else {
return super.convertResultToString(resultValue);
}
}
};
}
/**
* Submits an autocomplete query to the Places Geo Data Autocomplete API.
* Results are returned as frozen AutocompletePrediction objects, ready to be cached.
* Returns an empty list if no results were found.
* Returns null if the API client is not available or the query did not complete
* successfully.
* This method MUST be called off the main UI thread, as it will block until data is returned
* from the API, which may include a network request.
*
* #param constraint Autocomplete query string
* #return Results from the autocomplete API or null if the query was not successful.
* #see GeoDataClient#getAutocompletePredictions(String, LatLngBounds, AutocompleteFilter)
* #see AutocompletePrediction#freeze()
*/
private ArrayList<AutocompletePrediction> getAutocomplete(CharSequence constraint) {
Log.i(TAG, "Starting autocomplete query for: " + constraint);
// Submit the query to the autocomplete API and retrieve a PendingResult that will
// contain the results when the query completes.
Task<AutocompletePredictionBufferResponse> results =
mGeoDataClient.getAutocompletePredictions(constraint.toString(), mBounds,
mPlaceFilter);
// This method should have been called off the main UI thread. Block and wait for at most
// 60s for a result from the API.
try {
Tasks.await(results, 60, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException e) {
e.printStackTrace();
}
try {
AutocompletePredictionBufferResponse autocompletePredictions = results.getResult();
Log.i(TAG, "Query completed. Received " + autocompletePredictions.getCount()
+ " predictions.");
// Freeze the results immutable representation that can be stored safely.
return DataBufferUtils.freezeAndClose(autocompletePredictions);
} catch (RuntimeExecutionException e) {
// If the query did not complete successfully return null
Toast.makeText(getContext(), "Error contacting API: " + e.toString(),
Toast.LENGTH_SHORT).show();
Log.e(TAG, "Error getting autocomplete prediction API call", e);
return null;
}
}
}
my gradle file is following
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.example.anonymous.ghar_ka_khana"
minSdkVersion 17
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
productFlavors {
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
implementation 'com.google.firebase:firebase-database:11.8.0'
//11.0.4
implementation 'com.android.support:support-v4:26.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
implementation 'com.google.android.gms:play-services:11.8.0'
implementation 'com.firebaseui:firebase-ui-auth:3.1.3'
// FirebaseUI for Firebase Auth
implementation 'com.firebaseui:firebase-ui-database:3.1.3'
// FirebaseUI for Firebase Realtime Database
implementation 'com.firebaseui:firebase-ui-firestore:3.1.3'
// FirebaseUI for Cloud Firestore
compile 'com.google.firebase:firebase-firestore:11.8.0'
//Firestore dependency
implementation 'com.firebaseui:firebase-ui-storage:3.1.3'
// FirebaseUI for Cloud Storage
implementation 'com.facebook.android:facebook-login:4.29.0'
// Required only if Facebook login support is required
implementation 'de.hdodenhof:circleimageview:2.2.0'
implementation 'com.github.florent37:materialtextfield:1.0.7'
compile 'com.scalified:fab:1.1.3'
compile 'com.google.firebase:firebase-storage:11.8.0'
compile 'com.google.firebase:firebase-auth:11.8.0'
compile 'com.github.glomadrian:Grav:1.1'
implementation('com.mikepenz:materialdrawer:6.0.6#aar') {
transitive = true
}
compile 'com.wdullaer:materialdatetimepicker:3.5.1'
implementation 'com.android.support:design:26.1.0'
implementation 'com.android.support:recyclerview-v7:26.1.0'
implementation 'com.android.support:support-annotations:26.1.0'
implementation 'com.google.android.gms:play-services:11.8.0'
implementation 'com.google.android.gms:play-services-location:11.8.0'
}
apply plugin: 'com.google.gms.google-services'
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
def requested = details.requested
if (requested.group == 'com.android.support') {
if (!requested.name.startsWith("multidex")) {
details.useVersion '26.1.0'
}
}
}
}
Ok. I've got the solution instead of using:
autocompleteAdapter = new PlaceAutocompleteAdapter(getActivity(),mGoogleApiClient,Lat_Long_Bounds,null);
you will have to use:
autocompleteAdapter = new PlaceAutocompleteAdapter(getActivity(),Places.getGeoDataClient(getActivity(), null),Lat_Long_Bounds,null);

ActionBarActivity with fragment and Tab Bar how to implement it

As you see my Code, it dont have error when run debug, I put the tabhost into my fragment, but when app run. It dont show the tabhost
package com.example.phamxuanson.myapplication;
import android.app.Activity;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.support.v4.widget.DrawerLayout;
import android.widget.ArrayAdapter;
import android.widget.TextView;
public class MainActivity extends ActionBarActivity
implements NavigationDrawerFragment.NavigationDrawerCallbacks {
/**
* Fragment managing the behaviors, interactions and presentation of the navigation drawer.
*/
private NavigationDrawerFragment mNavigationDrawerFragment;
/**
* Used to store the last screen title. For use in {#link #restoreActionBar()}.
*/
private CharSequence mTitle;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
// Set up the drawer.
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
mTitle = getTitle();
}
#Override
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, PlaceholderFragment.newInstance(position + 1))
.commit();
}
public void onSectionAttached(int number) {
switch (number) {
case 1:
mTitle = getString(R.string.title_section1);
break;
case 2:
mTitle = getString(R.string.title_section2);
break;
case 3:
mTitle = getString(R.string.title_section3);
break;
}
}
public void restoreActionBar() {
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setTitle(mTitle);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
if (!mNavigationDrawerFragment.isDrawerOpen()) {
// Only show items in the action bar relevant to this screen
// if the drawer is not showing. Otherwise, let the drawer
// decide what to show in the action bar.
getMenuInflater().inflate(R.menu.main, menu);
restoreActionBar();
return true;
}
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
/**
* The fragment argument representing the section number for this
* fragment.
*/
private static final String ARG_SECTION_NUMBER = "section_number";
/**
* Returns a new instance of this fragment for the given section
* number.
*/
public static PlaceholderFragment newInstance(int sectionNumber) {
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
return rootView;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
((MainActivity) activity).onSectionAttached(
getArguments().getInt(ARG_SECTION_NUMBER));
}
}
}
Please see my piuctures
i.imgur.com/AuPA4GO.png
i.imgur.com/Vlrxuhz.png
i.imgur.com/or8m6Mj.png
Why wasn’t tabhost shown?

Displaying a scrollable WebView on Google Glass

I'm trying to display a HTML page on the Google Glass. I've managed to do it but the page is not scrollable. Here is my code:
package com.example.dbe13509.testglass;
import com.google.android.glass.media.Sounds;
import com.google.android.glass.widget.CardBuilder;
import com.google.android.glass.widget.CardScrollAdapter;
import com.google.android.glass.widget.CardScrollView;
import android.app.Activity;
import android.content.Context;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.AdapterView;
import java.io.File;
/**
* An {#link Activity} showing a tuggable "Hello World!" card.
* <p>
* The main content view is composed of a one-card {#link CardScrollView} that provides tugging
* feedback to the user when swipe gestures are detected.
* If your Glassware intends to intercept swipe gestures, you should set the content view directly
* and use a {#link com.google.android.glass.touchpad.GestureDetector}.
* #see GDK Developer Guide
*/
public class MainActivity extends Activity {
/** {#link CardScrollView} to use as the main content view. */
private CardScrollView mCardScroller;
WebView web;
#Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
web = new WebView(this);
web.getSettings().setJavaScriptEnabled(true);
web.loadUrl("http://www.google.fr");
File lFile = new File(Environment.getExternalStorageDirectory() + "/DCIM/Camera/test.html");
web.loadUrl("file:///" + lFile.getAbsolutePath());
mCardScroller = new CardScrollView(this);
mCardScroller.setAdapter(new CardScrollAdapter() {
#Override
public int getCount() {
return 1;
}
#Override
public Object getItem(int position) {
return web;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
return web;
}
#Override
public int getPosition(Object item) {
if (web.equals(item)) {
return 0;
}
return AdapterView.INVALID_POSITION;
}
});
// Handle the TAP event.
mCardScroller.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Plays disallowed sound to indicate that TAP actions are not supported.
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
am.playSoundEffect(Sounds.DISALLOWED);
}
});
setContentView(web);
}
#Override
protected void onResume() {
super.onResume();
}
#Override
protected void onPause() {
super.onPause();
}
/**
* Builds a Glass styled "Hello World!" view using the {#link CardBuilder} class.
*/
private View buildView() {
CardBuilder card = new CardBuilder(this, CardBuilder.Layout.TEXT);
// card.setText(R.string.hello_world);
// web.loadUrl("http://www.google.fr");
return card.getView(web, null);
}
}
I tried to use the CardScrollView unsuccessfully. I think I need to make some custom class somewhere but I can't find documentation on that and I'm a beginner on Glass so if someone can help me...

My Custom CursorAdapter doesn't update my ListView

I'm having troubles with my Custom CursorAdapter and my ListView, the fact is, I can save data in my sqlite Database in my custom ContentProvider but my ListView is not populated.
I know DB Operations are heavy long operations, therefore I do it in another thread and furthermore CursorLoader is a subclass of AsyncTaskLoader, so it should be prepared for that.
With SimpleCursorAdapter works fine but with this Custom CursorAdapter not.
Can anyone tell me what is wrong and how could I solve it?
Thanks in advance.
My code is the following
public class TextNoteAdapter extends CursorAdapter {
/*********** Declare Used Variables *********/
private Cursor mCursor;
private Context mContext;
private static LayoutInflater mInflater=null;
/************* TextNoteAdapter Constructor *****************/
public TextNoteAdapter (Context context, Cursor cursor, int flags) {
super(context, cursor,flags);
mInflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mContext = context;
mCursor = cursor;
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
//LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = mInflater.inflate(R.layout.textnote_info, parent, false);
ViewHolder holder = new ViewHolder();
holder.note_name = (TextView)view.findViewById(R.id.note_name);
holder.creation_date = (TextView)view.findViewById(R.id.creation_date);
holder.modification_date = (TextView)view.findViewById(R.id.modification_date);
holder.label_creation_date = (TextView)view.findViewById(R.id.label_creation_date);
holder.label_modification_date = (TextView)view.findViewById(R.id.label_modification_date);
view.setTag(holder);
return view;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
// here we are setting our data what means, take the data from the cursor and put it in views
View vi = view;
ViewHolder holder = (ViewHolder)view.getTag();
if(view==null){
/****** Inflate textnote_info.xml file for each row ( Defined below ) *******/
vi = mInflater.inflate(R.layout.textnote_info, null);
/************ Set holder with LayoutInflater ************/
vi.setTag( holder );
} else
holder=(ViewHolder)vi.getTag();
/************ Set Model values in Holder elements ***********/
holder.note_name.setText(cursor.getString(cursor.getColumnIndex(TextNotesDb.KEY_NOTE_NAME)));
holder.creation_date.setText(cursor.getString(cursor.getColumnIndex(TextNotesDb.KEY_CREATION_DATE)));
holder.modification_date.setText(cursor.getString(cursor.getColumnIndex(TextNotesDb.KEY_MODIFICATION_DATE)));
holder.label_creation_date.setText(Constants.LABEL_CREATION_DATE);
holder.label_modification_date.setText(Constants.LABEL_MODIFICATION_DATE);
}
#Override
protected void onContentChanged() {
// TODO Auto-generated method stub
super.onContentChanged();
notifyDataSetChanged();
}
/********* Create a holder Class to contain inflated xml file elements *********/
public static class ViewHolder{
public TextView note_name;
public TextView creation_date;
public TextView modification_date;
public TextView label_creation_date;
public TextView label_modification_date;
}
}
And here my MainActivity
import android.app.Activity;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.CursorAdapter;
import android.widget.ListView;
import android.widget.Toast;
public class MainActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor>{
private Cursor cursor;
private Button addButton;
private ListView listView;
private TextNoteAdapter dataAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
displayListView();
addButton = (Button)findViewById(R.id.add_textnote);
addButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// starts a new Intent to add a Note
Intent intent = new Intent(getBaseContext(), TextNoteEdit.class);
Bundle bundle = new Bundle();
bundle.putString("mode", "add");
intent.putExtras(bundle);
startActivity(intent);
}
});
}
#Override
protected void onResume() {
super.onResume();
Log.i("TAG", "MainActivity:: onResume");
/** Starts a new or restarts an existing Loader in this manager **/
getLoaderManager().restartLoader(0, null, this);
}
private void displayListView() {
// That ensures a loader is initialized and active.
// If the loader specified by the ID already exists, the last created loader is reused.
// else initLoader() triggers the LoaderManager.LoaderCallbacks method onCreateLoader().
// This is where you implement the code to instantiate and return a new loader
getLoaderManager().initLoader(0, null, this);
// We get ListView from Layout and initialize
listView = (ListView) findViewById(R.id.textnote_list);
// DB takes long, therefore this operation should take place in a new thread!
new Handler().post(new Runnable() {
#Override
public void run() {
dataAdapter = new TextNoteAdapter(MainActivity.this, null, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
listView.setAdapter(dataAdapter);
Log.i("TAG", "MainActivity:: Handler... Run()");
}
});
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> listView, View view, int position, long id) {
/** Get the cursor, positioned to the corresponding row in the result set **/
Cursor cursor = (Cursor) listView.getItemAtPosition(position);
// display the selected note
String noteName = cursor.getString(cursor.getColumnIndexOrThrow(TextNotesDb.KEY_NOTE_NAME));
Toast.makeText(getApplicationContext(), noteName, Toast.LENGTH_SHORT).show();
String rowId = cursor.getString(cursor.getColumnIndexOrThrow(TextNotesDb.KEY_ROWID));
// starts a new Intent to update/delete a Textnote
// pass in row Id to create the Content URI for a single row
Intent intent = new Intent(getBaseContext(), TextNoteEdit.class);
Bundle bundle = new Bundle();
bundle.putString("mode", "update");
bundle.putString("rowId", rowId);
intent.putExtras(bundle);
startActivityForResult(intent, 1);
}
});
}
/** This is called when a new Loader needs to be created.**/
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Log.i("TAG", "MainActivity:: onCreateLoader");
String[] projection = {
TextNotesDb.KEY_ROWID,
TextNotesDb.KEY_GUID,
TextNotesDb.KEY_NOTE_NAME,
TextNotesDb.KEY_NOTE,
TextNotesDb.KEY_CREATION_DATE,
TextNotesDb.KEY_MODIFICATION_DATE
};
CursorLoader cursorLoader = new CursorLoader(this, MyContentProvider.CONTENT_URI, projection, null, null, null);
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
dataAdapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
dataAdapter.swapCursor(null);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
As in comment below my Question, I could solve it by adding 2 lines. Now it should look like this
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
dataAdapter.notifyDataSetChanged(); // <-
listView.setAdapter(dataAdapter); // <-
dataAdapter.swapCursor(data);
}

GWT - button inside v3 google maps infowindow

I am trying to figure out how to propagate events for components inside google maps InfoWindow.
I create anchor or a button and want to handle click event on any of those.
I have found solutions described here
and
here
but those both are using google maps wrappers for gwt.
I would like to avoid those libraries.
QUESTION:
Do you know any way how can I propagate those events from info window to some GWT panel which wraps google maps?
Based on code found here:
http://gwt-maps3.googlecode.com/svn/trunk/src/com/googlecode/maps3/client/
I have created this class that solves problem with using no external library (you have to take Only InfoWindowJSO source from link given)
And then instead passing InnerHtml as string to setContent... you just pass Widget element.
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;
public class InfoWindow
{
static class FakePanel extends ComplexPanel
{
public FakePanel(Widget w)
{
w.removeFromParent();
getChildren().add(w);
adopt(w);
}
#Override
public boolean isAttached()
{
return true;
}
public void detachWidget()
{
this.remove(0);
}
}
/** */
InfoWindowJSO jso;
/** If we have a widget, this will exist so we can detach later */
FakePanel widgetAttacher;
/** Keep track of this so we can get it again later */
Widget widgetContent;
/** */
public InfoWindow()
{
this.jso = InfoWindowJSO.newInstance();
}
/** */
public InfoWindow(InfoWindowOptions opts)
{
this.jso = InfoWindowJSO.newInstance(opts);
}
/** Detaches the handler and closes */
public void close()
{
this.detachWidget();
this.jso.close();
}
/** Detaches the content widget, if it exists */
private void detachWidget()
{
if (this.widgetAttacher != null)
{
this.widgetAttacher.detachWidget();
this.widgetAttacher = null;
}
}
/** */
public void open(JavaScriptObject map)
{
this.jso.open(map);
}
public void open(JavaScriptObject map, JavaScriptObject marker)
{
this.jso.open(map, marker);
}
/** */
public void setOptions(InfoWindowOptions value)
{
this.jso.setOptions(value);
}
/** */
public void setContent(String value)
{
this.widgetContent = null;
this.detachWidget();
this.jso.setContent(value);
}
/** */
public void setContent(Element value)
{
this.widgetContent = null;
this.detachWidget();
this.jso.setContent(value);
}
/** */
public void setContent(Widget value)
{
this.widgetContent = value;
this.detachWidget();
this.jso.setContent(value.getElement());
if (this.widgetAttacher == null)
{
// Add a hook for the close button click
this.jso.addListener("closeclick", new Runnable() {
#Override
public void run()
{
detachWidget();
}
});
this.widgetAttacher = new FakePanel(value);
}
else if (this.widgetAttacher.getWidget(0) != value)
{
this.widgetAttacher.detachWidget();
this.widgetAttacher = new FakePanel(value);
}
}
/** #return the widget, if a widget was set */
public Widget getContentWidget()
{
return this.widgetContent;
}
/** */
public JavaScriptObject getPosition()
{
return this.jso.getPosition();
}
/** */
public void setPosition(JavaScriptObject value)
{
this.jso.setPosition(value);
}
/** */
public int getZIndex()
{
return this.jso.getZIndex();
}
/** */
public void setZIndex(int value)
{
this.jso.setZIndex(value);
}
/** */
public void addListener(String whichEvent, Runnable handler)
{
this.jso.addListener(whichEvent, handler);
}
}
A. Browser events bubble all the way to the top of the DOM tree. You can attach your click handlers to a widget that is parent to both the maps InfoWindow and your widget. Then, when a user clicks on your button, you need to check for the source of event to make sure it came from your button.
public void onClick(final ClickEvent event) {
Element e = Element.as(event.getNativeEvent().getEventTarget());
// check if e is your button
}
B. You can create a regular GWT button, attach a ClickHandler to it. Do not put it inside the InfoWindow: place it on top it using absolute positioning and a higher z-index.
I use the static value nextAnchorId to uniquely generate IDs for each InfoWindow, and when the InfoWindow is ready (usually when you call infoWindow.open(map);), I get the anchor by element ID and add my click handler to it. This is kind of what Manolo is doing, but this implementation doesn't require gwtquery, which means that I can run my code in Super Dev Mode.
private static int nextAnchorId = 1;
public InfoWindow makeInfo() {
InfoWindowOptions infoWindowOptions = InfoWindowOptions.create();
FlowPanel infoContentWidget = new FlowPanel();
final String theAnchorId_str = "theAnchor" + nextAnchorId;
HTML theAnchor = new HTML("<a id=\"" + theAnchorId_str + "\">Click me!</a>");
infoContentWidget.add(theAnchor);
infoWindowOptions.setContent(infoContentWidget.getElement());
InfoWindow infoWindow = InfoWindow.create(infoWindowOptions);
infoWindow.addDomReadyListenerOnce(new InfoWindow.DomReadyHandler() {
#Override
public void handle() {
com.google.gwt.user.client.Element muffinButton = (com.google.gwt.user.client.Element) Document.get().getElementById(theAnchorId_str);
DOM.sinkEvents(muffinButton, Event.ONCLICK);
DOM.setEventListener(muffinButton, new EventListener() {
#Override
public void onBrowserEvent(Event event) {
Window.alert("You clicked on the anchor!");
// This is where your click handling for the link goes.
}
});
}
});
nextAnchorId++;
return infoWindow
}
A very simple solution is to use gwtquery:
Identify the anchor in the map you want to add the click handler and define a css selector for that (for instance id=my_link)
Use gquery to locate it and to add the event.
$('#my_link').click(new Function() {
public boolean f(Event e) {
[...]
return false; //false means stop propagation and prevent default
}
});
Note that gwtquery is not a wrapper of jquery but an entire gwt implementation of its api, so including it in your project will not overload it, and the compiler will pick up just the stuff you use.