Android app modulization (classes) - java

thank you for taking the time to load my post and view my question.
For the longest time I have been creating long android activities just because I didn't quite understand classes and how they worked. Earlier this week, I learned how easy it is to send information between methods. Today, I am struggling to send information between classes. Instead of having one very long page of code to sift through, I want to break it down into chunks, and have the information reference and interact with eachother. This way it is easier to keep my code organized and faster to make changes. I have tried to remove two separate classes from my main thread and created them in the same directory, the code shows no errors so they should execute just fine. However, I am messing up somewhere with the context. I don't fully grasp the concept of context either. I have one class to get my phones location, and another class to upload to my server. Both these classes return null values when trying to be executed. However, when it was all stream-lined on one main activity, there was no null errors. I have been reading context is the issue here.
I have referenced the other classes from my mainthread like this:
LocationClass LocationClass;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mapfrag);
LocationClass = new LocationClass();
#Override
public void onMapReady(GoogleMap googleMap) {
Map = googleMap;
Map.setOnMapClickListener(this);
Map.setOnMapLongClickListener(this);
Map.setMapType(1);
Map.setOnMarkerClickListener(this);
LocationClass.GETLOCATION();
}
and my location class looks like this:
public class LocationClass extends AppCompatActivity implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 1000;
private Location mLastLocation;
private GoogleApiClient mGoogleApiClient;
public LatLng reallifelocation;
public static final String userdata = "userdata";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (checkPlayServices()) {
buildGoogleApiClient();
}
check_perms();
}
and then it checks the permissions and does what it has to to get the location. This code is all tested and working from the main thread, I'm just trying to create my own class for it
error:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
at android.content.ContextWrapper.getApplicationContext(ContextWrapper.java:112)
at biz.tanners.geo_x.LocationClass.GETLOCATION(LocationClass.java:49)
where the error sits(LocationClass.java):
public void GETLOCATION() {
if (ActivityCompat.checkSelfPermission(this.getApplicationContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {return;}
final SharedPreferences prefs = getSharedPreferences(userdata, MODE_PRIVATE);
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mLastLocation != null) {
reallifelocation = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
}
//add to prefs or offline db
}

Your error stems from here
if (ActivityCompat.checkSelfPermission(
this.getApplicationContext(),
android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
Here this probably should correspond to the Context and being another class, the context is different.
A quick fix would be to pass the Activity's context as a parameter to the LocationClass's object. By adding it in the constructor and assigning it to the right variable, which later you could use instead of this.
Hope this helps.

Related

How to create proper "this" object as argument? [duplicate]

This question already has answers here:
What is the meaning of "this" in Java?
(22 answers)
Closed 4 years ago.
I'm trying to write a function where I have to pass an Activity object to a method that requires such an argument. Usually in such case I'm supposed to just type "this" and it automatically recognizes which type of object it's supposed to create. But sometimes this doesn't work and it for whatever reason reassings a different type of object than the one that is required. For example, I actually use the exact same method in both of these cases:
if (checkLocationPermission(this)){
In this first one, the program automatically recognizes "this" as an Activity object. Here's the second one:
#Override
public void onSuccess(Location location) {
if (location == null || !checkLocationPermission(this)){
In this case the exact same method recognizes "this" as an OnSuccessListener instead of an Activity.
Another example I have in the same program is one where "this" object is supposed to be a Looper but instead it again gets recognized as an OnSuccessListener:
fusedLocationClient.requestLocationUpdates(locationRequest,new LocationCallback(),this);
I don't know how to actually select the proper type of object for "this" argument since I can only type the same damn word.
EDIT:
Here's the full code. I used Looper.this just so you can find it easier. I also tried with MapsActivity.this and it doesn't work:
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
private GoogleMap mMap;
private GoogleApiClient googleApiClient;
public static final String TAG = MapsActivity.class.getSimpleName();
private FusedLocationProviderClient fusedLocationClient;
private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000; //Request code to send to Google Play Services
private LocationRequest locationRequest;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
setUpMapIfNeeded();
googleApiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(this).addOnConnectionFailedListener(this).addApi(LocationServices.API).build();
locationRequest = LocationRequest.create().setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY).setInterval(10*1000).setFastestInterval(1*1000);
}
private void setUpMapIfNeeded(){
if (mMap==null){
SupportMapFragment mapFragment = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map));
mapFragment.getMapAsync(this);
}
}
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
//setUpMap();
// Add a marker in Sydney and move the camera
LatLng sydney = new LatLng(-34, 151);
mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
}
#Override
public void onConnected(Bundle bundle) {
Log.i(TAG,"Location Services Connected");
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
if (checkLocationPermission(this)){
fusedLocationClient.getLastLocation().addOnSuccessListener(this, new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
if (location == null || !checkLocationPermission(MapsActivity.this)){
fusedLocationClient.requestLocationUpdates(locationRequest,new LocationCallback(),Looper.this);
}
else{
handleNewLocation(location);
}
}
});
}
}
public static boolean checkLocationPermission(Activity activity){
if(ActivityCompat.checkSelfPermission(activity, android.Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(activity, android.Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(activity, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.ACCESS_COARSE_LOCATION},0);
return false;
}
return true;
}
private void handleNewLocation(Location location){
Log.d(TAG,location.toString());
}
#Override
public void onConnectionSuspended(int i) {
Log.i(TAG,"Location Services suspended. Please reconnect.");
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
if (connectionResult.hasResolution()){
//Starts an Activity that tries to resolve the error
try {
connectionResult.startResolutionForResult(this,CONNECTION_FAILURE_RESOLUTION_REQUEST);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
else{
Log.i(TAG,"Location services connection failed code: " + connectionResult.getErrorCode());
}
}
#Override
protected void onResume(){
super.onResume();
setUpMapIfNeeded();
googleApiClient.connect();
}
#Override
protected void onPause(){
super.onPause();
if (googleApiClient.isConnected()){
googleApiClient.disconnect();
}
}
#Override
public void onLocationChanged(Location location) {
handleNewLocation(location);
}
}
this corresponds to the object in which it is used. onSuccess is a method of OnSuccessListener class and hence this refers to OnSuccessListener. You need to use ActivityName.this. For example if you activity name is MainActivity, then
#Override
public void onSuccess(Location location) {
if (location == null || !checkLocationPermission(MainActivity.this)){
When you use an anonymous inner class, such as a listener, and use this, it refers to the anonymous inner class, because that's your current location.
For instance, with an OnClickListener:
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//"this" here will refer to the OnClickListener instance you create
}
}
Nothing is being "created" with a this argument. It's a direct reference to the current enclosing class. If you need to reference the Activity, you can use:
ActivityClassName.this
as long as you're in an inner class and not a static class.
If you use a lambda (only available for API 24+):
view.setOnClickListener((v) -> {
//"this" will reference your Activity because there's no inner class anymore
}
the this refers to the object of the immediate enclosing class. So, if you have some interface or class as an argument to a function, we usually do like this :
functionThatTakesInterfaceOrClassAsArgument( new TheInterfaceOrClass {
#Override
public void someMethod () {
// if you use `this` here, it refers to the object of `TheInterfaceOrClass`
}
});
If you want to use the object corresponding to an enclosing class (yet not the immediate enclosing class) using <className>.this
So, if the name of the enclosing Activity is MyActivity, one would need to use MyActivity.this.

How to pass information to a string outside a method?

I'm new to the Java language and have become a little stuck, I'm trying to pass a location to the String Bob. I need to pass a string from onLocationChanged method, to the String url which outside of the method. I have created a global variable, but the String bob does not hold any data.
Any help would be much appreciated.
public class MainActivity extends AppCompatActivity {
String bob = "";
LocationManager locationManager;
LocationListener locationListener;
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);}
}
}
TextView tempTextView;
TextView dateTextView;
TextView weatherDescTextView;
TextView cityTextView;
ImageView weatherImageView;
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tempTextView = (TextView) findViewById(R.id.tempTextView);
dateTextView = (TextView) findViewById(R.id.dateTextView);
weatherDescTextView = (TextView) findViewById(R.id.weatherDescTextView);
cityTextView = (TextView) findViewById(R.id.cityTextView);
weatherImageView = (ImageView) findViewById(R.id.weatherImageView);
dateTextView.setText(getCurrentDate());
// location
locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
locationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
// Log.i("Location", location.toString());
Geocoder geocoder = new Geocoder(getApplicationContext(), Locale.getDefault());
try {
List<Address> listAddresses = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1);
if (listAddresses != null && listAddresses.size() > 0) {
//Log.i("PlaceInfo", listAddresses.get(0).toString());
if (listAddresses.get(0).getLocality() != null)
{
bob += listAddresses.get(0).getLocality() + " ";
}
Log.i("hello", bob.toString());
}
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider ) {
}
#Override
public void onProviderDisabled(String provider) {
}
};
String url = "http://api.openweathermap.org/data/2.5/weather?q=" +bob+ "&units=metric&appid=xxxx";
i'm new to the Java language
My best piece of advice to you right now is to stop, put Android down, and go learn Java (or hell, get started with Kotlin) before trying to learn Android as well as you go. Android is complicated enough as is without trying to learn the language as well.
Anyway, as to your actual problem:
I'm trying to pass a location to the String 'Bob'. I need to pass a string from 'onLocationChanged' method, to the String 'url' which outside of the method... I have created a 'global' variable, but the String 'bob' does not hold any data.
You're problem is that that onLocationChanged method is a callback that actually doesn't get invoked until much later than the point at which you are trying to set the URL variable (assuming you actually register the callback, which your code does not show).
In other words, you are providing a hook in to the system that says, "when you have the location, let me know", meanwhile your code continues. When system lets you know the location is back (your callback is invoked) it's up to you to use the new value do something with it (in your case, I assume, make a network request).
So you would move your logic to right after the line where you update bob.
Hope that helps.
onLocationChanged is evaluated after you compute String url.
Once you have an understanding of multithreading, you will understand this.
At the moment, your code would be no different if you move String url to the very top of onCreate.
If you want the URL to be properly assigned, move it after Log.i("hello", and even check your logs to make sure it's correct
Personally, I don't suggest Android as the platform from which you learn Java

Android decompiler inserts 'this' keyword everywhere in the code?

I have original code which is given below.But i tried decompiling original apk it gave me this keyword everywhere(shown after this code below) :
public class Aboutt extends Activity {
WebView web;
ProgressBar progressBar;
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aboutt);
web = (WebView) findViewById(R.id.webview01);
if (savedInstanceState != null)
web.restoreState(savedInstanceState);
else
web.loadUrl("http://www.google.com");
progressBar = (ProgressBar) findViewById(R.id.progressBar1);
web.setWebViewClient(new myWebClient());
web.getSettings().setJavaScriptEnabled(true);
web.getSettings().setBuiltInZoomControls(true);
}
But the decompiled code gives me:
public class Aboutt
extends Activity {
ProgressBar progressBar;
WebView web;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_aboutt);
this.web = (WebView)this.findViewById(R.id.webview01));
if (savedInstanceState != null) {
this.web.restoreState(savedInstanceState);
} else {
this.web.loadUrl("http://www.google.com");
}
this.progressBar = (ProgressBar)this.findViewById(R.id.progressBar1);
this.web.setWebViewClient((WebViewClient)new myWebClient());
this.web.getSettings().setJavaScriptEnabled(true);
this.web.getSettings().setBuiltInZoomControls(true);
}
Here it gives this everywhere.
Will it affect working of the app or can i use this everywehere.
The this keyword refers to the current instance of the class i.e. the object that the method is called on.
Usually, this is omitted because if you have something like this:
private int i;
public int getI () { return i}
everyone knows that you are referring to i. If you want to add the this keyword, it's just more wordy and will not affect the compiled code.
Whenever you see your code reference a non-static member, you can add the this suffix. Like in the above example, you can change i to this.i.
However, this does not work in a static method. I mean it will NEVER appear in a static context.
But other than that, this is fine to appear anywhere else.
this is a reference to the current instance of a class. It is mostly used un cases where there are similar names of parameters and local variables (they live as long as the method) and member variables (which live as long as the object).
So while it's not okay to use it everywhere (will not work in static context for instance) you're safe with what the decompiler created.

