It can be difficult to handle android archive dependency issues if you are working on a maven based android project due to gradle and maven’s default dependency type differences as “aar” amd “jar”. Let’s say there is an android archive(aar) library dependency which has a transitive “aar” dependency and you want to use this in your maven based android project. That’s fine, you have added your dependency in your pom like this :
But it’s not over, because above library has a transitive dependency called “appcompat-v7” which is also an “aar” library. Now we are in trouble, bacause we can not access to the pom file of
“android-image-cropper” library and change it’s pom file. There is still something we can do in this situation. Simply exclude “appcompat-v7” dependency and include it manually by defining type as “aar.
If you use HockeyApp platform for tracking android native side crashes, you probably aware of NativeCrashManager which implemented by using apache network library and perfectly working until android sdk version 24. Google has removed apache network library related codes from it’s core android module and recommends HttpURLConnection usage as you can read details in the following page :
I have reimplemented the same logic using okhttp library and it’s tested on live with one of our tope games. Here it is :
importjava.io.*;importjava.net.HttpURLConnection;importjava.net.URL;importjava.util.Date;importjava.util.UUID;importandroid.net.Uri;importcom.squareup.okhttp.*;importandroid.app.Activity;importandroid.util.Log;publicclassNativeCrashManager{privatestaticfinalStringDESCRIPTION_FILE_NAME="description.txt";privatestaticfinalStringTAG="HockeyApp";publicstaticvoidhandleDumpFiles(Activityactivity,Stringidentifier,StringuserId){String[]filenames=searchForDumpFiles();for(StringdumpFilename:filenames){Log.d(TAG,"dumpFile "+dumpFilename);StringlogFilename=createLogFile();if(logFilename!=null){uploadDumpAndLog(activity,identifier,dumpFilename,logFilename,userId);}}}publicstaticStringcreateLogFile(){finalDatenow=newDate();try{// Create filename from a random uuidStringfilename=UUID.randomUUID().toString();Stringpath=Constants.FILES_PATH+"/"+filename+".faketrace";Log.d(TAG,"Writing unhandled exception to: "+path);// Write the stacktrace to diskBufferedWriterwrite=newBufferedWriter(newFileWriter(path));write.write("Package: "+Constants.APP_PACKAGE+"\n");write.write("Version: "+Constants.APP_VERSION+"\n");write.write("Android: "+Constants.ANDROID_VERSION+"\n");write.write("Manufacturer: "+Constants.PHONE_MANUFACTURER+"\n");write.write("Model: "+Constants.PHONE_MODEL+"\n");write.write("Date: "+now+"\n");write.write("\n");write.write("MinidumpContainer");write.flush();write.close();returnfilename+".faketrace";}catch(Exceptionanother){Log.e(TAG,"File couldn't be write to path!!!",another);}returnnull;}publicstaticvoiduploadDumpAndLog(finalActivityactivity,finalStringidentifier,finalStringdumpFilename,finalStringlogFilename){newThread(){@Overridepublicvoidrun(){try{OkHttpClienthttpClient=newOkHttpClient();FiledumpFile=newFile(Constants.FILES_PATH,dumpFilename);FilelogFile=newFile(Constants.FILES_PATH,logFilename);MultipartBuildermultipartBuilder=newMultipartBuilder().type(MultipartBuilder.FORM).addFormDataPart("attachment0",dumpFile.getName(),RequestBody.create(MediaType.parse("text/plain"),dumpFile)).addFormDataPart("log",logFile.getName(),RequestBody.create(MediaType.parse("text/plain"),logFile));RequestBodyrequestBody=multipartBuilder.build();Requestrequest=newRequest.Builder().url("https://rink.hockeyapp.net/api/2/apps/"+identifier+"/crashes/upload").post(requestBody).build();Responseresponse=httpClient.newCall(request).execute();if(response.isSuccessful()){Log.d(TAG,"result code : "+response.code());Log.d(TAG,"Upload response : "+response.message());}else{Log.d(TAG,"Failed to upload dumpFile caused by code : "+response.code());}}catch(Exceptione){Log.e(TAG,"Failed to upload dumpFile",e);}finally{activity.deleteFile(logFilename);activity.deleteFile(dumpFilename);}}privatevoidfillCrashDescription(FiledescriptionFile){FileWriterfileWriter=null;try{fileWriter=newFileWriter(descriptionFile);fileWriter.write(SessionLogManager.getSavedSessionData());}catch(IOExceptione){Log.e(TAG,"description file error",e);}finally{if(fileWriter!=null){try{fileWriter.close();}catch(IOExceptione){Log.e(TAG,"fileWriter couldn't be closed!",e);}}}}}.start();}privatestaticString[]searchForDumpFiles(){if(Constants.FILES_PATH!=null){// Try to create the files folder if it doesn't existFiledir=newFile(Constants.FILES_PATH+"/");booleancreated=dir.mkdir();if(!created&&!dir.exists()){returnnewString[0];}// Filter for ".dmp" filesFilenameFilterfilter=newFilenameFilter(){publicbooleanaccept(Filedir,Stringname){returnname.endsWith(".dmp");}};returndir.list(filter);}else{Log.d(TAG,"Can't search for exception as file path is null.");returnnewString[0];}}}
C++ language’s standard vector has a function called “find_if” provides search functionality depends on a boolean return typed function. Let’s say you have
an integer vector and trying to find an even number :
#include <iostream>
#include <algorithm>
#include <vector>
boolIsEven(inti){return((i%2)==0);}intmain(){std::vector<int>numberVector;numberVector.push_back(1);numberVector.push_back(3);numberVector.push_back(5);numberVector.push_back(6);numberVector.push_back(7);std::vector<int>::iteratorit=std::find_if(numberVector.begin(),numberVector.end(),IsEven);printf("First even number : %d",*it);return0;}
But what if you want to pass an additional parameter to your search function and use lambda functions. Take a look to the following code snippet. We have a Popup object and a lambda function defines a search condition
depending on name of the popup.
Playing a custom sound for android push notifications is really simple with just one trick you have to know. If you are an impatient person who reads the last part of novels first, then move to the end of this post and get the trick :)
That’s all. You sent your push notification to your device but couldn’t hear the sound you expect. Now, the mighty trick you should apply is ignoring default sound on notification builder object as follows :
If you have used custom notification in your android project, you already aware of RemoteViews and content field of android notification. In this blog post,
I have explained how to use custom notifications with OneSignal android SDK integration.
Requirements
Approximately a month ago OneSignal has started to support custom push notifications. You need to use version 2.4.0 or newer.
You can use OneSignalSDK.jar directly in your project.
Action
You have prepared a layout xml file in your res/layout folder. Now you need to extend NotificationExtenderService and implement “onNotificationProcessing” method.
Most important part of this implementation is setting style of builder to “null”. In default implementation of OneSignal, style of notification builder is set
as BigTextStyle. If your push notification is on most top of your notification bar, it’s expanded version is shown and your custom layout is hidden. By setting style to null, you replace
the style and also say that you will use custom layout for your push notifications.
Returning true in onNotificationProcessing method means that you want to disable default one signal notification manager’s behaviour and you also prevent duplicate push messages. I have used “customLayout” named
additional parameter for my push notifications sending from One Signal for differentiating normal and custom push notifications. If there is no “customLayout” parameter
in push notification, returning false will do the trick and one signal default notification manager will show the default push notification.
One last thing to do is defining your extender service in manifest file.