Execute function in current activity after onReponse() of Retrofit itself outside a service

Viewed 132

I am currently working on an Android Application, and i have a problem to handle a request and execute a function just after. The fact is my Retrofit request is in a Controller, used by a Service, and i am calling the service function inside my Activity (am i clear?). Clearly, i have to manage one user (get and refresh access token from a webservice) and i need to be able to call my refreshToken() function and execute some code after getting and parsing the response.

This is my code :

UserActivity

public class UserActivity extends AppCompatActivity {

    private final static String TAG = "UserActivity";
    private User user;
    private TextView textViewAccessTokenShow, textViewExpiresInShow, textViewIGPShow, textViewRefreshTokenShow;
    private LoginController loginController;
    private Wso2Service wso2Service, wso2ServiceIS;
    boolean mBounded;
    private LoginService loginService;
    private Intent mIntent;


    //Connection to LoginService
    ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceDisconnected(ComponentName name) {
        Toast.makeText(UserActivity.this, "Service is disconnected", Toast.LENGTH_LONG).show();
        mBounded = false;
        loginService = null;
        Log.e(TAG, "onServiceDisconnected: " );
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Toast.makeText(UserActivity.this, "Service is connected", Toast.LENGTH_LONG).show();
        mBounded = true;
        LoginService.LocalBinder mLocalBinder = (LoginService.LocalBinder) service;
        loginService = mLocalBinder.getServerInstance();
        user = loginService.getUser();
        refreshIHM();


    }
};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user);

        mIntent = new Intent(this, LoginService.class);
        textViewAccessTokenShow = findViewById(R.id.textViewAccessTokenShow);
        textViewRefreshTokenShow = findViewById(R.id.textViewRefreshTokenShow);
        textViewExpiresInShow = findViewById(R.id.textViewExpiresInShow);
        textViewIGPShow = findViewById(R.id.textViewIGPShow);

    }



    @Override
    protected void onResume() { //Getting my user updated outside this activity and printing his informations
        super.onResume();
        Log.i(TAG, "onResume: ");
        if(mBounded == false){
            bindService(mIntent, mConnection, BIND_AUTO_CREATE);
        } else {
            user = loginService.getUser();
            refreshIHM();
        }
    }


    public void onClickRefreshToken(View view){
       //Where i have to refresh my token, and after that executing refreshIHM()

        refreshIHM();

    }


    public void refreshIHM(){
        Log.d(TAG, "refreshIHM() called");
        Log.i(TAG, "refreshIHM: "+user.toString());
        textViewExpiresInShow.setVisibility(View.VISIBLE);
        textViewAccessTokenShow.setVisibility(View.VISIBLE);
        textViewRefreshTokenShow.setVisibility(View.VISIBLE);
        textViewIGPShow.setVisibility(View.VISIBLE);

        textViewAccessTokenShow.setText(user.getAccess_token());
        textViewAccessTokenShow.invalidate();
        textViewAccessTokenShow.requestLayout();

        textViewRefreshTokenShow.setText(user.getRefresh_token());
        textViewRefreshTokenShow.invalidate();
        textViewRefreshTokenShow.requestLayout();

        textViewExpiresInShow.setText(String.valueOf(user.getExpire_in()));
        textViewExpiresInShow.invalidate();
        textViewExpiresInShow.requestLayout();

        textViewIGPShow.setText(user.getId_group_parent());
        textViewIGPShow.invalidate();
        textViewIGPShow.requestLayout();
    }
}


LoginController, where i execute every functions about User data

public class LoginController {

    public static final String TAG = "LOGINSERVICE";
    private User usertemp;

    private Wso2Service wso2Service, wso2ServiceIS;

    public LoginController(){
        this.wso2Service = new Retrofit.Builder()
                .baseUrl(Wso2Service.APIMENDPOINT)
                .addConverterFactory(ScalarsConverterFactory.create())
                .build()
                .create(Wso2Service.class);

        this.wso2ServiceIS = new Retrofit.Builder()
                .baseUrl(Wso2Service.ISENDPOINT)
                .addConverterFactory(ScalarsConverterFactory.create())
                .build()
                .create(Wso2Service.class);

    }

    public User parseUserInfo(String request, User user) {
        try {
            JSONObject jo = new JSONObject(request);
            user.setAccess_token(jo.getString("access_token"));
            user.setRefresh_token(jo.getString("refresh_token"));
            user.setScope(jo.getString("scope"));
            user.setId_token(jo.getString("id_token"));
            user.setToken_type(jo.getString("token_type"));
            user.setExpire_in(jo.getInt("expires_in"));


            return user;
        } catch (Exception e){
            Log.e(TAG, "getUserInfo: "+e.toString());
        }
        return null;
    }

    public User parseIdGroupParentInfo(String request, User user){
        try {
            Log.i(TAG, "parseIdGroupParentInfo: "+request);
            JSONObject jo = new JSONObject(request);
            user.setId_group_parent(jo.getString("id_group_parent"));

            return user;
        } catch (Exception e){
            Log.e(TAG, "parseIdGroupParentInfo: "+e.toString());
        }
        return null;
    }


