Aplicación Android (curso 21-22)

App que se comunica con el API Rest creado en Laravel

Aplicación Android

¿Esquema de la red?

ApiRestClient.java

public class ApiRestClient {
    private static ApiService API_SERVICE;

    public static final String BASE_URL = "https://todo.alumno.me/";

    public static synchronized ApiService getInstance() {

        if (API_SERVICE == null) {

            OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder()
                    .connectTimeout(10, TimeUnit.SECONDS)
                    .readTimeout(10, TimeUnit.SECONDS)
                    .writeTimeout(5, TimeUnit.SECONDS);

            Gson gson = new GsonBuilder()
                    .setDateFormat("dd-MM-yyyy")
                    .create();

            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .client(okHttpBuilder.build())
                    .build();

            API_SERVICE = retrofit.create(ApiService.class);
        }
        return API_SERVICE;
    }
}

ApiService.java

public interface ApiService {
    @FormUrlEncoded
    @POST("api/register")
    Call<RegisterResponse> register(
            @Field("name") String name,
            @Field("email") String email,
            @Field("password") String password,
            @Field("confirm_password") String confirmPassword);

    //@POST("api/register")
    //Call<RegisterResponse>register(@Body User user);

    @FormUrlEncoded
    @POST("api/login")
    Call<LoginResponse> login(
            @Field("email") String email,
            @Field("password") String password);

    //@POST("api/login")
    //Call<LoginResponse>login(@Body User user);
}

ApiTokenRestClient.java

public class ApiTokenRestClient {
    private static ApiTokenService API_SERVICE;

    public static final String BASE_URL = "https://todo.alumno.me/";

    public static synchronized ApiTokenService getInstance(final String token) {

        if (API_SERVICE == null) {

            Interceptor interceptor = new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request newRequest = chain.request().newBuilder()
                            .addHeader("Accept", "application/json")
                            .addHeader("authorization", "Bearer " + token)
                            .build();
                    return chain.proceed(newRequest);
                }
            };

            OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder()
                    .connectTimeout(10, TimeUnit.SECONDS)
                    .readTimeout(10, TimeUnit.SECONDS)
                    .writeTimeout(5, TimeUnit.SECONDS)
                    .addInterceptor(interceptor);

            Gson gson = new GsonBuilder()
                    .setDateFormat("dd-MM-yyyy")
                    .create();

            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .client(okHttpBuilder.build())
                    .build();

            API_SERVICE = retrofit.create(ApiTokenService.class);
        }

        return API_SERVICE;
    }

    public static void deleteInstance() {
        API_SERVICE = null;
    }

}

ApiTokenService

public interface ApiTokenService {

    @POST("api/logout")
    Call<LogoutResponse> logout(
            //@Header("Authorization") String token
    );

    @GET("api/tasks")
    Call<GetTasksResponse> getTasks(
            //@Header("Authorization") String token
    );

    @POST("api/tasks")
    Call<AddResponse> createTask(
            //@Header("Authorization") String token,
            @Body Task task);

    @PUT("api/tasks/{id}")
    Call<AddResponse> updateTask(
            //@Header("Authorization") String token,
            @Body Task task,
            @Path("id") int id);

    @DELETE("api/tasks/{id}")
    Call<DeleteResponse> deleteTask(
            //@Header("Authorization") String token,
            @Path("id") int id);

    @POST("api/email")
    Call<EmailResponse> sendEmail(@Body Email email);
}

SharedPreferencesManager

public class SharedPreferencesManager {

    //public static final String APP = "MyApp";
    //public static final String EMAIL = "email";
    //public static final String PASSWORD = "password";
    //public static final String TOKEN = "token";

    SharedPreferences sp;
    SharedPreferences.Editor spEditor;

    public SharedPreferencesManager(Context context){
        sp = context.getSharedPreferences(MainActivity.APP, Context.MODE_PRIVATE);
        spEditor = sp.edit();
    }

    public void save (String email, String password){
        spEditor.putString(MainActivity.EMAIL, email);
        spEditor.putString(MainActivity.PASSWORD, password);
        spEditor.apply();
    }

    public void save (String email, String password, String token){
        spEditor.putString(MainActivity.EMAIL, email);
        spEditor.putString(MainActivity.PASSWORD, password);
        spEditor.putString(MainActivity.TOKEN, token);
        spEditor.apply();
    }
    public void saveEmail(String key, String value){
        spEditor.putString(key, value);
        spEditor.apply();
    }

