Application Stopped on GPS Location Request Programmatically in Released APK - android

Update
Forgot to mention that this is a xamarin coding.
In case may help:
I changed my linking option in android options from none to sdk assemblies only
I also enabled proguard
I am having a problem regarding request of location but only in released apk, it always says application has stopped. I tried to debug it and found the part where exception occured. My application works well in debug mode so i'm clueless why it hit an exception during released apk testing. Tested using Asus zenfone 3 Nougat and OPPO lollipop, same result (failed on release, no issue on debug).
Here is part of my code
public void m4_setUpAllClickable()
{
try
{
btnEnableGPS.Click += delegate
{
enableGPSLocation();
};
}
catch { throw; }
}
private async void enableGPSLocation()
{
try
{
GoogleApiClient
googleApiClient = new GoogleApiClient.Builder(this)
.AddApi(LocationServices.API)
.Build();
googleApiClient.Connect();
LocationRequest
locationRequest = LocationRequest.Create()
.SetPriority(LocationRequest.PriorityHighAccuracy)
.SetSmallestDisplacement(LocationValues.minimunDisplacement)
.SetInterval(LocationValues.normalInterval)
.SetFastestInterval(LocationValues.fastestInterval);
LocationSettingsRequest.Builder
locationSettingsRequestBuilder = new LocationSettingsRequest.Builder()
.AddLocationRequest(locationRequest);
locationSettingsRequestBuilder.SetAlwaysShow(false);
//****** -> Problem Occur in this part and I don't know why since it is going straight to app has stopped instead of catch exception
LocationSettingsResult
locationSettingsResult = await LocationServices.SettingsApi.CheckLocationSettingsAsync(
googleApiClient, locationSettingsRequestBuilder.Build());
//****** -> ends here
if (locationSettingsResult.Status.StatusCode == CommonStatusCodes.ResolutionRequired)
{
locationSettingsResult.Status.StartResolutionForResult(this, 1);
}
else if (locationSettingsResult.Status.StatusCode == CommonStatusCodes.Success)
{
if (activitySource == "GPS Failed")
{
Intent intent;
intent = new Intent(this, typeof(Activity_AssignedList));
StartActivity(intent);
FinishAffinity();
}
else if (activitySource == "GPS Disabled")
{
Finish();
}
}
}
catch { throw; }
}

Have you added proguard rules to your project?If not, try this and rerun your release build
-keep public class com.google.android.gms.* { public *; }
-dontwarn com.google.android.gms.**

As #SushiHangover suggested on comment, I tried to change my application linking option from sdk assemblies only to none and it works well on released apk. I don't know why in this tutorial : https://developer.xamarin.com/guides/android/deployment,_testing,_and_metrics/publishing_an_application/part_1_-_preparing_an_application_for_release/ it says to enable proguard and change the linking option from none to sdk assemblies only so I did to make my application smaller. It goes down from 66mb to 40mb so I thought its a good thing but the problem is that it cuts some classes my application need.
So far it's working without linking options and my proguard is still enabled. Thanks for the help !

Related

App is working fine in debug build but not working in production