    public void refreshToken(User user){
        this.usertemp = user;
        Log.i(TAG, "refreshToken: ");
        this.wso2Service.getTokensByRefresh("refresh_token",user.getRefresh_token(),"openid", ApiConstants.token).enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
                if(response.isSuccessful()) {
                    //On parse la réponse
                    usertemp.setLogin_request_responseJSON(response.body());
                    parseUserInfo(response.body(), usertemp);
                    Log.i(TAG, "onLoginReady: " + usertemp.toString());
                    wso2ServiceIS.getUserInfo("Bearer "+usertemp.getAccess_token()).enqueue(new Callback<String>() {
                        @Override
                        public void onResponse(Call<String> call, Response<String> response) {
                            Log.i(TAG, "onResponse: "+response.code()+response.body());
                            usertemp = parseIdGroupParentInfo(response.body(),usertemp);
                        }

                        @Override
                        public void onFailure(Call<String> call, Throwable t) {
                            Log.e(TAG, "onFailure: ",t );
                        }
                    });
                } else {
                    Log.e(TAG, "onResponse: " );
                    Log.e(TAG, "onResponse: Code "+response.code()+" Body : "+response.body() );
                }
            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {
                Log.e(TAG, "onFailure: ",t );
            }
        });
}

 }

LoginService, what i call in every activities to use the same User everytime

public class LoginService extends Service {
    public final String TAG = "LoginService";

    private User user;
    private LoginController loginController;
    IBinder mBinder = new LocalBinder();

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    public class LocalBinder extends Binder {
        public LoginService getServerInstance() {
            return LoginService.this;
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand: ");
        this.user = (User)intent.getSerializableExtra("user");
        Log.i(TAG, "onStartCommand: "+user.toString());

        loginController = new LoginController();


        return START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "onCreate: ");
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy: ");
        super.onDestroy();
    }

    public User getUser(){
        Log.i(TAG, "getUser: ");
        return this.user;
    }

    public void regenerateByRefreshToken(){
        Log.d(TAG, "regenerateByRefreshToken: ");
        loginController.refreshToken(user);
        Log.d(TAG, "regenerateByRefreshToken: end");
    }
}

Do you have any idea about how to make my RefroFit function handle its response and only after executing another function inside my UI ? Or inside my regenerateByRefreshToken() function ?

Thank you !

1 Answers

Do you have any idea about how to make my RefroFit function handle its response and only after executing another function inside my UI ? Or inside my regenerateByRefreshToken() function ?

As per the current implementation, You can achieve this using Callbacks. Create two callbacks to

  1. Get the usertemp inside service from the controller after successful execution.

  2. Second callback to send the user object back to activity from service

So follow below steps:

a) Create callback interface

// create new OnUserRefresh.java
public interface OnUserRefresh{
    void onRefresh(User user);
    void onError(Throwable t);
}

b) Modify the controller to receive the callback reference

public class LoginController {

    // code...

    public void refreshToken(User user, OnUserRefresh onUserRefresh){
        this.usertemp = user;
        Log.i(TAG, "refreshToken: ");
        this.wso2Service.getTokensByRefresh("refresh_token",user.getRefresh_token(),"openid", ApiConstants.token).enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
                if(response.isSuccessful()) {
                    //On parse la réponse
                    usertemp.setLogin_request_responseJSON(response.body());
                    parseUserInfo(response.body(), usertemp);
                    Log.i(TAG, "onLoginReady: " + usertemp.toString());
                    wso2ServiceIS.getUserInfo("Bearer "+usertemp.getAccess_token()).enqueue(new Callback<String>() {
                        @Override
                        public void onResponse(Call<String> call, Response<String> response) {
                            Log.i(TAG, "onResponse: "+response.code()+response.body());
                            usertemp = parseIdGroupParentInfo(response.body(),usertemp);
                            onUserRefresh.onRefresh(usertemp);
                        }

                        @Override
                        public void onFailure(Call<String> call, Throwable t) {
                            Log.e(TAG, "onFailure: ",t );
                            onUserRefresh.onError(t);
                        }
                    });
                } else {
                    Log.e(TAG, "onResponse: " );
                    Log.e(TAG, "onResponse: Code "+response.code()+" Body : "+response.body() );
                }
            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {
                Log.e(TAG, "onFailure: ",t );
            }
        });
    }

}

c) Pass callback object from service to controller

public class LoginService extends Service {

    /*Add interface, to be used for data passing*/

    public void regenerateByRefreshToken(OnUserRefresh onUserRefresh){
        Log.d(TAG, "regenerateByRefreshToken: ");
        loginController.refreshToken(user, new OnUserRefresh(){

            @Override
            void onRefresh(User user){
                this.user = user;
                onUserRefresh.onRefresh(user); // trigger onRefresh in client i.e. activity 
            }

            @Override
            void onError(Throwable t){
                onUserRefresh.onError(t);
                // log error etc
            }

        });
        Log.d(TAG, "regenerateByRefreshToken: end");
    }

}

d) Pass callback object from activity to service and implement UI updates method call

public class UserActivity extends AppCompatActivity {

     public void onClickRefreshToken(View view){
       //Where i have to refresh my token, and after that executing refreshIHM()
        loginService.regenerateByRefreshToken(new OnUserRefresh(){

            @Override
            void onRefresh(User user){
                this.user = user;
                refreshIHM();
            }

            @Override
            void onError(Throwable t){
                // log error etc
            }

        });
    }

}

Note: The initial user reference is always null as you are receiving it from intent in your service

this.user = (User)intent.getSerializableExtra("user");

but you are neither initialising any user object in UserActivity nor adding it in the mIntent object so you need to a user object with token and other required properties in activity for network calls.

You can optimize the flow with lambdas, Rxjava etc as well.

Related