How to add NodeJs, S3, heroku direct upload to android? - android

I am trying to make sense of how to convert how they provide the example from the website to android/retrofit...
This is the code example for the website:
function get_signed_request(file){
var xhr = new XMLHttpRequest();
xhr.open("GET", "/sign_s3?file_name="+file.name+"&file_type="+file.type);
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status === 200){
var response = JSON.parse(xhr.responseText);
upload_file(file, response.signed_request, response.url);
}
else{
alert("Could not get signed URL.");
}
}
};
xhr.send();
}
function upload_file(file, signed_request, url){
var xhr = new XMLHttpRequest();
xhr.open("PUT", signed_request);
xhr.setRequestHeader('x-amz-acl', 'public-read');
xhr.onload = function() {
if (xhr.status === 200) {
document.getElementById("preview").src = url;
document.getElementById("avatar_url").value = url;
}
};
xhr.onerror = function() {
alert("Could not upload file.");
};
xhr.send(file);
}
This is my android retrofit attempt:
#GET("/sign_s3")
public void getSign(
#Query("name") String userId,
#Query("type") String type,
Callback<UserResponse> callback);
#Multipart
#PUT("/{url}")
public void sendMedia(
#Path("url") String signRequest,
#Part("theNameToUse") String theNameToUse,
#Part("isItAPicture") boolean isItAPicture, //if true it is a picture
#Part("media") TypedFile media,
Callback<UserResponse> callback);
the User Response:
ApiManager.getAsyncApi().getSign(name, type, new Callback<UserResponse>() {
#Override
public void success(UserResponse userResponse, Response response) {
sendMedia(response.signed_request);
}
#Override
public void failure(RetrofitError error) {
throw error;
}
});
private sendApi(path) {
ApiManager.getAsyncApi().sendMedia(path, title, isPictureNotvideo, media, new Callback<UserResponse>() {
#Override
public void success(UserResponse userResponse, Response response) {
}
#Override
public void failure(RetrofitError error) {
throw error;
}
});
}
the link to the heroku document is: https://devcenter.heroku.com/articles/s3-upload-node
response.signed_request does not work and is not an option... if I do response.getBody() that is the only thing I can see or to get the headers.... not sure how to grab the signed request...

Related

Android WebRTC remote stream not displaying on SurfaceView, getting 0 frames

