Search code examples
javalaravelandroid-studiojwt

Error Code 0 when trying to implement Laravel login in Android Studio


I am working on implementing a login system for my Android application using Laravel as the backend. The Laravel code seems to be functioning correctly as I have tested it using Postman, and it returns the expected results. However, when I try to integrate the login functionality into my Android app using Java in Android Studio, I keep encountering an error with code 0.

I have double-checked my code for any potential errors, but I haven't been able to identify the cause of this issue. I suspect it might be related to how I am handling the HTTP request or how I am parsing the response.

Here is a snippet of my Android code for the login functionality:

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import org.json.JSONException;
import org.json.JSONObject;

public class Login extends AppCompatActivity {


    EditText etEmail, etPassword;
    Button bnLogin;
    String email, password;
    LocalStorage localStorage;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        localStorage = new LocalStorage(Login.this);

        etEmail = findViewById(R.id.editTextTextEmailAddress2);
        etPassword = findViewById(R.id.editTxtPhone);

        bnLogin = findViewById(R.id.btn_login);
        bnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                checkLogin();
            }
        });
    }

    private  void checkLogin(){
        email = etEmail.getText().toString();
        password = etPassword.getText().toString();
        if(email.isEmpty() || password.isEmpty()){
            Toast.makeText(this, "Email and Password is required", Toast.LENGTH_SHORT).show();
        }
        else {
            sendLogin();
            Toast.makeText(this, "work and still ", Toast.LENGTH_SHORT).show();
        }
    }

    private void sendLogin(){
        JSONObject params = new JSONObject();
        try {
            params.put("email", email);
            params.put("password", password);
            String data = params.toString();
            String url = getString(R.string.api_server) + "login";
            // Log the data before sending the request
            Log.d("https send data", data);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Http http = new Http(Login.this, url);
                    http.setMethod("post");
                    http.setData(data);
                    http.send();
                    Log.d("https send data",   data);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Integer code = http.getStatusCode();
                            if(code == 200) {
                                try {
                                    JSONObject response = new JSONObject(http.getResponse());
                                    String token = response.getString("token");
                       
                                    localStorage.setToken(token);

                                    Intent intent = new Intent(Login.this, Dashborad.class);
                                    startActivity(intent);
                                    finish();
                                }catch (JSONException e){
                                    e.printStackTrace();
                                }
                            }
                            else if(code == 422) {
                                try {
                                    JSONObject response = new JSONObject(http.getResponse());
                                    String msg = response.getString("message");
                                    Toast.makeText(Login.this, msg, Toast.LENGTH_SHORT).show();
                                }catch (JSONException e){
                                    e.printStackTrace();
                                }
                            }
                            else if(code == 401) {
                                try {
                                    JSONObject response = new JSONObject(http.getResponse());
                                    String msg = response.getString("message");
                                    Toast.makeText(Login.this, msg, Toast.LENGTH_SHORT).show();
                                }catch (JSONException e){
                                    e.printStackTrace();
                                }
                            }
                            else {
                                Toast.makeText(Login.this, "Error "+code , Toast.LENGTH_SHORT).show();
                            }
                        }
                    });
                }
            }).start();


        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
    }

//Http Class

package com.example.myapplication;