I am facing problem with my app in live environment. Everything works fine when I generate a debug build for testing and test it thoroughly, but when I uploaded my application on google play console and then in the production app I am facing many problems. For example when I try to login into the app, after entering the correct credentials and clicking the login button a loader is generated and after loading for few seconds it stops on the login screen itself. Ideally the user should be able to login, but the screen is not redirecting the user to home screen.
I have checked hitting the login api url in postman and it works fine. I have also checked the logcat by attaching the production app through usb cable and the logcat shows that api is being called and i can see the response also.
What could be the possible reason please suggest..
This is the retrofit code which is being used for calling a rest api for login purpose.
private void userLoginApi() {
if (Util.isConnectingToInternet(getActivity())) {
CommonMethods.showLoading(getActivity());
MultipartBody.Builder builder = new MultipartBody.Builder();
builder.setType(MultipartBody.FORM);
builder.addFormDataPart(Constants.MOBILE, mobileEt.getText().toString());
builder.addFormDataPart(Constants.PASSWORD, passwordEt.getText().toString());
builder.addFormDataPart(Constants.DEVICE_ID, token);
builder.addFormDataPart(Constants.LANGUAGE, SharedPref.getSharedPreferences(getActivity(), Constants.LANGUAGE));
MultipartBody requestBody = builder.build();
RetrofitClient.getAPIService().user_login(requestBody).enqueue(new Callback<RetroResponse>() {
#Override
public void onResponse(Call<RetroResponse> call, Response<RetroResponse> response) {
CommonMethods.dismissLoading();
try {
if (response.body().getStatus() == 200) {
Pref.with(getApplicationContext()).getSharedPreferences().edit().putBoolean("isLogin", false)
.putString("admin_id", response.body().getId())
.putString("first_name", response.body().getData().getFirstName())
.putString("email", response.body().getData().getEmail())
.apply();
userID = response.body().getData().getUser_id();
SharedPref.setSharedPreference(getActivity(), Constants.USER_ID, response.body().getData().getUser_id());
SharedPref.setSharedPreference(getActivity(), Constants.ADMIN_ID, response.body().getId());
SharedPref.setSharedPreference(getActivity(), Constants.FIRST_NAME, response.body().getData().getFirstName());
SharedPref.setSharedPreference(getActivity(), Constants.LAST_NAME, response.body().getData().getLastName());
SharedPref.setSharedPreference(getActivity(), Constants.EMAIL, response.body().getData().getEmail());
SharedPref.setSharedPreference(getActivity(), Constants.MOBILE, response.body().getData().getMobile());
SharedPref.setSharedPreference(getActivity(), Constants.USER_MOBILE, response.body().getData().getMobile());
SharedPref.setSharedPreference(getActivity(), Constants.PROFILE_CITY, response.body().getData().getCity());
startActivity(new Intent(getActivity(), UserHomeActivity.class)
.putExtra("screen_type", "")
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK));
getActivity().finish();
} else if (response.body().getStatus() == 204) {
Util.ShowToastMessage(getActivity(), response.body().getMessage());
mobileEt.setText("");
passwordEt.setText("");
captchaCheck.setChecked(false);
captchaVerified = false;
} else if (response.body().getStatus() == 401) {
Util.ShowToastMessage(getActivity(), "Something went wrong");
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Call<RetroResponse> call, Throwable t) {
CommonMethods.dismissLoading();
}
});
} else {
Util.ShowToastMessage(getActivity(), R.string.internet_connection);
}
}
This api is getting called successfully, I have checked it in logcat. But after it the user is not redirected to intended screen.
The possible reason for this behavior could be because of proguard rules set in the build.gradle file
Change minifyEnabled true to minifyEnabled false
Note:
Proguard is a free Java class file shrinker, optimizer, obfuscator, and preverifier. It detects and removes unused classes, fields, methods, and attributes. Mobile app development companies use proguard in android , it optimizes bytecode and removes unused instructions.
This is a temporary solution and you need to manually figure out the error when minifiyEnable is true.

GoogleApiClient builder failing to build

I'm implementing Google Smart Lock into an app, and I was having no trouble with the Api Client building before. In fact, I was finalizing some syntax changes and cleaning up the code (didn't even touch the code that initializes the Api Client), and my app now dies when build() is called on the Api Client builder, due to abstract method zza. Here is the error being displayed:
java.lang.AbstractMethodError: abstract method "com.google.android.gms.common.api.Api$zze com.google.android.gms.common.api.Api$zza.zza(android.content.Context, android.os.Looper, com.google.android.gms.common.internal.zzq, java.lang.Object, com.google.android.gms.common.api.GoogleApiClient$ConnectionCallbacks, com.google.android.gms.common.api.GoogleApiClient$OnConnectionFailedListener)"
at com.google.android.gms.common.api.GoogleApiClient$Builder.build(Unknown Source)
I have no clue why it suddenly started failing, and I couldn't find any changes I made that would have caused this error. Why isn't that abstract method being overridden? It's nested deep inside the library so I don't understand how I could have affected it.
I wrapped the Google Api Client calls in a manager I named CredentialManager. Here is the code I used to initialize the client:
public CredentialManager(ContextProvider contextProvider) {
mContextProvider = contextProvider;
mCredentialsApiClient = new GoogleApiClient.Builder(mContextProvider.getContext())
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(#Nullable Bundle bundle) {
Log.i(CredentialManager.TAG, "Api connected");
}
#Override
public void onConnectionSuspended(int i) {
Log.i(CredentialManager.TAG, "Connection suspended with status " + i);
}
})
.enableAutoManage(mContextProvider.getContext(), connectionFailedResult -> {
if (connectionFailedResult.hasResolution()) {
try {
connectionFailedResult.startResolutionForResult(
mContextProvider.getContext(),
CredentialManager.Codes.RESOLVE_CONNECTION_REQUEST_CODE);
} catch (IntentSender.SendIntentException e) {
// Unable to resolve, log error
Log.e(CredentialManager.TAG, "Resolution failed: " + e.getMessage());
}
} else {
//instead of displaying a dialog, just let the user continue and login manually.
Log.e(CredentialManager.TAG, "Connection failed: " + connectionFailedResult.getErrorMessage());
}
})
.addApi(Auth.CREDENTIALS_API)
.build();
}
If you have any insight as to what is causing this error, please let me know. I've scoured the internet for anyone that has seen something like this before, but couldn't find anything.
The issue was that some google play services dependencies had their versions updated and not the play-services-auth dependency used for google smart lock. The apk would compile fine, but crash when the Google Api Client was trying to initialize. The fix was to make all the versions the same, and invalidate cache + restart android studio, recompile, and run.