I'm attempting to stream my webcam locally from a webpage on my computer to an Android app (native WebRTC). I'm using WebRTC for the peer connection and NodeJS with Socket.io for signaling. When I initiate the video stream all sdp appears to be set correctly, but no track plays on my Andorid surface view. All I get is a black screen and this console output:
2020-08-19 13:23:19.667 30492-31575/com.example.demowebrtcclient I/org.webrtc.Logging: EglRenderer: video_viewDuration: 4050 ms. Frames received: 0. Dropped: 0. Rendered: 0. Render fps: .0. Average render time: NA. Average swapBuffer time: NA.
Here's my code, sorry there's so much I just don't know where the root of the problem is.
Peer 1 (Webpage, JS)
navigator.mediaDevices.getUserMedia({audio: true, video: true})
.then(function(s) {
stream = s;
video.srcObject = stream;
video.play();
});
function startStream() {
socket.emit('broadcaster');
}
socket.on('answer', function(id, description) {
let RTCDescription = description;
if (isAndroid) {
RTCDescription = new RTCSessionDescription();
RTCDescription.sdp = description;
RTCDescription.type = "answer";
console.log("Answer received");
}
console.log(RTCDescription);
peerConnection.setRemoteDescription(RTCDescription);
});
socket.on('watcher', function(id) {
peerConnection = new RTCPeerConnection(config);
// peerConnections[id] = peerConnection;
stream.getTracks().forEach(track => peerConnection.addTrack(track, stream));
peerConnection.createOffer()
.then(function(sdp) {
peerConnection.setLocalDescription(sdp);
})
.then(function() {
socket.emit('offer', id, peerConnection.localDescription);
})
peerConnection.onicecandidate = function(event) {
if (event.candidate) {
socket.emit('candidate', id, event.candidate);
}
};
});
socket.on('candidate', function(id, candidate) {
console.log("Candidate recieved");
peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
});
socket.on('close', function(id) {
peerConnection.close();
delete peerConnection;
});
Peer 2 (Android, Java)
private void initializePeerConnectionFactory() {
PeerConnectionFactory.InitializationOptions initOptions = PeerConnectionFactory.InitializationOptions.builder(getApplicationContext())
.setEnableInternalTracer(true)
.setFieldTrials("WebRTC-H264HighProfile/Enabled/")
.createInitializationOptions();
PeerConnectionFactory.initialize(initOptions);
VideoEncoderFactory defaultVideoEncoderFactory = new DefaultVideoEncoderFactory(rootEglBaseContext, /* enableIntelVp8Encoder */true, /* enableH264HighProfile */true);
VideoDecoderFactory defaultVideoDecoderFactory = new DefaultVideoDecoderFactory(rootEglBaseContext);
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
options.disableEncryption = true;
options.disableNetworkMonitor = true;
factory = PeerConnectionFactory.builder()
.setVideoEncoderFactory(defaultVideoEncoderFactory)
.setVideoDecoderFactory(defaultVideoDecoderFactory)
.setOptions(options)
.createPeerConnectionFactory();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rootEglBase = EglBase.create();
rootEglBaseContext = rootEglBase.getEglBaseContext();
videoView = findViewById(R.id.video_view);
videoView.setMirror(true);
videoView.setEnableHardwareScaler(true);
videoView.init(rootEglBaseContext, null);
videoSink = new ProxyVideoSink();
iceServers = new ArrayList<>();
stunServer = (PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer());
iceServers.add(stunServer);
executor = Executors.newSingleThreadScheduledExecutor();
mediaConstraints = new MediaConstraints();
mSocket.on(Socket.EVENT_CONNECT, onConnect);
mSocket.on("offer", handleOffer);
mSocket.on("broadcaster", onBroadcast);
mSocket.on("candidate", onCandidate);
mSocket.connect();
initializePeerConnectionFactory();
}
private Emitter.Listener handleOffer = new Emitter.Listener() {
#Override
public void call(final Object... args) {
Log.d("socket", "Offer recieved");
peerConnection = factory.createPeerConnection(iceServers, observer);
String id = (String) args[0];
JSONObject data = (JSONObject) args[1];
peerConnection.setRemoteDescription(new SdpAdapter("setremote") {
#Override
public void onSetSuccess() {
peerConnection.createAnswer(new SdpAdapter("createanswer") {
#Override
public void onCreateSuccess(final SessionDescription sdp) {
super.onCreateSuccess(sdp);
Log.d("socket", "Session description " + sdp.toString() + " created");
SdpAdapter local = new SdpAdapter("setlocal") {
#Override
public void onSetSuccess() {
super.onSetSuccess();
mSocket.emit("answer" , id, peerConnection.getLocalDescription().description);
}
};
peerConnection.setLocalDescription(local, sdp);
}
}, mediaConstraints);
}
}, new SessionDescription(SessionDescription.Type.OFFER, data.optString("sdp")));
}
};
private Emitter.Listener onConnect = new Emitter.Listener() {
#Override
public void call(final Object... args) {
Log.d("socket", "Socket connected");
mSocket.emit("watcher");
}
};
private Emitter.Listener onBroadcast = new Emitter.Listener() {
#Override
public void call(final Object... args) {
Log.d("socket", "Received broadcast request");
mSocket.emit("watcher");
}
};
private Emitter.Listener onCandidate = new Emitter.Listener() {
#Override
public void call(final Object... args) {
Log.d("ice", "New ice candidate recieved");
JSONObject data = (JSONObject) args[1];
IceCandidate candidate = new IceCandidate(data.optString("sdpMid"), Integer.parseInt(data.optString("sdpMLineIndex")), data.optString("candidate"));
peerConnection.addIceCandidate(candidate);
}
};
public class SdpAdapter implements SdpObserver {
private String name;
public SdpAdapter(String name) {
this.name = name;
}
#Override
public void onCreateSuccess(SessionDescription sessionDescription) {
Log.d("socket", "SDP create success for " + name );
}
#Override
public void onSetSuccess() {
Log.d("socket", "SDP set success for " + name);
}
#Override
public void onCreateFailure(String s) {
Log.d("socket", "SDP create failure for" + s);
}
#Override
public void onSetFailure(String s) {
Log.d("socket", "SDP set failure for" + s);
}
};
public static class ProxyVideoSink implements VideoSink {
private VideoSink mTarget;
#Override
synchronized public void onFrame(VideoFrame frame) {
if (mTarget == null) {
Log.d("socket", "Dropping frame in proxy because target is null.");
return;
}
mTarget.onFrame(frame);
}
synchronized void setTarget(VideoSink target) {
this.mTarget = target;
}
}
Observer observer = new Observer() {
#Override
public void onIceCandidate(IceCandidate iceCandidate) {
Log.d("ice", iceCandidate.toString());
mSocket.emit("candidate", iceCandidate);
}
#Override
public void onAddStream(MediaStream stream) {
executor.execute(new Runnable() {
#Override
public void run() {
Log.d("socket", "Stream added");
Log.d("tracks", "Getting tracks");
VideoTrack remoteVideoTrack = (VideoTrack)stream.videoTracks.get(0);
AudioTrack remoteAudioTrack = (AudioTrack)stream.audioTracks.get(0);
Log.d("tracks", "Enabling tracks");
remoteAudioTrack.setEnabled(true);
remoteVideoTrack.setEnabled(true);
Log.d("tracks", "Adding sink");
videoSink.setTarget(videoView);
remoteVideoTrack.addSink(videoSink);
peerConnection.getStats(reports -> {
for (StatsReport report : reports) {
Log.d("Stats", "Stats: " + report.toString());
}
}, null);
}
});
}
};
}
Disabling encryption in Android will break interoperability with the browser, unless you start them in a special (insecure) mode.
options.disableEncryption = true;
A good point to get started with the Android native WebRTC API is by looking at the example App code:
https://chromium.googlesource.com/external/webrtc/+/refs/heads/master/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
I'll recommend to add more logs and extend the description / question.
Recommended procedure when debugging a WebRTC issue:
Did ICE succeed? (iceConnectionState is connected) If not, look at the gathered and received candidates. Check involved firewall configurations, NAT and TURN.
Do you receive packets but no frames can be rendered? Check your codecs and encoders / decoders.