    public String getEmail(){
        return sp.getString(MainActivity.EMAIL, "");
    }

    public void savePassword(String key, String value){
        spEditor.putString(key, value);
        spEditor.apply();
    }

    public String getPassword(){
        return sp.getString(MainActivity.PASSWORD, "");
    }

    public void saveToken(String key, String value){
        spEditor.putString(key, value);
        spEditor.apply();
    }

    public String getToken(){
        return sp.getString(MainActivity.TOKEN, "");
    }
}

Login en el MainActivity

    private void loginByServer() {
        progressDialog = new ProgressDialog(this);
        progressDialog.setIndeterminate(true);
        progressDialog.setMessage("Login ...");
        progressDialog.setCancelable(false);
        progressDialog.show();
        binding.login.setEnabled(false);

        String email = binding.email.getText().toString();
        String password = binding.password.getText().toString();

        Call<LoginResponse> call = ApiRestClient.getInstance().login(email, password);
        //User user = new User(name, email, password);
        call.enqueue(new Callback<LoginResponse>() {
            @Override
            public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
                StringBuilder message = new StringBuilder();
                progressDialog.dismiss();
                binding.login.setEnabled(true);

                //onRegisterSuccess();
                LoginResponse loginResponse = response.body();
                if (response.isSuccessful()) {
                    if (loginResponse.getSuccess()) {
                        Log.d("onResponse", "" + response.body());
                        //showMessage(response.body().getToken());
                        //guardar token en shared preferences
                        preferences.save(binding.email.getText().toString(), binding.password.getText().toString(), loginResponse.getData().getToken());
                        startActivity(new Intent(getApplicationContext(), PanelActivity.class));
                        finish();
                    } else {
                        showMessage("Error in login: " + loginResponse.getMessage());
                    }
                } else {
                        message.append("Error in login: ");
                        if (response.body() != null)
                            message.append("\nBody\n" + response.body());
                        if (response.errorBody() != null)
                            try {
                                message.append("\nError\n" + response.errorBody().string());
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        showMessage(message.toString());
                }
            }

            @Override
            public void onFailure(Call<LoginResponse> call, Throwable t) {
                progressDialog.dismiss();
                binding.login.setEnabled(true);
                String message = "Failure in the communication\n";
                if (t != null) {
                    Log.d("onFailure", t.getMessage());
                    message += t.getMessage();
                }
                showMessage(message);
                binding.register.setEnabled(true);
            }
        });
}

Registro en RegisterActivity