Testing Xamarin Geofence prototye in emulator fails

I have a prototype application that uses Geofencing set up in AndroidStudio and have been able to succesfully test it in the Android Emulator. Because I need the application to also be iOS i have ported the prototype to Xamarin/Visual Studio 2017 to make sure that it works in that environment so I can save myself from having to code the core logic of the app in Android and iOS. However I am not able to get the Geofences to fire in the Xamarin based app on the same emulator. Has anyone worked with this technology in Xamarin? are there specific settings that need to change for Xamarin to make this work?
The issue is probably coming from the manifest.
In Xamarin, when you create a service (or intent service) it should be tagged with the attribute [Service], instead of adding it to the manifest manually.
You should also check for errors when handling the intent (in case you are not doing it already):
[Service]
public class GeofenceTransitionsIntentService : IntentService, IEnableDatabaseLogger
{
public GeofenceTransitionsIntentService()
: base(nameof(GeofenceTransitionsIntentService)) { }
protected override void OnHandleIntent(Intent intent)
{
base.OnHandleIntent(intent);
this.Log().Info("Intent received");
var geofencingEvent = GeofencingEvent.FromIntent(intent);
if (geofencingEvent.HasError)
{
var errorMessage = GeofenceErrorMessages.GetErrorString(this, geofencingEvent.ErrorCode);
this.Log().Error(errorMessage);
return;
}
var geofenceTransition = geofencingEvent.GeofenceTransition;
var geofences = geofencingEvent.TriggeringGeofences;
var location = geofencingEvent.TriggeringLocation;
if (geofenceTransition == Geofence.GeofenceTransitionEnter)
{
foreach (var geofence in geofences)
this.Log().Info($"Entered {geofence.RequestId} at {location.Latitude}/{location.Longitude}");
// do something
}
else if (geofenceTransition == Geofence.GeofenceTransitionExit)
{
foreach (var geofence in geofences)
this.Log().Info($"Exited {geofence.RequestId} at {location.Latitude}/{location.Longitude}");
// do something
}
else
{
this.Log().Error($"Geofence transition invalid type: {geofenceTransition}");
}
}
}
Here is a demo (working) project I did recently: https://github.com/xleon/geofencing-playground