How to handle errors from API in Login Activity (Template from Android Studio)

I'm implementing a simple login to an endpoints using Retrofit2. Things work fine when the user credentials are correct but break when I try to enter a non valid data.
I'm trying to handle the errors when the user is not found but I can't find a way to do that.
The error response looks like:
{
"0": [
"erreur",
"statut"
],
"erreur": "Erreur, connexion echoue.",
"statut": "KO"
}
This response has status 200 despite being an error.
The app is crashing with NPE in the LoginRepository where I'm trying to save user's data to SharedPreferences because the error result is not handled so the app threat any response as Successful.
The sample provides a Result class which doesn't seem to work for my use case because the response is always successful:
public class Result<T> {
// hide the private constructor to limit subclass types (Success, Error)
private Result() {
}
#Override
public String toString() {
if (this instanceof Result.Success) {
Result.Success success = (Result.Success) this;
return "Success[data=" + success.getData().toString() + "]";
} else if (this instanceof Result.Error) {
Result.Error error = (Result.Error) this;
return "Error[exception=" + error.getError().toString() + "]";
}
return "";
}
// Success sub-class
public final static class Success<T> extends Result {
private T data;
public Success(T data) {
this.data = data;
}
public T getData() {
return this.data;
}
}
// Error sub-class
public final static class Error extends Result {
private Exception error;
public Error(Exception error) {
this.error = error;
}
public Exception getError() {
return this.error;
}
}
}
And here is how I'm handling the login in the LoginRepository:
public Result<LoggedInUser> login(String username, String password) {
// handle login
Result<LoggedInUser> result = dataSource.login(username, password);
if (result instanceof Result.Success) {
setLoggedInUser(((Result.Success<LoggedInUser>) result).getData());
}
return result;
}
Note: I don't have access to the server. I use Gson as converter
The login activity sample I used can be found here
UPDATE:
Login successful with valid credentials:
Check this answer it will help you.
#POST("end_path")
Call<ResponseBody> LoginCall(
#Field("email") String user_id,
#Part("paassword") String language
);
Call<ResponseBody> call = Constant.service.LoginCall(
"email", "pass");
call.enqueue(new retrofit2.Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call,
Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
String responseData = response.body().string();
JSONObject object = new JSONObject(responseData);
if(object.getString("statut").equalsIgnoreCase("success")){
LoggedInUser successData = new
Gson().fromJson(responseData, LoggedInUser.class);
}else{
showToast("Email password incorrect");//or show you want
this message.
}
} catch (IOException e) {
e.printStackTrace();
} catch (JsonSyntaxException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
} else {
showToast("something_went_wrong");
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
t.printStackTrace();
}
});