private void sendToServer(String name, String email, String password, String confirmPassord) {
    progressDialog = new ProgressDialog(this);
    progressDialog.setIndeterminate(true);
    progressDialog.setMessage("Creating Account ...");
    progressDialog.setCancelable(false);
    progressDialog.show();
    binding.register.setEnabled(false);

    Call<RegisterResponse> call = ApiRestClient.getInstance().register(name, email, password, confirmPassord);
    //User user = new User(name, email, password);
    call.enqueue(new Callback<RegisterResponse>() {
        @Override
        public void onResponse(Call<RegisterResponse> call, Response<RegisterResponse> response) {
            progressDialog.dismiss();
            //onRegisterSuccess();
            binding.register.setEnabled(true);
            if (response.isSuccessful()) {
                RegisterResponse registerResponse = response.body();
                if (registerResponse.getSuccess()) {
                    //Log.d("onResponse", "" + registerResponse.getData().getToken());
                    //enviar al Login para entrar después de validar el email
                    binding.register.setEnabled(true);
                    Intent resultIntent = new Intent();
                    resultIntent.putExtra("email", email);
                    resultIntent.putExtra("password", password);
                    //guardar el token en shared preferences
                    resultIntent.putExtra("token", registerResponse.getData().getToken());
                    setResult(RESULT_OK, resultIntent);
                    finish();
                } else {
                    showMessage("Error in registration " + registerResponse.getMessage());
                }
            } else {
                StringBuilder message = new StringBuilder();
                message.append("Download error: " + response.code());
                if (response.body() != null)
                    message.append("\n" + response.body());
                if (response.errorBody() != null)
                    try {
                        message.append("\n" + response.errorBody().string());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                showMessage(message.toString());
            }
        }

        @Override
        public void onFailure(Call<RegisterResponse> call, Throwable t) {
            progressDialog.dismiss();
            String message = "Failure in the communication\n";
            if (t != null) {
                Log.d("onFailure", t.getMessage());
                message += t.getMessage();
            }
            showMessage(message);
            binding.register.setEnabled(true);
        }
    });
}

Adapter en TodoAdapter

public class TodoAdapter extends RecyclerView.Adapter<TodoAdapter.MyViewHolder> {
    private ArrayList<Task> tasks;

    public TodoAdapter(){
        this.tasks = new ArrayList<>();
    }

    // Provide a direct reference to each of the views within a data item
    // Used to cache the views within the item layout for fast access
    public class MyViewHolder extends RecyclerView.ViewHolder {
        private ItemViewBinding binding;//Name of the item_contact.xml in camel case + "Binding"

        public MyViewHolder(ItemViewBinding b) {
            super(b.getRoot());
            binding = b;
        }
    }

    @Override
    public TodoAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
        return new MyViewHolder(ItemViewBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
    }

    // Involves populating data into the item through holder
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        // Get the data model based on position
        Task task = tasks.get(position);

        holder.binding.description.setText(task.getDescription());
    }


    @Override
    public int getItemCount() {
        return tasks.size();
    }

    public int getId(int position){

        return this.tasks.get(position).getId();
    }

    public void setTasks(ArrayList<Task> tasks) {
        this.tasks = tasks;
        notifyDataSetChanged();
    }

    public Task getAt(int position){
        Task task;
        task = this.tasks.get(position);
        return task;
    }

    public void add(Task task) {
        this.tasks.add(task);
        notifyItemInserted(tasks.size() - 1);
        notifyItemRangeChanged(0, tasks.size() - 1);
    }

    public void modifyAt(Task task, int position) {
        this.tasks.set(position, task);
        notifyItemChanged(position);
    }

    public void removeAt(int position) {
        this.tasks.remove(position);
        notifyItemRemoved(position);
        notifyItemRangeChanged(0, tasks.size() - 1);
    }
}

Respuesta para convertir el JSON

public class GetTasksResponse {

    @SerializedName("success")
    @Expose
    private Boolean success;
    @SerializedName("data")
    @Expose
    private ArrayList<Task> data = null;
    @SerializedName("message")
    @Expose
    private String message;

    public Boolean getSuccess() {
        return success;
    }

    public void setSuccess(Boolean success) {
        this.success = success;
    }

    public ArrayList<Task> getData() {
        return data;
    }