crashlyticsDidDetectCrashDuringPreviousExecution for crashlytics NDK

We are using crashlyticsDidDetectCrashDuringPreviousExecution to detect java crashes and report them to our BI systems, but our app is mostly C++ and we are using crashlytics NDK, we can't find anything similar to crashlyticsDidDetectCrashDuringPreviousExecution.
Is there any way that we can actually detect an NDK crash when the app starts?
thanks
Oded
Mike from Fabric here.
Currently, there isn't a way to do this within Fabric or the SDK for an NDK crash.
NOTE: This works on older version only (Crashlytics 2.6.7 and CrashlyticsNDK 1.1.6)
I'm also looking for a solution for this.
We currently found a partial solution. I'm not sure how good it is, it's definitely not official, plus it's asynchronic (which we're trying to overcome by looping), but it's the best solution I found and it seems like it's working
Fabric.with(this, new Crashlytics.Builder().core(core.build()).build(), new CrashlyticsNdk(), new Crashlytics());
if (!userLeft) { // our handling to fix bug, see explanation below
new Thread(new Runnable() {
#Override
public void run() {
SessionEventData crashEventData = null;
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000); // in ms
} catch (InterruptedException e) { }
crashEventData = CrashlyticsNdk.getInstance().getCrashEventData();
if (crashEventData != null)
{
// there was a crash!
// crash timestamp can be found at crashEventData.timestamp
break;
}
}
}
}).start();
}
Explaination for userLeft:
We had some bug with reporting crash for users that exited app, and this is the solution for that. We set this flag to true, and save it on the device (SharedPreferences). We do it on our main activity (which extends NativeActivity), on finish() func.
Code:
#Override
public void finish() {
// set some key such as USER_LEFT to TRUE
super.finish();
}
After that, just get that USER_LEFT value, assign it into userLeft param, and set it back to false on SharedPerferences.
Any insights about this solution?

How to retrieve crash reports from a wearable app?