Android App , how to communicate between Activity, Service and SyncAdapter

Hi Im working on an android app which works both online and offline. So if its connected to wifi , when i click on save button in the Activity , there are multiple requests (StringRequest) sent to server to update the db. And if offline it saves the data to sqlite and once wifi is available, it automatically syncs the data using SyncAdapter. My problem is there is lot of redundant/duplicate code - one for Activity and one for SyncAdapter and i'm trying to cleanup the code. The only difference between the both for each StringRequests in Activity , Response.Listener & Response.ErrorListener shows some alerts in the Activity and in SyncAdapter it just logs the messages.
Now i have written a Service class that can be used by both activity and syncadapter , the question i have is how can i send alerts/updates to activity in the Response.Listener & Response.ErrorListener , without breaking the flow of service methods.
private void Method1 () {
if (NetworkConnection.isNetworkAvailable (this))
{
StringRequest strRequest = new StringRequest (Request.Method.POST, AppConfigURL.API_URL,
new Response.Listener<String> () {
#Override
public void onResponse (String response) {
if (response != null) {
Utils.showLog (Log.INFO, AppConfigTags.SERVER_RESPONSE, response, true);
} else {
Utils.showOkDialog (MainActivity.this, "Custom alert message", false);
}
Method2();
}
},
new Response.ErrorListener () {
#Override
public void onErrorResponse (VolleyError error) {
Utils.showLog (Log.ERROR, AppConfigTags.VOLLEY_ERROR, error.toString (), true);
Method2();
}
}) {
#Override
public byte[] getBody () throws com.android.volley.AuthFailureError {
JSONObject sendJSON = db.getOfflineSavedOrderDetails();
String str = "{\"API_username\":\"" + Constants.api_username + "\",\n" +
"\"API_password\":\"" + Constants.api_password + "\",\n" +
"\"API_function\":\"updateSavedFixedDetails\",\n" +
"\"API_parameters\": " + String.valueOf(sendJSON) + "}";
Utils.showLog (Log.INFO, AppConfigTags.PARAMETERS_SENT_TO_THE_SERVER, str, true);
return str.getBytes ();
}
public String getBodyContentType () {
return "application/json; charset=utf-8";
}
};
AppController.getInstance().addToRequestQueue(strRequest);
} else {
Method2();
}
}
private void Method2 () {
if (NetworkConnection.isNetworkAvailable (this))
{
StringRequest strRequest = new StringRequest (Request.Method.POST, AppConfigURL.API_URL,
new Response.Listener<String> () {
#Override
public void onResponse (String response) {
if (response != null) {
Utils.showLog (Log.INFO, AppConfigTags.SERVER_RESPONSE, response, true);
} else {
Utils.showLog (Log.WARN, AppConfigTags.SERVER_RESPONSE, AppConfigTags.DIDNT_RECEIVE_ANY_DATA_FROM_SERVER, true);
}
Method3();
}
},
new Response.ErrorListener () {
#Override
public void onErrorResponse (VolleyError error) {
Utils.showOkDialog (MainActivity.this, "Connection Error: My custom error", false);
Method3();
}
}) {
#Override
public byte[] getBody () throws com.android.volley.AuthFailureError {
JSONObject sendJSON = db.getOfflineSavedOrderDetails();
String str = "{\"API_username\":\"" + Constants.api_username + "\",\n" +
"\"API_password\":\"" + Constants.api_password + "\",\n" +
"\"API_function\":\"updateContractDetails\",\n" +
"\"API_parameters\": " + String.valueOf(sendJSON) + "}";
Utils.showLog (Log.INFO, AppConfigTags.PARAMETERS_SENT_TO_THE_SERVER, str, true);
return str.getBytes ();
}
public String getBodyContentType () {
return "application/json; charset=utf-8";
}
};
AppController.getInstance().addToRequestQueue(strRequest);
} else {
Method3();
}
}

Linkedin login android SDK access token is not set