Android - getting current location through Google Map

I am developing an app related to Google map. I have done following steps successfully.
Created API key to access Google Map
Added Google Play Services Library in my app
Added required permissions
Added map in my activity with SupportMapFragment
Added a separate class MyMap.java to manipulate the map
Passed tow parameters to this class - Context of main activity and object of GoogleMap
Turned Wi-Fi and GPS on and ran the app
After this I am getting map with nice look and controls.
MyMap.java
public class MyMap implements ConnectionCallbacks, OnConnectionFailedListener {
private Context context;
private GoogleMap map;
private GoogleApiClient client = null;
public MyMap(Context context, GoogleMap map) {
this.context = context;
this.map = map;
client = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API).addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
}
#Override
public void onConnectionFailed(ConnectionResult arg0) {
// TODO Auto-generated method stub
}
#Override
public void onConnected(Bundle arg0) {
Toast.makeText(context, "Connected", 1).show();
Location mLastLocation = LocationServices.FusedLocationApi
.getLastLocation(client);
if (mLastLocation != null) {
Toast.makeText(
context,
String.valueOf(mLastLocation.getLatitude()) + ","
+ String.valueOf(mLastLocation.getLongitude()), 1)
.show();
}
}
#Override
public void onConnectionSuspended(int arg0) {
// TODO Auto-generated method stub
}
}
Problem
In the above class I want to toast the current location. But it is not toasting anything. At least I need to see a toast saying "connected" on onConnected
event. Is there something wrong in my implementation?
Thanks in advance.
You seemingly never connect your client so it would be a real suprise if onConnected was called :)
You create your client with
client = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API).addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
but for the client to do something you have to add:
client.connect();
getLastLocation() is going to give location only once. To get periodic location updates, you need to override onLocationChanged() method. You can get this Link
Best way that I found is simple implement you activity like so:
public class MapActivity extends Activity implements GoogleMap.OnMyLocationChangeListener
and override method
#Override
public void onMyLocationChange(Location location) {
mMap.addMarker(new MarkerOptions().position(location).icon(
BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)));
}
And don't forget about mMap.setMyLocationEnabled(true); and mMap.setOnMyLocationChangeListener(this); in map init method
That's all!
Also, you can check is map available like here:
public boolean checkMapsAvailable() {
int isAvailable = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (isAvailable == ConnectionResult.SUCCESS) {
return true;
} else if (GooglePlayServicesUtil.isUserRecoverableError(isAvailable)) {
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(isAvailable, this, 9001);
dialog.show();
} else {
Constants.showToast(Constants.ALERT_GOOGLEPLAY_CONNECTION);
}
return false;
}
Hope this helps.

How can I check wether a (Google maps API) map has loaded?

I have started building an android application that focuses on a map from google-play-services-libs. When I start the app, it takes a while for the map to load from the gray grid to showing the map image.
I'd like to move/play around a bit with the map when it has fully loaded the part that is currently on screen. I have however been unable to find a way to programmatically check whether the map has loaded.
How can I achieve this?
In short:
From a instance of GoogleMap, how do I determine if it is actually showing something?
You can try using a OnCameraChangeListener on your map. The onCameraChange call will be called when the map's tiles are initially loaded.
this.map.setOnCameraChangeListener(new OnCameraChangeListener() {
public void onCameraChange(CameraPosition arg0) {
isMapReady = true;
map.setOnCameraChangeListener(null);
}
});
chckReady() function checks whether the map is ready or not
public class MapView extends android.support.v4.app.FragmentActivity {
private GoogleMap mMap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ui_settings_demo);
setUpMapIfNeeded();
}
#Override
protected void onResume() {
super.onResume();
setUpMapIfNeeded();
}
private void setUpMapIfNeeded() {
if (mMap == null) {
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
.getMap();
}
}
private boolean checkReady() {
if (mMap == null) {
return false;
}
}
}

Categories

Resources