Since Crashlytics doesn't work on wearable apps out of the box, I'm looking for an optimal way to intercept and report any potential exception thrown in the runtime. I wonder why they're not being automatically reported to Google Play Developer Console?
Google already announced that the future Android Wear update will have Wi-Fi support built-in, but even then, not every device is going to be equipped with the adequate hardware.
In that case, my initial idea was to create a subclass of Application and implement Thread.UncaughtExceptionHandler. Then, every exception would have to be marshalled and sent to a handset, using MessageApi. An extension of WearableListenerService on the handset would receive a message, unmarshal the exception and pass it to, for instance, Crashlytics.
However, that raises a few more questions. There's a risk that the Bluetooth connection between wearable and handset is disrupted, so all errors should be queued and stored on the wearable device's file system.
This seems like an overkill for a simple crash report. Is there an easier way to do this?
Don't use MessageApi for this purpose but DataApi. Then you don't have to worry about lost bluetooth connection.
The way it works:
when a crash occurs, set a DataItem with the crash on the wearable;
eventually it will be delivered to the Mobile device.
send the information about the crash from the Mobile and delete the DataItem.
More information here: http://developer.android.com/training/wearables/data-layer/index.html
Here's a draft of my solution. As #gruszczy suggested, I'm using DataApi.
Wearable Application:
public class WApplication extends Application
implements Thread.UncaughtExceptionHandler {
private static final String LOG_TAG = WApplication.class.getSimpleName();
private Thread.UncaughtExceptionHandler mDefaultUncaughtExceptionHandler;
...
#Override
public void onCreate() {
super.onCreate();
mDefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
}
#Override
public void uncaughtException(Thread thread, final Throwable throwable) {
Log.e(LOG_TAG, "Uncaught exception thrown.");
WearableService.launchService(throwable, WApplication.this);
mDefaultUncaughtExceptionHandler.uncaughtException(thread, throwable);
}
}
Wearable Service:
public class WearableService extends Service {
...
public static void launchService(Throwable throwable, Context context) {
Intent startServiceIntent = new Intent(context, WearableService.class);
startService.putExtra(EXTRA_KEY_EXCEPTION, throwable);
context.startService(startServiceIntent);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Throwable throwable = (Throwable) intent.getSerializableExtra(KEY_EXCEPTION);
sendExceptionToMobile(throwable);
return super.onStartCommand(intent, Service.START_REDELIVER_INTENT, startId);
}
private void sendExceptionToMobile(final Throwable throwable) {
if (throwable == null) {
return;
}
Log.d(LOG_TAG, "Sending exception to mobile...");
PutDataMapRequest putDataMapReq = PutDataMapRequest
.create(WearCommunicationConstants.PATH_EXCEPTION);
DataMap dataMap = putDataMapReq.getDataMap();
StringWriter sw = new StringWriter();
throwable.printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();
dataMap.putString(WearCommunicationConstants.KEY_STACK_TRACE, stackTrace);
PutDataRequest putDataReq = putDataMapReq.asPutDataRequest();
PendingResult<DataApi.DataItemResult> pendingResult =
Wearable.DataApi.putDataItem(mGoogleApiClient, putDataReq);
pendingResult.setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
#Override
public void onResult(final DataApi.DataItemResult result) {
if (result.getStatus().isSuccess()) {
Log.d(LOG_TAG,
"DataItem synced: " + result.getDataItem().getUri());
} else {
Log.e(LOG_TAG,
"Failed to sync DataItem: " + result.getStatus().getStatusCode() + ", "
+ result.getStatus().getStatusMessage());
}
}
});
}
}
Mobile Service:
public class MobileService extends WearableListenerService {
...
#Override
public void onDataChanged(DataEventBuffer dataEvents) {
Log.d(LOG_TAG, "Data changed, data event(s) received.");
for (DataEvent event : dataEvents) {
Log.d(LOG_TAG, "Data event type: " + event.getType());
switch (event.getType()) {
case DataEvent.TYPE_CHANGED:
DataItem item = event.getDataItem();
DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
switch (item.getUri().getPath()) {
case WearCommunicationConstants.PATH_EXCEPTION:
Log.e(LOG_TAG, "Received exception from a wearable device.");
String stackTrace = dataMap
.getString(WearCommunicationConstants.KEY_STACK_TRACE);
Utils.logWithCrashlytics(stackTrace);
break;
// ...
}
break;
case DataEvent.TYPE_DELETED:
// ...
}
}
}
}
Existing solutions require that the phone is currently in range. With Wear 2.0 providing for watch autonomy, we need to be able to store the crashes and send them over once we are connected. WearCrashReporter does exactly this.
We install a crash handler on the watch Virtual Machine. When a crash is caught, its trace and type are serialized to json, saved to the FileSystem, then sent with a service as a MessageApi Message when the phone is available. Upon reception by a WearableListenerService in the phone app it is deserialized and passed to the installed Phone Virtual Machine's crash reporter.
I solved this problem in following way:
Integrate this lib into your project. This lib will transmit all exceptions from wear app to mobile app.
If you don't use proguard - you can simple use ExceptionWear lib and log exceptions on mobile-app side into crashlytics.
otherwise
When you receive throwable on mobile-app side - you can log it into crashlytics, but there is a problem:
If we build mobile+wear application using android plugin feature we will have something like this:
dependencies {
compile 'com.google.android.gms:play-services:5.0.+#aar'
...lots of cool libs...
wearApp project(':wear')
}
and apply crashlytics plugin on both applications (mobile and wear) then during building wear application you can see that after proguard task and dex task (gradle tasks) crashlytics plugin doesn't store and upload Deobs and as a result - stacktraces are not remapped(retraced) on the crashlytics dashboard:
:wear:crashlyticsCleanupResourcesRelease//EXPECTED
:wear:crashlyticsUploadStoredDeobsRelease//EXPECTED
:wear:crashlyticsGenerateResourcesRelease//EXPECTED
:wear:generateReleaseResValues UP-TO-DATE
:wear:generateReleaseResources
:wear:mergeReleaseResources
:wear:processReleaseResources
:wear:generateReleaseSources
:wear:compileReleaseJava
:wear:proguardRelease
:wear:dexRelease//NO crashlytics store and upload Deobs tasks
:wear:processReleaseJavaRes UP-TO-DATE
:wear:shrinkReleaseResources
but when wear app is builded (wear ap is like dependency to mobile app) then mobile app build starts and during mobile build process crashlytics plugin works well:
:mobile:crashlyticsCleanupResourcesRelease//EXPECTED
:mobile:crashlyticsUploadStoredDeobsRelease//EXPECTED
:mobile:crashlyticsGenerateResourcesRelease//EXPECTED
:mobile:generateReleaseResValues UP-TO-DATE
:mobile:generateReleaseResources
:mobile:mergeReleaseResourcesknown
:mobile:processReleaseResources
:mobile:generateReleaseSources
:mobile:compileReleaseJava
:mobile:proguardRelease
:mobile:dexRelease
:mobile:crashlyticsStoreDeobsRelease//EXPECTED
:mobile:crashlyticsUploadDeobsRelease//EXPECTED
:mobile:crashlyticsCleanupResourcesAfterUploadRelease//EXPECTED
:mobile:lintVitalRelease
:mobile:compileReleaseNdk UP-TO-DATE
:mobile:processReleaseJavaRes UP-TO-DATE
:mobile:shrinkReleaseResources
So, during standard build process wear module deobs are not uploaded, but there is workaround:
if start build wear app separately and then manualy package wear apk in mobile module resources,
then wear deobs uploaded successfully and you can observe retraced crashes on dashboard.
But i personally don't like manual way of building apk, so i tried to do following : at first just build only wear app. deobs will uploaded to crashlytics. then run full build using 'wearApp project(':wear')' feature and looks like it works.
Anyway i am waiting for android-wear support by crashlytics out of the box.
It's possible to upload the deobs of mobile and wear within the build process.
Concept:
1. Ensure mobile and wear have unique mappings
2. Merge wear mappings into mobile mappings before upload
1. Configure proquard (usually proguard-rules.pro)
for wear add:
-useuniqueclassmembernames
for mobile add:
-useuniqueclassmembernames
-applymapping ../wear/build/outputs/mapping/release/mapping.txt
This change ensures you have unique names over mobile and wear by applying the mappings of the wear build to the mobile build.
2. Configure the build to merge mapping.txt
Add to build.gradle of mobile:
// allows to use Crashlytics also for wear by merging the mappings of wear into the
// mappings of mobile
//noinspection GroovyAssignabilityCheck
task mergeMappings(dependsOn: "transformClassesAndResourcesWithProguardForRelease") << {
File wearMappingFile = new File("wear/build/outputs/mapping/release/mapping.txt");
File mobileMappingFile = new File("mobile/build/outputs/mapping/release/mapping.txt");
if (wearMappingFile.exists() && mobileMappingFile.exists()) {
println("merge mapping.txt")
java.nio.file.Files.copy(wearMappingFile.toPath(),
new FileOutputStream(mobileMappingFile, true))
} // else we are on the wear build and the mobile build was not yet executed
}
afterEvaluate {
project.("crashlyticsStoreDeobsRelease").dependsOn(mergeMappings);
}
Appends the wear mappings to the mobile mappings before crashlyticsStoreDeobsRelease.

Categories

Resources