    public void setData(ArrayList<Task> data) {
        this.data = data;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

GetTasksData

public class GetTasksData {

    @SerializedName("id")
    @Expose
    private Integer id;
    @SerializedName("description")
    @Expose
    private String description;
    @SerializedName("created_at")
    @Expose
    private String createdAt;
    @SerializedName("updated_at")
    @Expose
    private String updatedAt;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(String createdAt) {
        this.createdAt = createdAt;
    }

    public String getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(String updatedAt) {
        this.updatedAt = updatedAt;
    }

}

DowloadTasks() en PanelActivity

private void downloadTasks() {
    progreso = new ProgressDialog(this);
    progreso.setProgressStyle(ProgressDialog.STYLE_SPINNER);
    progreso.setMessage("Connecting . . .");
    progreso.setCancelable(false);
    progreso.show();

    //Call<ArrayList<Site>> call = ApiRestClient.getInstance().getSites("Bearer " + preferences.getToken());
    Call<GetTasksResponse> call = ApiTokenRestClient.getInstance(preferences.getToken()).getTasks();
    call.enqueue(new Callback<GetTasksResponse>() {
        @Override
        public void onResponse(Call<GetTasksResponse> call, Response<GetTasksResponse> response) {
            progreso.dismiss();
            if (response.isSuccessful()) {
                GetTasksResponse getTasksResponse = response.body();
                if (getTasksResponse.getSuccess()) {
                    adapter.setTasks(getTasksResponse.getData());
                    showMessage("Tasks downloaded ok");
                } else {
                    showMessage("Error downloading the tasks: " + getTasksResponse.getMessage());
                }
            } else {
                StringBuilder message = new StringBuilder();
                message.append("Download error: " + response.code());
                if (response.body() != null)
                    message.append("\n" + response.body());
                if (response.errorBody() != null)
                    try {
                        message.append("\n" + response.errorBody().string());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                showMessage(message.toString());
            }
        }

        @Override
        public void onFailure(Call<GetTasksResponse> call, Throwable t) {
            progreso.dismiss();
            String message = "Failure in the communication\n";
            if (t != null)
                message += t.getMessage();
            showMessage(message);
        }
    });
}

Añadir una nueva tarea en AddActivity

private void connection(Task task) {
    progreso = new ProgressDialog(this);
    progreso.setProgressStyle(ProgressDialog.STYLE_SPINNER);
    progreso.setMessage("Connecting . . .");
    progreso.setCancelable(false);
    progreso.show();

    //Call<Site> call = ApiRestClient.getInstance().createSite("Bearer " + preferences.getToken(), s);
    Call<AddResponse> call = ApiTokenRestClient.getInstance(preferences.getToken()).createTask(task);
    call.enqueue(this);
}

@Override
public void onResponse(Call<AddResponse> call, Response<AddResponse> response) {
    progreso.dismiss();
    if (response.isSuccessful()) {
        AddResponse addResponse = response.body();
        if (addResponse.getSuccess()) {
            Intent i = new Intent();
            Bundle bundle = new Bundle();
            bundle.putInt("id", addResponse.getData().getId());
            bundle.putString("description", addResponse.getData().getDescription());
            i.putExtras(bundle);
            setResult(OK, i);
            finish();
            showMessage("Task created ok");
        } else {
            String message = "Error creating the task";
            if (!addResponse.getMessage().isEmpty()) {
                message += ": " + addResponse.getMessage();
            }
            showMessage(message);

        }
    } else {
        StringBuilder message = new StringBuilder();
        message.append("Download error: ");
        if (response.body() != null)
            message.append("\n" + response.body());
        if (response.errorBody() != null)
            try {
                message.append("\n" + response.errorBody().string());
            } catch (IOException e) {
                e.printStackTrace();
            }
        showMessage(message.toString());
    }

}

@Override
public void onFailure(Call<AddResponse> call, Throwable t) {
    progreso.dismiss();
    if (t != null)
        showMessage("Failure in the communication\n" + t.getMessage());

}

Actualizar una tarea

Eventos click y longClick

//manage click
binding.recyclerView.addOnItemTouchListener(new RecyclerTouchListener(this, binding.recyclerView, new ClickListener() {
    @Override
    public void onClick(View view, final int position) {
        showMessage("Single Click on task with id: " + adapter.getAt(position).getId());
        modify(adapter.getAt(position));
        positionClicked = position;
    }

    @Override
    public void onLongClick(View view, int position) {
        showMessage("Long press on position :" + position);
        confirm(adapter.getAt(position).getId(), adapter.getAt(position).getDescription(), position);
    }
}));

Lanzar UpdateActivity

private void modify(Task task) {
    Intent i = new Intent(this, UpdateActivity.class);
    i.putExtra("task", task);
    startActivityForResult(i, UPDATE_CODE);
}

conectarse al API en UpdateActivity

private void connection(Task task) {
    showMessage(task.getId() + "");
    progreso = new ProgressDialog(this);
    progreso.setProgressStyle(ProgressDialog.STYLE_SPINNER);
    progreso.setMessage("Connecting . . .");
    progreso.setCancelable(false);
    progreso.show();

    //Call<Site> call = ApiRestClient.getInstance().createSite("Bearer " + preferences.getToken(), s);
    Call<AddResponse> call = ApiTokenRestClient.getInstance(preferences.getToken()).updateTask(task, task.getId());
    call.enqueue(this);
}

@Override
public void onResponse(Call<AddResponse> call, Response<AddResponse> response) {
    progreso.dismiss();
    if (response.isSuccessful()) {
        AddResponse addResponse = response.body();
        if (addResponse.getSuccess()) {
            Intent i = new Intent();
            Bundle bundle = new Bundle();
            bundle.putInt("id", addResponse.getData().getId());
            bundle.putString("description", addResponse.getData().getDescription());
            i.putExtras(bundle);
            setResult(OK, i);
            finish();
            showMessage("Task updated ok: " + addResponse.getData().getDescription());
        } else {
            String message = "Error updating the task";
            if (!addResponse.getMessage().isEmpty()) {
                message += ": " + addResponse.getMessage();
            }
            showMessage(message);

        }
    } else {
        StringBuilder message = new StringBuilder();
        message.append("Download error: ");
        Log.e("Error:", response.errorBody().toString());
        if (response.body() != null)
            message.append("\n" + response.body());
        if (response.errorBody() != null)
            try {
                message.append("\n" + response.errorBody().string());
            } catch (IOException e) {
                e.printStackTrace();
            }
        showMessage(message.toString());
    }
}

@Override
public void onFailure(Call<AddResponse> call, Throwable t) {
    progreso.dismiss();
    if (t != null)
        showMessage("Failure in the communication\n" + t.getMessage());
}

Eliminar una tarea en PanelActivity

private void confirm(final int idTask, String description, final int position) {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage(description + "\nDo you want to delete?")
            .setTitle("Delete")
            .setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    dialog.dismiss();
                    connection(position);
                }
            })
            .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    dialog.dismiss();
                }
            });
    builder.show();
}