I implement LinkedIn Register in to my android app... I use LinkedIn Android SDK:
link and i follow these tutorial: tut
However I stuck in one big problem. I successful get token by method:
LISessionManager.getInstance(getContext()).init(mLoginActivity, buildScope(), new AuthListener() {
And after it i try to make APIHelper request to get profile data:
APIHelper apiHelper = APIHelper.getInstance(getContext());
apiHelper.getRequest(mLoginActivity, topCardUrl, new ApiListener() {
#Override
public void onApiSuccess(ApiResponse s) {
But every time i try to get profile data i get following result:
exceptionMsg: access toke is not set
How can i fix it? I can't understand where is problem. Please help.
Linkedin currently stopped support for Mobile SDK that's why all started getting access token is not set.
For more information https://engineering.linkedin.com/blog/2018/12/developer-program-updates
Alternate to achieve the same in Android by this https://stackoverflow.com/a/22219383
public void login(){
LISessionManager.getInstance(getActivity()).init(getActivity(),
buildScope(), new AuthListener()
{
#Override
public void onAuthSuccess()
{
progress.show();
getUserData();
}
#Override
public void onAuthError(LIAuthError error) {
if(progress.isShowing())
progress.dismiss();
}
}, false);
}
private static Scope buildScope() {
return Scope.build(Scope.R_BASICPROFILE, Scope.R_EMAILADDRESS);
}
String u="https://api.linkedin.com/v1/people/~:(id,first-name,last- name,headline,picture-url,public_profile_url,industry,summary,specialties,picture-urls::(original),positions:(id,title,summary,start-date,end-date,is-current,company:(id,name,type,size,industry,ticker)),educations:(id,school-name,field-of-study,start-date,end-date,degree,activities,notes),associations,interests,num-recommenders,date-of-birth,publications:(id,title,publisher:(name),authors:(id,name),date,url,summary),patents:(id,title,summary,number,status:(id,name),office:(name),inventors:(id,name),date,url),languages:(id,language:(name),proficiency:(level,name)),skills:(id,skill:(name)),certifications:(id,name,authority:(name),number,start-date,end-date),courses:(id,name,number),recommendations-received:(id,recommendation-type,recommendation-text,recommender),honors-awards,three-current-positions,three-past-positions,volunteer)";
public void getUserData()
{
APIHelper apiHelper = APIHelper.getInstance(getActivity());
apiHelper.getRequest(getActivity(), u, new ApiListener() {
#Override
public void onApiSuccess(ApiResponse result) {
try {
//progress.dismiss();
socialData = new SocialLoginBean();
JSONObject json = result.getResponseDataAsJson();
String fname = json.getString("firstName");
String lname = json.getString("lastName");
socialData.setFname(fname);
socialData.setLname(lname);
JSONObject pic = json.getJSONObject("pictureUrls");
JSONArray picarr = pic.getJSONArray("values");
profile_pic = picarr.getString(0);
socialData.setPictureUrl(profile_pic);
JSONObject obj = json.getJSONObject("positions").getJSONArray("values").getJSONObject(0);
JSONObject positions = obj.getJSONObject("company");
String companyDesc="",companyName="",industry="",jobTitle="";
if(obj.has("title"))
jobTitle = obj.getString("title");
if(obj.has("summary"))
companyDesc = obj.getString("summary");
if(positions.has("name"))
companyName = positions.getString("name");
if(json.has("industry"))
industry = json.getString("industry");
String publicProfileUrl = json.getString("publicProfileUrl");
socialData.setProfileUrl(publicProfileUrl);
socialData.setIndustry(industry);
socialData.setCompany(companyName);
socialData.setDesignation(jobTitle);
socialData.setCompanyDescription(companyDesc);
moveTo(socialData);
} catch (Exception e) {
e.printStackTrace();
if(progress.isShowing())
progress.dismiss();
moveTo(socialData);
}
}
#Override
public void onApiError(LIApiError error) {
if(progress.isShowing())
progress.dismiss();
}
});
}

Retrofit and Centralized Error Handling

Each request to the server may return error_code. I want to handle these error in one place
when I was using AsyncTask I had a BaseAsyncTask like that
public abstract class BaseAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
protected Context context;
private ProgressDialog progressDialog;
private Result result;
protected BaseAsyncTask(Context context, ProgressDialog progressDialog) {
this.context = context;
this.progressDialog = progressDialog;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(Result result) {
super.onPostExecute(result);
HttpResponse<ErrorResponse> response = (HttpResponse<ErrorResponse>) result;
if(response.getData().getErrorCode() != -1) {
handleErrors(response.getData());
}else
onResult(result);
}
private void handleErrors(ErrorResponse errorResponse) {
}
public abstract void onResult(Result result);
}
But, using retrofit each request has its error handling callback:
git.getFeed(user,new Callback<gitmodel>() {
#Override
public void success(gitmodel gitmodel, Response response) {
}
#Override
public void failure(RetrofitError error) {
}
});
}
});
How can I handle all errors in one place?
If you need to get some 'logic' error, then you need some Java logic since it's not a Retrofit feature so basically:
Create a Your implementation Callback that implements the Retrofit Callback
Create a base object that define the method 'isError'
Modify Retrofit RestAdapter in order to get your Callback instead of the Retrofit One
MyCallback.java
import android.util.Log;
import retrofit.Callback;
import retrofit.client.Response;
public abstract class MyCallback<T extends MyObject> implements Callback<T> {
#Override
public final void success(T o, Response response) {
if (o.isError()) {
// [..do something with error]
handleLogicError(o);
}
else {
handleSuccess(o, response);
}
}
abstract void handleSuccess(T o, Response response);
void handleLogicError(T o) {
Log.v("TAG", "Error because userId is " + o.id);
}
}
MyObject.java (the base class for all your objects you get from Retrofit)
public class MyObject {
public long id;
public boolean isError() {
return id == 1;
}
}
MyRealObject.java - a class that extends the base object
public class MyRealObject extends MyObject {
public long userId;
public String title;
public String body;
}
RetroInterface.java - the interface used by retrofit you should be familiar with
import retrofit.http.GET;
import retrofit.http.Path;
public interface RetroInterface {
#GET("/posts/{id}")
void sendGet(#Path("id") int id, MyCallback<MyRealObject> callback);
}
And finally the piece of code where you use all the logic
RestAdapter adapter = new RestAdapter.Builder()
.setEndpoint("http://jsonplaceholder.typicode.com")
.build();
RetroInterface itf = adapter.create(RetroInterface.class);
itf.sendGet(2, new MyCallback<MyRealObject>() {
#Override
void handleSuccess(MyRealObject o, Response response) {
Log.v("TAG", "success");
}
#Override
public void failure(RetrofitError error) {
Log.v("TAG", "failure");
}
});
If you copy and paste this code, you'll get an error when you'll execute the itf.sendGet(1, new MyCallback..) and a success for itf.sendGet(2, new MyCallback...)
Not sure I understood it correctly, but you could create one Callback and pass it as a parameter to all of your requests.
Instead of:
git.getFeed(user,new Callback<gitmodel>() {
#Override
public void success(gitmodel gitmodel, Response response) {
}
#Override
public void failure(RetrofitError error) {
}
});
First define your Callback:
Callback<gitmodel> mCallback = new Callback<gitmodel>() {
#Override
public void success(gitmodel gitmodel, Response response) {
}
#Override
public void failure(RetrofitError error) {
// logic to handle error for all requests
}
};
Then:
git.getFeed(user, mCallback);
In Retrofit you can specify ErrorHandler to all requests.
public class ApiErrorHandler implements ErrorHandler {
#Override
public Throwable handleError(RetrofitError cause) {
//here place your logic for all errors
return cause;
}
}
Apply it to RestAdapter
RestAdapter.Builder()
.setClient(client)
.setEndpoint(endpoint)
.setErrorHandler(errorHandler)
.build();
I think that it is what you asked for.
In Retrofit2 you can't set an ErrorHandler with the method .setErrorHandler(), but you can create an interceptor to fork all possible errors centralised in one place of your application.
With this example you have one centralised place for your error handling with Retrofit2 and OkHttpClient. Just reuse the Retrofit object (retrofit).
You can try this standalone example with a custom interceptor for network and server errors. These both will be handled differently in Retrofit2, so you have to check the returned error code from the server over the response code (response.code()) and if the response was not successful (!response.isSuccessful()).
For the case that the user has no connection to the network or the server you have to catch an IOException of the method Response response = chain.proceed(chain.request()); and handle the network error in the catch block.
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
try {
Response response = chain.proceed(chain.request());
if (!response.isSuccessful()) {
Log.e("tag", "Failure central - response code: " + response.code());
Log.e("tag", "central server error handling");
// Central error handling for error responses here:
// e.g. 4XX and 5XX errors
switch (response.code()) {
case 401:
// do something when 401 Unauthorized happened
// e.g. delete credentials and forward to login screen
// ...
break;
case 403:
// do something when 403 Forbidden happened
// e.g. delete credentials and forward to login screen
// ...
break;
default:
Log.e("tag", "Log error or do something else with error code:" + response.code());
break;
}
}
return response;
} catch (IOException e) {
// Central error handling for network errors here:
// e.g. no connection to internet / to server
Log.e("tag", e.getMessage(), e);
Log.e("tag", "central network error handling");
throw e;
}
}
})
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://10.0.2.2:8000/api/v1/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
UserRepository backendRepository = retrofit.create(UserRepository.class);
backendRepository.getUser("userId123").enqueue(new Callback<UserModel>() {
#Override
public void onResponse(Call<UserModel> call, retrofit2.Response<UserModel> response) {
Log.d("tag", "onResponse");
if (!response.isSuccessful()) {
Log.e("tag", "onFailure local server error handling code:" + response.code());
} else {
// its all fine with the request
}
}
#Override
public void onFailure(Call<UserModel> call, Throwable t) {
Log.e("tag", "onFailure local network error handling");
Log.e("tag", t.getMessage(), t);
}
});
UserRepository example:
public interface UserRepository {
#GET("users/{userId}/")
Call<UserModel> getUser(#Path("userId") String userId);
}
UserModel example:
public class UserModel implements Parcelable {
#SerializedName("id")
#Expose
public String id = "";
#SerializedName("email")
#Expose
public String mail = "";
public UserModel() {
}
protected UserModel(Parcel in) {
id = in.readString();
mail = in.readString();
}
public static final Creator<UserModel> CREATOR = new Creator<UserModel>() {
#Override
public UserModel createFromParcel(Parcel in) {
return new UserModel(in);
}
#Override
public UserModel[] newArray(int size) {
return new UserModel[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(mail);
}
}
Fairly simply Retrofit custom error handling example. Is set up so that you don't need to do much work in the 'failure' handler of a retrofit call to get the user-visible error message to show. Works on all endpoints. There's lots of exception handling as our server folks like to keep us on our toes by sending all kinds of random stuff..!
// on error the server sends JSON
/*
{ "error": { "data": { "message":"A thing went wrong" } } }
*/
// create model classes..
public class ErrorResponse {
Error error;
public static class Error {
Data data;
public static class Data {
String message;
}
}
}
//
/**
* Converts the complex error structure into a single string you can get with error.getLocalizedMessage() in Retrofit error handlers.
* Also deals with there being no network available
*
* Uses a few string IDs for user-visible error messages
*/
private static class CustomErrorHandler implements ErrorHandler {
private final Context ctx;
public CustomErrorHandler(Context ctx) {
this.ctx = ctx;
}
#Override
public Throwable handleError(RetrofitError cause) {
String errorDescription;
if (cause.isNetworkError()) {
errorDescription = ctx.getString(R.string.error_network);
} else {
if (cause.getResponse() == null) {
errorDescription = ctx.getString(R.string.error_no_response);
} else {
// Error message handling - return a simple error to Retrofit handlers..
try {
ErrorResponse errorResponse = (ErrorResponse) cause.getBodyAs(ErrorResponse.class);
errorDescription = errorResponse.error.data.message;
} catch (Exception ex) {
try {
errorDescription = ctx.getString(R.string.error_network_http_error, cause.getResponse().getStatus());
} catch (Exception ex2) {
Log.e(TAG, "handleError: " + ex2.getLocalizedMessage());
errorDescription = ctx.getString(R.string.error_unknown);
}
}
}
}
return new Exception(errorDescription);
}
}
// When creating the Server...
retrofit.RestAdapter restAdapter = new retrofit.RestAdapter.Builder()
.setEndpoint(apiUrl)
.setLogLevel(retrofit.RestAdapter.LogLevel.FULL)
.setErrorHandler(new CustomErrorHandler(ctx)) // use error handler..
.build();
server = restAdapter.create(Server.class);
// Now when calling server methods, get simple error out like this:
server.postSignIn(login,new Callback<HomePageResponse>(){
#Override
public void success(HomePageResponse homePageResponse,Response response){
// Do success things!
}
#Override
public void failure(RetrofitError error){
error.getLocalizedMessage(); // <-- this is the message to show to user.
}
});

Categories

Resources