import android.content.Context;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class Http {
    Context context;
    private String url, method="GET", data=null, response=null;
    private Integer statusCode = 0;
    private Boolean token = false;
    private LocalStorage localStorage;

    // Constructor with context and localStorage
    public Http(Context context, String url) {
        this.context = context;
        this.url = url;
        localStorage = new LocalStorage (context);
    }

    public void setMethod(String method) {
        this.method = method.toUpperCase();
    }

    public void setData(String data) {
        this.data = data;
    }

    public void setToken(Boolean token) {
        this.token = token;
    }

    public String getResponse() {
        return response;
    }

    public Integer getStatusCode() {
        return statusCode;
    }

    public void send(){
        try{
            URL surl = new URL(url);
            HttpURLConnection connection = (HttpURLConnection) surl.openConnection();
            connection.setRequestMethod(method);
            connection.setRequestProperty("Content-Type","application/json");
            connection.setRequestProperty("X-Requested-With", "WMLHttpRequest");
            if(token){
                connection.setRequestProperty("Authorization", "Bearer "+localStorage.getToken());
            }
            if(! method.equals("POST")){
                connection.setDoOutput(true);
            }
            if(data !=null){
                OutputStream os = connection.getOutputStream();
                os.write(data.getBytes());
                os.flush();
                os.close();
            }
            statusCode = connection.getResponseCode();

            InputStreamReader isr;
            if(statusCode >= 200 && statusCode <= 299){
                //if success response
                isr = new InputStreamReader(connection.getInputStream());
            }else {
                //if error response
                isr = new InputStreamReader(connection.getErrorStream());
            }

            BufferedReader br = new BufferedReader(isr);
            StringBuffer sb = new StringBuffer();
            String line;
            while ((line = br.readLine()) != null){
                sb.append(line);
            }
            br.close();
            response = sb.toString();
        }catch (IOException e) {
            e.printStackTrace();
        }
    }
}

//local storage 
package com.example.myapplication;

import android.content.Context;
import android.content.SharedPreferences;

public class LocalStorage {
    SharedPreferences sharedPreferences ;
    SharedPreferences.Editor editor;
    Context context;
    String token;

    public LocalStorage(Context context){
        this.context = context;
        sharedPreferences = context.getSharedPreferences("STORAGE_LOGIN_API", Context.MODE_PRIVATE);
        editor = sharedPreferences.edit();
    }

    public String getToken() {
        token = sharedPreferences.getString("TOKEN", "");
        return token;
    }

    public  void setToken(String token) {
        editor.putString("TOKEN", token);
        editor.commit();
        this.token = token;
    }
}
//String.xml
<resources>
    <string name="app_name">My Application</string>
    <string name="api_server">http://192.168.1.18:8000/api/</string>
</resources>

And here is the relevant portion of the Laravel code:

<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpFoundation\Response;
use App\Models\User;
use Illuminate\Support\Facades\Validator;

class AuthController extends Controller
{

      /**
     * Create a new AuthController instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth:api', ['except' => ['login', 'register']]);
    }

    public function index(Request $request){
        return $request->user();
    }

    public function login(Request $request) {
        $credentials = request(['email', 'password']);

        if (! $token = auth()->attempt($credentials)) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return $this->respondWithToken($token);
    }


       /**
     * Register a User.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function register() {
        $validator = Validator::make(request()->all(), [
            'name' => 'required',
            'email' => 'required|email|unique:users',
            'password' => 'required|confirmed|min:8',
        ]);

        if($validator->fails()){
            return response()->json($validator->errors()->toJson(), 400);
        }

        $user = new User;
        $user->name = request()->name;
        $user->email = request()->email;
        $user->password = bcrypt(request()->password);
        $user->save();

        return response()->json($user, 201);
    }

    /**
     * Get the authenticated User.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function me()
    {
        return response()->json(auth()->user());
    }

    /**
     * Log the user out (Invalidate the token).
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        auth()->logout();

        return response()->json(['message' => 'Successfully logged out']);
    }

    /**
     * Refresh a token.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh()
    {
        return $this->respondWithToken(auth()->refresh());
    }

    /**
     * Get the token array structure.
     *
     * @param  string $token
     *
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken($token)
    {
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth()->factory()->getTTL() * 60
        ]);
    }

}
Router
Route::match(['GET', 'POST'], '/login', [AuthController::class, 'login'])->name('login');

//project run at 

 php artisan serve --host=192.168.1.18

   INFO  Server running on [http://192.168.1.18:8000].


Solution

  • I encountered an issue with my Android application where it was unable to communicate with my server. enter code hereI realized that the problem stemmed from missing configuration in the AndroidManifest.xml file. Specifically, the tag was lacking the android:usesCleartextTraffic="true" attribute.

    For anyone experiencing a similar issue, ensure that your AndroidManifest.xml file includes the following attribute within the tag: <application android:usesCleartextTraffic="true" ... > ... </application>