conexión al API en PanelActivity para eliminar una tarea

private void connection(final int position) {
    //Call<ResponseBody> call = ApiRestClient.getInstance().deleteSite("Bearer " + preferences.getToken(), adapter.getId(position));
    Call<DeleteResponse> call = ApiTokenRestClient.getInstance(preferences.getToken()).deleteTask(adapter.getId(position));
    progreso.setProgressStyle(ProgressDialog.STYLE_SPINNER);
    progreso.setMessage("Connecting . . .");
    progreso.setCancelable(false);
    progreso.show();
    call.enqueue(new Callback<DeleteResponse>() {
        @Override
        public void onResponse(Call<DeleteResponse> call, Response<DeleteResponse> response) {
            progreso.dismiss();
            if (response.isSuccessful()) {
                DeleteResponse deleteResponse = response.body();
                if (deleteResponse.getSuccess()) {
                    adapter.removeAt(position);
                    showMessage("Task deleted OK");
                } else
                    showMessage("Error deleting the task");
            } else {
                StringBuilder message = new StringBuilder();
                message.append("Error deleting a site: " + response.code());
                if (response.body() != null)
                    message.append("\n" + response.body());
                if (response.errorBody() != null)
                    try {
                        message.append("\n" + response.errorBody().string());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                showMessage(message.toString());
            }
        }

        @Override
        public void onFailure(Call<DeleteResponse> call, Throwable t) {
            progreso.dismiss();
            if (t != null)
                showMessage("Failure in the communication\n" + t.getMessage());

        }
    });
}

Enviar un email en EmailActivity

private void connection(Email e) {
    progreso = new ProgressDialog(this);
    progreso.setProgressStyle(ProgressDialog.STYLE_SPINNER);
    progreso.setMessage("Connecting . . .");
    progreso.setCancelable(false);
    progreso.show();

    Call<EmailResponse> call = ApiTokenRestClient.getInstance(preferences.getToken()).sendEmail(e);
    call.enqueue(this);
}

@Override
public void onResponse(Call<EmailResponse> call, Response<EmailResponse> response) {
    progreso.dismiss();
    if (response.isSuccessful()) {
        EmailResponse emailResponse = response.body();
        if (emailResponse.getSuccess()) {
            //Intent i = new Intent();
            //setResult(OK, i);
            showMessage("Email sent ok: " + emailResponse.getMessage());
            finish();
        } else {
            String message = "Email not sent";
            if (!emailResponse.getMessage().isEmpty()) {
                message += ": " + emailResponse.getMessage();
            }
            showMessage(message);
        }
    } else {
        StringBuilder message = new StringBuilder();
        message.append("Error sending the mail: " + response.code());
        if (response.body() != null)
            message.append("\n" + response.body());
        if (response.errorBody() != null)
            try {
                message.append("\n" + response.errorBody().string());
            } catch (IOException e) {
                e.printStackTrace();
            }
        showMessage(message.toString());
    }
}

@Override
public void onFailure(Call<EmailResponse> call, Throwable t) {
    String message = "Failure sending the email\n";
    if (t != null)
        message += t.getMessage();
    showMessage(message);
}

Mejoras

  • Usar el patrón MVVM en el diseño de la aplicación
  • Enviar un email de verificación al registrarse un usuario y comprobar en el login si el usuario está verificado
  • Añadir más campos a la tarea (título, fecha límite, observaciones, . . . )
  • Devolver los códigos de estado adecuados en los errores (no solo el 404)

 

Deja una respuesta