Membuat Aplikasi E-Laundry Menggunakan Android

Artikel kali ini penulis membahas mengenai aplikasi E-laundry, Aplikasi ini masih belum selesai anggap saja masih setengah dari pengerjaan karna sampai sekarang client belum ada kejelasan apakah mau dilanjutkan apa tidak, jadi terpaksa saya jadikan artikel dan tutorial, sekaligus saya pelajari lagi alur dari code aplikasi, berharap aplikasi e-laundry dilanjutkan pengerjaannya dari client tersebut.

Vidio akan tersedia beberapa saat lagi…

Add Dependencies ... (Still Under Development)

Anda tambahkan modul dependencies dibagian gradle

implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'com.google.android.material:material:1.1.0'

implementation 'com.google.firebase:firebase-analytics:17.2.0'
implementation 'com.google.firebase:firebase-auth:19.1.0'
implementation 'com.google.android.gms:play-services-auth:17.0.0'

implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.1'

implementation 'com.google.android.gms:play-services-maps:17.0.0'
implementation 'com.google.android.gms:play-services-location:17.0.0'
implementation 'com.google.android.libraries.places:places:2.2.0'

Add URL API

Masukan URL API dibagian Build Types, Anda buat dibagian type debug dan dibagian type release

buildConfigField "String", "SERVER_URL", 'URL_API_YOU'

Add Model Package ... (Still Under Development)

Anda Tambahkan class model Konsumen, ResultLogin, ResultSignup, ... (Still Under Development)

Model Konsumen

class Konsumen {
    @SerializedName("id")
    @Expose
    val id: String? = null

    @SerializedName("nama")
    @Expose
    val nama: String? = null

    @SerializedName("alamat")
    @Expose
    val alamat: String? = null

    @SerializedName("telp")
    @Expose
    val telp: String? = null

    @SerializedName("email")
    @Expose
    val email: String? = null

    @SerializedName("password")
    @Expose
    val password: String? = null
}

Model ResultLogin

class ResultLogin {
    @SerializedName("status")
    @Expose
    val isStatus: Boolean = false

    @SerializedName("user_login")
    @Expose
    val user_login: String? = null

    @SerializedName("token")
    @Expose
    val token: String? = null

    @SerializedName("result")
    @Expose
    val result: Konsumen? = null
}

Model ResultSignup

class ResultSignup {

    @SerializedName("status")
    @Expose
    val isStatus: Boolean = false
}

Jika Anda sudah membuat model selanjutnya anda buat untuk menghubungkan API, untuk menghubungkan digunakan modul retrofit yang anda sudah tambahkan dibagian dependencies

Add Data Service ... (Still Under Development)

Anda buat data service yang diambil dari setiap Part API anda, kemudian anda tentukan tindakan yang digunakan apakah POST, GET, PUT, UPDATE atau yang lainnya sesuaikan berdasarkan bagian API

interface DataService {
    @FormUrlEncoded
    @POST("login")
    fun getLogin(
        @Field("email") email: String,
        @Field("password") password: String
    ): Call<ResultLogin>

    @FormUrlEncoded
    @POST("signup")
    fun getSignup(
        @Field("nama") nama: String,
        @Field("alamat") alamat: String,
        @Field("telp") telp: String,
        @Field("email") email: String,
        @Field("password") password: String
    ): Call<ResultSignup>
}

Add Data Provider ... (Still Under Development)

Anda buat data provider di class ini digunakan untuk menghubungkan antara API dengan melakukan convert munggunakan modul retrofit dari sebelumnya dalam bentuk JSON ke dalam bentuk object berupa attribut supaya bisa mudah di proses lebih mudah

class DataProvider(token: String) {
    private val retrofit: Retrofit
    private var mytoken: String? = null
    private val dataService: DataService

    init {
        val interceptor = HttpLoggingInterceptor()
        interceptor.level = HttpLoggingInterceptor.Level.BODY

        val client = OkHttpClient.Builder()
            .readTimeout(60, TimeUnit.SECONDS)
            .connectTimeout(60, TimeUnit.SECONDS)
            .addInterceptor(interceptor).build()

        val gson = GsonBuilder()
            .setLenient()
            .create()

        retrofit = Retrofit.Builder()
            .baseUrl(BuildConfig.SERVER_URL)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build()
        dataService = retrofit.create(DataService::class.java)
        setMytoken(token)
    }

    private fun setMytoken(mytoken: String) {
        this.mytoken = mytoken
    }

    fun getLogin(email: String, password: String): Call<ResultLogin> {
        return dataService.getLogin(email, password)
    }

    fun getSignup(
        nama: String,
        alamat: String,
        mobile_number: String,
        email: String,
        password: String
    ): Call<ResultSignup> {
        return dataService.getSignup(nama, alamat, mobile_number, email, password)
    }

    companion object {

        fun instance(token: String): DataProvider {
            return DataProvider(token)
        }
    }
}

Add Local Data

Menampung data sementara dari setiap model, dalam kasus ini menyesuaikan dengan data API sesuai respon, contohnya seperti data pengguna yang login, class yang digunakan untuk penyimpanan sementara class SharedPreferences

class LocalData(private val context: Context) {
    private fun setting(): SharedPreferences {
        return context.getSharedPreferences(PREFS_NAME, 0)
    }

    private fun editor(): SharedPreferences.Editor {
        return setting().edit()
    }

    fun setData(key: String, data: String) {
        val editor = editor()
        editor.putString(key, data)
        editor.commit()
    }

    fun getData(key: String): String {
        return setting().getString(key, "").toString()
    }

    fun getData(key: String, def: Int): Int {
        return setting().getInt(key, def)
    }

    companion object {
        private val PREFS_NAME = "elaundry"

        fun L(context: Context): LocalData {
            return LocalData(context)
        }
    }
}

Download Resource File

Anda download file yang berisi data class dari tamplate yang sudah diupdate dan file resource, Download Archive E-laundry

Jika Anda sudah memasukan file yang anda download dibagian aplikasi sesuai langkah di video selanjutnya anda buat beberapa class utama


Buat Class Login

Anda buat activity login yang digunakan konsumen dalam masuk ke aplikasi elaundry dengan aman

public class LoginActivity extends AppCompatActivity {

    private static final String TAG = "LoginActivity";
    private static final int REQUEST_SIGNUP = 0;
    private boolean isLogin = false;

    ProgressBar _login_progress;
    EditText _emailText;
    EditText _passwordText;
    Button _loginButton;
    TextView link_forgotpassword;
    LinearLayout _signupLink;

    //name strings
    String lastName = "";
    String firstName = "";

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        try {
            this.getSupportActionBar().hide();
        } catch (NullPointerException ignored) {
        }
        setContentView(R.layout.activity_login);

        /*black icons on top bar like battery etc*/
        getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

        _login_progress = findViewById(R.id.login_progress);
        _emailText = findViewById(R.id.input_email);
        _passwordText = findViewById(R.id.input_password);
        _loginButton = findViewById(R.id.btn_login);
        _signupLink = findViewById(R.id.link_signup);
        link_forgotpassword = findViewById(R.id.link_forgotpassword);

        link_forgotpassword.setOnClickListener(v -> {
            Intent intent = new Intent(LoginActivity.this, ResetPasswordActivity.class);
            startActivity(intent);
        });

        _loginButton.setOnClickListener(v -> login());

        _signupLink.setOnClickListener(v -> {
            // Start the ResultSignup activity
            startActivity(new Intent(LoginActivity.this, SignUpActivity.class));
        });
    }

    public void login() {
        Log.d(TAG, "ResultLogin");

        if (!validate()) {
            onLoginFailed();
            return;
        }

        _loginButton.setEnabled(false);

        if (isLogin) {
            return;
        }

        _login_progress.setVisibility(View.VISIBLE);
        _login_progress.setProgress(10);
        isLogin = true;

        String email = _emailText.getText().toString();
        String password = _passwordText.getText().toString();

        DataProvider.Companion.instance("").getLogin(email, password).enqueue(new Callback<ResultLogin>() {
            @Override
            public void onResponse(@NotNull Call<ResultLogin> call, @NotNull Response<ResultLogin> response) {
                _login_progress.setProgress(50);

                if (response.code() == 200) {
                    ResultLogin resultLogin = response.body();
                    if (resultLogin != null && resultLogin.isStatus()) {
                        Log.e("getLaundry", "" + resultLogin.getResult().getNama());
                        LocalData.Companion.L(getBaseContext()).setData("token", resultLogin.getToken());
                        LocalData.Companion.L(getBaseContext()).setData("user_login", resultLogin.getUser_login());
                        LocalData.Companion.L(getBaseContext()).setData("nama", resultLogin.getResult().getNama());
                        LocalData.Companion.L(getBaseContext()).setData("alamat", resultLogin.getResult().getAlamat());
                        LocalData.Companion.L(getBaseContext()).setData("telp", resultLogin.getResult().getTelp());
                        LocalData.Companion.L(getBaseContext()).setData("email", resultLogin.getResult().getEmail());
                        stopProgress(new onCallback() {
                            @Override
                            public void onRun() {
                                isLogin = false;
                                startActivity(new Intent(getBaseContext(), MainActivity.class));
                                onLoginSuccess();
                            }
                        });
                    } else {
                        stopProgress(() -> {
                            isLogin = false;
                            AlertDialogBox.INSTANCE.Alert(LoginActivity.this, "Username or Password not valid");
                            onLoginFailed();
                        });
                    }
                } else {
                    stopProgress(() -> {
                        isLogin = false;
                        AlertDialogBox.INSTANCE.Alert(LoginActivity.this, "Username or Password not valid");
                        onLoginFailed();
                    });

                }
            }

            @Override
            public void onFailure(@NotNull Call<ResultLogin> call, @NotNull Throwable t) {
                Log.e("getLaundry", "" + t.getMessage());
                stopProgress(() -> {
                    isLogin = false;
                    AlertDialogBox.INSTANCE.Alert(LoginActivity.this, "Error with connection");
                });

            }
        });
    }


    private String getfirstname(String name) {
        if (name.split("\\w+").length > 1) {
            lastName = name.substring(name.lastIndexOf(" ") + 1);
            firstName = name.substring(0, name.lastIndexOf(' '));
            return firstName;
        } else {
            firstName = name;
            return firstName;
        }
    }

    private boolean havelastname(String name) {
        if (name.split("\\w+").length > 1) {
            lastName = name.substring(name.lastIndexOf(" ") + 1);
            firstName = name.substring(0, name.lastIndexOf(' '));
            return true;
        } else {
            firstName = name;
            return false;
        }
    }

    @SuppressLint("MissingSuperCall")
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_SIGNUP) {
            if (resultCode == RESULT_OK) {

                // TODO: Implement successful signup logic here
                // By default we just finish the Activity and log them in automatically
                this.finish();
            }
        }
    }

    @Override
    public void onBackPressed() {
        // disable going back to the MainActivityStudent
        moveTaskToBack(true);
    }

    public void onLoginSuccess() {
        _loginButton.setEnabled(true);
        finish();
    }

    public void onLoginFailed() {
        _loginButton.setEnabled(true);
    }

    public boolean validate() {
        boolean valid = true;

        String email = _emailText.getText().toString();
        String password = _passwordText.getText().toString();

        if (email.isEmpty() || !android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
            _emailText.setError("enter a valid email address");
            valid = false;
        } else {
            _emailText.setError(null);
        }

        if (password.isEmpty() || password.length() < 4 || password.length() > 10) {
            _passwordText.setError("between 4 and 10 alphanumeric characters");
            valid = false;
        } else {
            _passwordText.setError(null);
        }

        return valid;
    }

    private void stopProgress(final onCallback callback) {
        new Handler().postDelayed(() -> _login_progress.setProgress(100), 5000);
        new Handler().postDelayed(() -> {
            _login_progress.setVisibility(View.GONE);
            callback.onRun();
        }, 3000);
    }

    private interface onCallback {
        void onRun();
    }
}

Buat Class SignUp

Digunakan untuk registrasi atau daftar konsumen baru pada aplikasi e-laundry

public class SignUpActivity extends AppCompatActivity {

    private static final String TAG = "SignupActivity";
    EditText _phonenumberText, _emailText, _passwordText, _nameText, _addressText;
    Button _signupButton;
    ImageView backbtn;
    String namest, emailst, passst, addressst, phonenumber;
    TextView titletxt, desctxt;
    LinearLayout userinfo_tab, _loginLink, namell, addressll, emailll, passwordll, phonenumberll;

    int step = 1;

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sign_up);

        getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

        _passwordText = findViewById(R.id.input_password);
        _emailText = findViewById(R.id.input_email);
        _nameText = findViewById(R.id.input_name);
        _addressText = findViewById(R.id.input_address);
        _signupButton = findViewById(R.id.btn_signup);
        _phonenumberText = findViewById(R.id.input_phonenumber);
        _loginLink = findViewById(R.id.link_login);
        namell = findViewById(R.id.namell);
        addressll = findViewById(R.id.addressll);
        emailll = findViewById(R.id.emailll);
        passwordll = findViewById(R.id.passwordll);
        phonenumberll = findViewById(R.id.phonenumberll);

        titletxt = findViewById(R.id.titletxt);
        desctxt = findViewById(R.id.desctxt);

        userinfo_tab = findViewById(R.id.userinfo_tab);
        backbtn = findViewById(R.id.backbtn);

        hideall();
        namell.setVisibility(View.VISIBLE);

        /*Setting on click listeners*/
        backbtn.setOnClickListener(v -> finish());

        _signupButton.setOnClickListener(v -> {
            if (step == 1) {
                namest = _nameText.getText().toString();
                if (namest.isEmpty()) {
                    Toast.makeText(SignUpActivity.this, "Please write your name!", Toast.LENGTH_SHORT).show();
                } else {
                    namell.setVisibility(View.GONE);
                    addressll.setVisibility(View.VISIBLE);
                    titletxt.setText("Create Address");
                    desctxt.setText("enter your address below.");
                    step++;
                }
            } else if (step == 2) {
                addressst = _addressText.getText().toString();
                if (addressst.isEmpty()) {
                    Toast.makeText(SignUpActivity.this, "Please write your address!", Toast.LENGTH_SHORT).show();
                } else {
                    addressll.setVisibility(View.GONE);
                    emailll.setVisibility(View.VISIBLE);
                    titletxt.setText("Create Email");
                    desctxt.setText("enter your email below.");
                    step++;
                }
            } else if (step == 3) {
                emailst = _emailText.getText().toString();
                if (emailst.isEmpty()) {
                    Toast.makeText(SignUpActivity.this, "Please write your email!", Toast.LENGTH_SHORT).show();
                } else {
                    emailll.setVisibility(View.GONE);
                    passwordll.setVisibility(View.VISIBLE);
                    titletxt.setText("Create Password");
                    desctxt.setText("Your password must have atleast one symbol & 4 or more characters.");
                    step++;
                }
            } else if (step == 4) {
                passst = _passwordText.getText().toString();
                if (passst.isEmpty()) {
                    Toast.makeText(SignUpActivity.this, "Please write your password!", Toast.LENGTH_SHORT).show();
                } else {
                    passwordll.setVisibility(View.GONE);
                    phonenumberll.setVisibility(View.VISIBLE);
                    titletxt.setText("Create Mobile Number");
                    desctxt.setText("Enter your mobile number.");
                    step++;
                }
            } else if (step == 5) {
                phonenumber = _phonenumberText.getText().toString();
                if (phonenumber.isEmpty()) {
                    Toast.makeText(SignUpActivity.this, "Please write your mobile number!", Toast.LENGTH_SHORT).show();
                } else {
                    phonenumberll.setVisibility(View.GONE);
                    titletxt.setText("Let's Get Started");
                    _signupButton.setText("FINISH");
                    step++;
                }
            } else if (step == 6) {
                signup();
            }
        });

        _loginLink.setOnClickListener(v -> {
            // Finish the registration screen and return to the ResultLogin activity
            finish();
        });
    }

    private void hideall() {
        //removing all the edit text will be opened one by one
        namell.setVisibility(View.GONE);
        addressll.setVisibility(View.GONE);
        emailll.setVisibility(View.GONE);
        passwordll.setVisibility(View.GONE);
        phonenumberll.setVisibility(View.GONE);
    }

    public void signup() {
        Log.d(TAG, "ResultSignup");

        _signupButton.setEnabled(false);

        final ProgressDialog progressDialog = new ProgressDialog(SignUpActivity.this);
        progressDialog.setIndeterminate(true);
        progressDialog.setMessage("Save process...");
        progressDialog.show();

        String name = _nameText.getText().toString();
        String address = _addressText.getText().toString();
        String email = _emailText.getText().toString();
        String password = _passwordText.getText().toString();
        String mobilephone = _phonenumberText.getText().toString();

        DataProvider.Companion.instance("").getSignup(
                name, address, mobilephone, email, password
        )
                .enqueue(new Callback<ResultSignup>() {
                    @Override
                    public void onResponse(@NotNull Call<ResultSignup> call, @NotNull Response<ResultSignup> response) {
                        if (response.code() == 200) {
                            ResultSignup result = response.body();

                            if (result != null && result.isStatus()) {
                                new android.os.Handler().postDelayed(
                                        () -> {
                                            onSignupSuccess();
                                            progressDialog.dismiss();
                                        }, 3000);
                            } else {
                                onSignupFailed();
                                progressDialog.dismiss();
                            }
                        }
                    }

                    @Override
                    public void onFailure(@NotNull Call<ResultSignup> call, @NotNull Throwable t) {
                        Log.e("getLaundry", "" + t.getMessage());
                    }
                });
    }

    public void onSignupSuccess() {
        _signupButton.setEnabled(true);
        setResult(RESULT_OK, null);
        Intent intent = new Intent(SignUpActivity.this, LoginActivity.class);
        startActivity(intent);
        finish();
        AlertDialogBox.INSTANCE.Alert(SignUpActivity.this, "Saved successfully");
    }

    public void onSignupFailed() {
        Toast.makeText(getBaseContext(), "Login failed", Toast.LENGTH_LONG).show();
        _signupButton.setEnabled(true);
    }

    public boolean validate() {
        boolean valid = true;
        String name = _nameText.getText().toString();
        String email = _emailText.getText().toString();
        String password = _passwordText.getText().toString();

        if (name.isEmpty() || name.length() < 3) {
            _nameText.setError("at least 3 characters");
            valid = false;
        } else {
            _nameText.setError(null);
        }

        if (email.isEmpty() || !android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
            _emailText.setError("enter a valid email address");
            valid = false;
        } else {
            _emailText.setError(null);
        }

        if (password.isEmpty() || password.length() < 4 || password.length() > 10) {
            _passwordText.setError("between 4 and 10 alphanumeric characters");
            valid = false;
        } else {
            _passwordText.setError(null);
        }

        return valid;
    }
}

Buat Class Reset Password ... (Still Under Development)

Digunakan untuk reset password pengguna pada konsumen

public class ResetPasswordActivity extends AppCompatActivity {

    private static final String TAG = "ResetPasswordActivity";
    private EditText inputEmail;
    private Button btnReset;
    private TextView btnBack;
    private ProgressBar progressBar;
    private ImageView backbtn;

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

        inputEmail = findViewById(R.id.email);
        btnReset = findViewById(R.id.btn_reset_password);
        btnBack = findViewById(R.id.link_login);
        progressBar = findViewById(R.id.progressBar);
        backbtn = findViewById(R.id.backbtn);

        backbtn.setOnClickListener(v -> finish());

        btnBack.setOnClickListener(v -> finish());

        btnReset.setOnClickListener(v -> {

            String email = inputEmail.getText().toString().trim();

            if (TextUtils.isEmpty(email)) {
                AlertDialogBox.INSTANCE.Alert(ResetPasswordActivity.this, "Enter your registered email");
            } else {
                resetEmail();
            }

            progressBar.setVisibility(View.VISIBLE);
        });
    }

    private void resetEmail() {
        Toast.makeText(this, "Still under development", Toast.LENGTH_SHORT).show();
    }

Buat Class MainActivity

Diclass ini sebagai transaksi untuk memilih jenis pakaian yang akan di laundry sekaligus menampilkan jumlah biaya laundry

public class MainActivity extends AppCompatActivity implements LeftMenuAdapter.onItemSelectedListener,
        ServicesCartImp, ServicesCartDialog.ShopCartDialogImp {

    private final static String TAG = "MainActivity";
    private RecyclerView leftMenu;
    private RecyclerView rightMenu;
    private TextView headerView;
    private LinearLayout headerLayout;
    private ServiceMenu headMenu;
    private LeftMenuAdapter leftAdapter;
    private RightServiceAdapter rightAdapter;
    private ArrayList<ServiceMenu> serviceMenuList;
    private boolean leftClickType = false;
    private ServicesCart servicesCart;
    private ImageView servicesCartView;
    private FrameLayout servicesCartLayout;
    private TextView totalPriceTextView;
    private TextView totalPriceNumTextView;
    private RelativeLayout mainLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        initView();
        initAdapter();
    }

    private void initView() {
        mainLayout = findViewById(R.id.main_layout);
        leftMenu = findViewById(R.id.left_menu);
        rightMenu = findViewById(R.id.right_menu);
        headerView = findViewById(R.id.right_menu_tv);
        headerLayout = findViewById(R.id.right_menu_item);
        servicesCartView = findViewById(R.id.services_cart);
        servicesCartLayout = findViewById(R.id.services_cart_layout);
        totalPriceTextView = findViewById(R.id.services_cart_total_tv);
        totalPriceNumTextView = findViewById(R.id.services_cart_total_num);

        leftMenu.setLayoutManager(new LinearLayoutManager(this));
        rightMenu.setLayoutManager(new LinearLayoutManager(this));

        rightMenu.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NotNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(@NotNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (!recyclerView.canScrollVertically(1)) {
                    showHeadView();
                    return;
                }
                View underView = null;
                if (dy > 0)
                    underView = rightMenu.findChildViewUnder(headerLayout.getX(), headerLayout.getMeasuredHeight() + 1);
                else
                    underView = rightMenu.findChildViewUnder(headerLayout.getX(), 0);
                if (underView != null && underView.getContentDescription() != null) {
                    int position = Integer.parseInt(underView.getContentDescription().toString());
                    ServiceMenu menu = rightAdapter.getMenuOfMenuByPosition(position);

                    if (leftClickType || !menu.getMenuName().equals(headMenu.getMenuName())) {
                        if (dy > 0 && headerLayout.getTranslationY() <= 1 && headerLayout.getTranslationY() >= -1 * headerLayout.getMeasuredHeight() * 4 / 5 && !leftClickType) {// underView.getTop()>9
                            int dealtY = underView.getTop() - headerLayout.getMeasuredHeight();
                            headerLayout.setTranslationY(dealtY);
//                            Log.e(TAG, "onScrolled: "+headerLayout.getTranslationY()+"   "+headerLayout.getBottom()+"  -  "+headerLayout.getMeasuredHeight() );
                        } else if (dy < 0 && headerLayout.getTranslationY() <= 0 && !leftClickType) {
                            headerView.setText(menu.getMenuName());
                            int dealtY = underView.getBottom() - headerLayout.getMeasuredHeight();
                            headerLayout.setTranslationY(dealtY);
//                            Log.e(TAG, "onScrolled: "+headerLayout.getTranslationY()+"   "+headerLayout.getBottom()+"  -  "+headerLayout.getMeasuredHeight() );
                        } else {
                            headerLayout.setTranslationY(0);
                            headMenu = menu;
                            headerView.setText(headMenu.getMenuName());
                            for (int i = 0; i < serviceMenuList.size(); i++) {
                                if (serviceMenuList.get(i) == headMenu) {
                                    leftAdapter.setSelectedNum(i);
                                    break;
                                }
                            }
                            if (leftClickType) leftClickType = false;
                            Log.e(TAG, "onScrolled: " + menu.getMenuName());
                        }
                    }
                }
            }
        });

        servicesCartLayout.setOnClickListener(view -> showCart(view));
    }

    private void initData() {
        servicesCart = new ServicesCart();
        serviceMenuList = new ArrayList<>();
        ArrayList<Services> menu1 = new ArrayList<>();
        menu1.add(new Services("Cuci Kering Gosok", 10000, 50, R.drawable.cuci_kering_gosok, "/kg"));
        menu1.add(new Services("Jasa setrika", 8000, 50, R.drawable.jasa_setrika, "/kg"));
        menu1.add(new Services("Cuci Kering Lipat", 8000, 50, R.drawable.cuci_kering_lipat, "/kg"));
        menu1.add(new Services("Express Services", 16000, 50, R.drawable.expres_services, "/kg"));
        ServiceMenu serviceMenu1 = new ServiceMenu("Daily kiloan", R.drawable.daily_kiloan_color, menu1);

        ArrayList<Services> menu2 = new ArrayList<>();
        menu2.add(new Services("Bed Cover King", 25000, 50, R.drawable.sprei, "/pc"));
        menu2.add(new Services("Bed Cover Single", 20000, 50, R.drawable.sprei, "/pc"));
        menu2.add(new Services("Bantal / Guling Tidur Besar", 25000, 50, R.drawable.bantal, "/pc"));
        menu2.add(new Services("Bantal / Guling Tidur Sedang", 20000, 50, R.drawable.bantal, "/pc"));
        menu2.add(new Services("Sarung Bantal / Guling (min 3 pc)", 5000, 50, R.drawable.sarung_bantal, "/pc"));
        menu2.add(new Services("Selimut", 15000, 50, R.drawable.selimut, "/pc"));
        menu2.add(new Services("Sprei / Bed Sheet", 10000, 50, R.drawable.sprei, "/pc"));
        ServiceMenu serviceMenu2 = new ServiceMenu("Beddings", R.drawable.baddings_color, menu2);

        ArrayList<Services> menu3 = new ArrayList<>();
        menu3.add(new Services("Boneka Super Jumbo > 100 cm", 50000, 50, R.drawable.boneka_super_jumbo, "/pc"));
        menu3.add(new Services("Boneka Besar > 30 cm", 25000, 50, R.drawable.boneka_besar, "/pc"));
        menu3.add(new Services("Boneka Kecil <  30 cm", 15000, 50, R.drawable.boneka_kecil, "/pc"));
        menu3.add(new Services("Handuk badan", 10000, 50, R.drawable.handuk_badan, "/pc"));
        ServiceMenu serviceMenu3 = new ServiceMenu("Households", R.drawable.household_color, menu3);

        ArrayList<Services> menu4 = new ArrayList<>();
        menu4.add(new Services("Sajadah Tebal", 20000, 50, R.drawable.sajadah_tebal, "/pc"));
        menu4.add(new Services("T-shirt / Kemeja Pendek (min 3 pc)", 5000, 50, R.drawable.kemeja_pendek, "/pc"));
        menu4.add(new Services("T-shirt  / Kemeja Panjang (min 3 pc)", 8000, 50, R.drawable.kemeja_panjang, "/pc"));
        menu4.add(new Services("Celana / Rok Jeans Panjang", 12000, 50, R.drawable.celana_panjang, "/pc"));
        menu4.add(new Services("Celana / Rok Jeans Pendek", 10000, 50, R.drawable.celana_pendek, "/pc"));
        menu4.add(new Services("Batik Kemeja", 10000, 50, R.drawable.batik_kemeja, "/pc"));
        menu4.add(new Services("Jaket / Sweater", 15000, 50, R.drawable.jaket, "/pc"));
        menu4.add(new Services("Gamis Muslimah", 15000, 50, R.drawable.gamis_muslimah, "/pc"));
        ServiceMenu serviceMenu4 = new ServiceMenu("Premium satuan", R.drawable.premium_color, menu4);

        ArrayList<Services> menu5 = new ArrayList<>();
        menu5.add(new Services("Koper besar", 45000, 50, R.drawable.koper_besar, "/pc"));
        menu5.add(new Services("Ransel Besar", 20000, 50, R.drawable.ransel_besar, "/pc"));
        menu5.add(new Services("Ransel Kecil", 15000, 50, R.drawable.ransel_kecil, "/pc"));
        menu5.add(new Services("Sepatu (non leather)", 20000, 50, R.drawable.sepatu, "/pasang"));
        ServiceMenu serviceMenu5 = new ServiceMenu("Tas & Sepatu", R.drawable.bag_shoes_color, menu5);

        serviceMenuList.add(serviceMenu1);
        serviceMenuList.add(serviceMenu2);
        serviceMenuList.add(serviceMenu3);
        serviceMenuList.add(serviceMenu4);
        serviceMenuList.add(serviceMenu5);
    }

    private void initAdapter() {
        leftAdapter = new LeftMenuAdapter(this, serviceMenuList);
        rightAdapter = new RightServiceAdapter(this, serviceMenuList, servicesCart);
        rightMenu.setAdapter(rightAdapter);
        leftMenu.setAdapter(leftAdapter);
        leftAdapter.addItemSelectedListener(this);
        rightAdapter.setServicesCartImp(this);
        initHeadView();
    }

    private void initHeadView() {
        headMenu = rightAdapter.getMenuOfMenuByPosition(0);
        headerLayout.setContentDescription("0");
        headerView.setText(headMenu.getMenuName());
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        leftAdapter.removeItemSelectedListener(this);
    }

    private void showHeadView() {
        headerLayout.setTranslationY(0);
        View underView = rightMenu.findChildViewUnder(headerView.getX(), 0);
        if (underView != null && underView.getContentDescription() != null) {
            int position = Integer.parseInt(underView.getContentDescription().toString());
            headMenu = rightAdapter.getMenuOfMenuByPosition(position + 1);
            headerView.setText(headMenu.getMenuName());
            for (int i = 0; i < serviceMenuList.size(); i++) {
                if (serviceMenuList.get(i) == headMenu) {
                    leftAdapter.setSelectedNum(i);
                    break;
                }
            }
        }
    }

    @Override
    public void onLeftItemSelected(int position, ServiceMenu menu) {
        int sum = 0;
        for (int i = 0; i < position; i++) {
            sum += serviceMenuList.get(i).getServicesList().size() + 1;
        }
        LinearLayoutManager layoutManager = (LinearLayoutManager) rightMenu.getLayoutManager();
        layoutManager.scrollToPositionWithOffset(sum, 0);
        leftClickType = true;
    }

    @Override
    public void add(View view, int position) {
        int[] addLocation = new int[2];
        int[] cartLocation = new int[2];
        int[] recycleLocation = new int[2];
        view.getLocationInWindow(addLocation);
        servicesCartView.getLocationInWindow(cartLocation);
        rightMenu.getLocationInWindow(recycleLocation);

        PointF startP = new PointF();
        PointF endP = new PointF();
        PointF controlP = new PointF();

        startP.x = addLocation[0];
        startP.y = addLocation[1] - recycleLocation[1];
        endP.x = cartLocation[0];
        endP.y = cartLocation[1] - recycleLocation[1];
        controlP.x = endP.x;
        controlP.y = startP.y;

        final FakeAddImageView fakeAddImageView = new FakeAddImageView(this);
        mainLayout.addView(fakeAddImageView);
        fakeAddImageView.setImageResource(R.drawable.ic_add_circle_blue_700_36dp);
        fakeAddImageView.getLayoutParams().width = getResources().getDimensionPixelSize(R.dimen.item_service_circle_size);
        fakeAddImageView.getLayoutParams().height = getResources().getDimensionPixelSize(R.dimen.item_service_circle_size);
        fakeAddImageView.setVisibility(View.VISIBLE);
        ObjectAnimator addAnimator = ObjectAnimator.ofObject(fakeAddImageView, "mPointF",
                new PointFTypeEvaluator(controlP), startP, endP);
        addAnimator.setInterpolator(new AccelerateInterpolator());
        addAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
                fakeAddImageView.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animator animator) {
                fakeAddImageView.setVisibility(View.GONE);
                mainLayout.removeView(fakeAddImageView);
            }

            @Override
            public void onAnimationCancel(Animator animator) {

            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });

        ObjectAnimator scaleAnimatorX = new ObjectAnimator().ofFloat(servicesCartView, "scaleX", 0.6f, 1.0f);
        ObjectAnimator scaleAnimatorY = new ObjectAnimator().ofFloat(servicesCartView, "scaleY", 0.6f, 1.0f);
        scaleAnimatorX.setInterpolator(new AccelerateInterpolator());
        scaleAnimatorY.setInterpolator(new AccelerateInterpolator());
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.play(scaleAnimatorX).with(scaleAnimatorY).after(addAnimator);
        animatorSet.setDuration(800);
        animatorSet.start();
        showTotalPrice();
    }

    @Override
    public void remove(View view, int position) {
        showTotalPrice();
    }

    @SuppressLint("SetTextI18n")
    private void showTotalPrice() {
        if (servicesCart != null && servicesCart.getServicesTotalPrice() > 0) {
            totalPriceTextView.setVisibility(View.VISIBLE);
            totalPriceTextView.setText(FormatMoney.INSTANCE.formatMoney().format((double) servicesCart.getServicesTotalPrice()));
            totalPriceNumTextView.setVisibility(View.VISIBLE);
            totalPriceNumTextView.setText("" + servicesCart.getServicesAccount());

        } else {
            totalPriceTextView.setVisibility(View.GONE);
            totalPriceNumTextView.setVisibility(View.GONE);
        }
    }

    private void showCart(View view) {
        if (servicesCart != null && servicesCart.getServicesAccount() > 0) {
            ServicesCartDialog dialog = new ServicesCartDialog(this, servicesCart, R.style.Cartdialog);
            Window window = dialog.getWindow();
            dialog.setShopCartDialogImp(this);
            dialog.setCanceledOnTouchOutside(true);
            dialog.setCancelable(true);
            dialog.show();
            WindowManager.LayoutParams params = window.getAttributes();
            params.width = WindowManager.LayoutParams.MATCH_PARENT;
            params.height = WindowManager.LayoutParams.WRAP_CONTENT;
            params.gravity = Gravity.BOTTOM;
            params.dimAmount = 0.5f;
            window.setAttributes(params);
        }
    }

    @Override
    public void dialogDismiss() {
        showTotalPrice();
        rightAdapter.notifyDataSetChanged();
    }
}

Buat Class Transaksi ... (Still Under Development)

Setelah anda memilih jenis pakaian yang akan dilaundry, selanjutnya anda menentukan alamat dan metode pambayaran menggunakan gopay atau cash

class TransactionActivity : AppCompatActivity() {

    private var tvName: TextView? = null
    private var tvAddress: TextView? = null
    private var tvAddressDetail: TextView? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_transaksi)
        init()
    }

    private fun init() {
        val bottomBack = findViewById<LinearLayout>(R.id.bottom_back)
        bottomBack.setOnClickListener { view -> finish() }

        findViewById<View>(R.id.btn_change_address).setOnClickListener { v ->
            startActivity(
                Intent(
                    this@TransactionActivity,
                    ChangeAddressActivity::class.java
                )
            )
        }

        val intent = intent
        (findViewById<View>(R.id.services_cart_total_tv) as TextView).text =
            intent.getStringExtra("total_price")
        tvName = findViewById(R.id.tv_name)

        tvAddress = findViewById(R.id.tv_address)
        tvAddressDetail = findViewById(R.id.tv_address_detail)

        if (intent.getStringExtra("address") != null) {
            tvAddress!!.text = intent.getStringExtra("address")
            tvAddressDetail!!.text = intent.getStringExtra("address_detail")
        }
    }
}

Buat Class Change Address

Digunakan untuk menentukan alamat konsumen berdasarkan pada Lokasi GPS atau Lokasi yang dipilih pada MAP

class ChangeAddressActivity : FragmentActivity(), OnMapReadyCallback,
    GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
    com.google.android.gms.location.LocationListener {

    private var mMap: GoogleMap? = null
    private var mGoogleApiClient: GoogleApiClient? = null
    private lateinit var mContext: Context
    private lateinit var mAddress: TextView
    private lateinit var mAddressDetail: TextView
    private var mCenterLatLong: LatLng? = null
    private var mBottomSheetBehavior: BottomSheetBehavior<*>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_change_address)
        mContext = this

        initView()
    }

    private fun initView() {
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        val mapFragment = supportFragmentManager
            .findFragmentById(R.id.map) as SupportMapFragment?

        mAddressDetail = findViewById(R.id.tv_address_detail)
        mAddress = findViewById(R.id.tv_address)
        findViewById<View>(R.id.backbtn).setOnClickListener { v -> finish() }

        mapFragment!!.getMapAsync(this)

        if (checkPlayServices()) {
            // If this check succeeds, proceed with normal processing.
            // Otherwise, prompt user to get valid Play Services APK.
            if (!LocationUtils.isLocationEnabled(mContext)) {
                // notify userLocality
                val dialog = AlertDialog.Builder(mContext)
                dialog.setMessage("Location not enabled!")
                dialog.setPositiveButton("Open location settings") { paramDialogInterface, paramInt ->
                    val myIntent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
                    startActivity(myIntent)
                }
                dialog.setNegativeButton("Cancel") { paramDialogInterface, paramInt ->
                    // TODO Auto-generated method stub

                }
                dialog.show()
            }
            buildGoogleApiClient()
        } else {
            Toast.makeText(mContext, "Location not supported in this device", Toast.LENGTH_SHORT)
                .show()
        }

        val bottomSheet = findViewById<View>(R.id.bottom_sheet)
        mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
        mBottomSheetBehavior!!.isHideable = true
        mBottomSheetBehavior!!.state = BottomSheetBehavior.STATE_HIDDEN

        findViewById<View>(R.id.btn_select_location).setOnClickListener { v ->
            val intent = Intent(this@ChangeAddressActivity, TransactionActivity::class.java)
            intent.putExtra("address", mAddress.text)
            intent.putExtra("address_detail", mAddressDetail.text)
            startActivity(intent)
        }
    }

    override fun onMapReady(googleMap: GoogleMap) {
        initialiseMap(googleMap)
    }

    @SuppressLint("SetTextI18n")
    private fun initialiseMap(googleMap: GoogleMap) {
        mMap = googleMap

        Log.d(TAG, "OnMapReady")
        mMap = googleMap

        mMap!!.setOnCameraIdleListener {
            // Cleaning all the markers.
            if (mMap != null) {
                mMap!!.clear()
            }

            mCenterLatLong = mMap!!.cameraPosition.target
            mMap!!.clear()
            try {

                val mLocation = Location("")
                mLocation.latitude = mCenterLatLong!!.latitude
                mLocation.longitude = mCenterLatLong!!.longitude

                getCompleteAddressString(mCenterLatLong!!.latitude, mCenterLatLong!!.longitude)

                mBottomSheetBehavior!!.setState(BottomSheetBehavior.STATE_EXPANDED)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }

        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_COARSE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return
        }
    }


    override fun onConnected(bundle: Bundle?) {
        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_COARSE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return
        }
        val mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
            mGoogleApiClient
        )
        if (mLastLocation != null) {
            changeMap(mLastLocation)
            Log.d(TAG, "ON connected")

        } else
            try {
                LocationServices.FusedLocationApi.removeLocationUpdates(
                    mGoogleApiClient, this
                )

            } catch (e: Exception) {
                e.printStackTrace()
            }

        try {
            val mLocationRequest = LocationRequest()
            mLocationRequest.interval = 10000
            mLocationRequest.fastestInterval = 5000
            mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
            LocationServices.FusedLocationApi.requestLocationUpdates(
                mGoogleApiClient, mLocationRequest, this
            )

        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    override fun onConnectionSuspended(i: Int) {
        Log.i(TAG, "Connection suspended")
        mGoogleApiClient!!.connect()
    }

    override fun onLocationChanged(location: Location?) {
        try {
            if (location != null)
                changeMap(location)
            LocationServices.FusedLocationApi.removeLocationUpdates(
                mGoogleApiClient, this
            )

        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    override fun onConnectionFailed(connectionResult: ConnectionResult) {

    }

    @Synchronized
    protected fun buildGoogleApiClient() {
        mGoogleApiClient = GoogleApiClient.Builder(this)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build()
    }

    override fun onStart() {
        super.onStart()
        try {
            mGoogleApiClient!!.connect()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    override fun onStop() {
        super.onStop()
        try {

        } catch (e: RuntimeException) {
            e.printStackTrace()
        }

        if (mGoogleApiClient != null && mGoogleApiClient!!.isConnected) {
            mGoogleApiClient!!.disconnect()
        }
    }

    private fun checkPlayServices(): Boolean {
        val resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this)
        if (resultCode != ConnectionResult.SUCCESS) {
            if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
                GooglePlayServicesUtil.getErrorDialog(
                    resultCode, this,
                    PLAY_SERVICES_RESOLUTION_REQUEST
                ).show()
            } else {
                //finish();
            }
            return false
        }
        return true
    }

    private fun changeMap(location: Location) {
        Log.d(TAG, "Reaching map" + mMap!!)

        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_COARSE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return
        }

        // check if map is created successfully or not
        if (mMap != null) {
            mMap!!.uiSettings.isZoomControlsEnabled = false
            val latLong: LatLng

            latLong = LatLng(location.latitude, location.longitude)

            val cameraPosition = CameraPosition.Builder()
                .target(latLong).zoom(12f).build()
            //                  .target(latLong).zoom(19f).tilt(70).build();

            mMap!!.isMyLocationEnabled = true
            mMap!!.uiSettings.isMyLocationButtonEnabled = true
            mMap!!.animateCamera(
                CameraUpdateFactory
                    .newCameraPosition(cameraPosition)
            )

            getCompleteAddressString(location.latitude, location.longitude)

        } else {
            Toast.makeText(
                applicationContext,
                "Sorry! unable to create maps", Toast.LENGTH_SHORT
            )
                .show()
        }
    }

    @SuppressLint("LongLogTag")
    private fun getCompleteAddressString(LATITUDE: Double, LONGITUDE: Double) {

        val geocoder = Geocoder(this, Locale.getDefault())
        try {
            val addresses = geocoder.getFromLocation(LATITUDE, LONGITUDE, 1)
            if (addresses != null) {
                val returnedAddress = addresses[0]
                val strReturnedAddress = StringBuilder("")

                for (i in 0..returnedAddress.maxAddressLineIndex) {
                    strReturnedAddress.append(returnedAddress.getAddressLine(i)).append("\n")
                }
                mAddress.text = returnedAddress.subLocality
                mAddressDetail.text = strReturnedAddress.toString()

            } else {
                Log.w("My Current loction address", "No Address returned!")
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Log.w("My Current loction address", "Canont get Address!")
        }
    }

    companion object {
        private const val PLAY_SERVICES_RESOLUTION_REQUEST = 9000
        private const val TAG = "MAP LOCATION"
    }
}

Resource Template Shopping Cart Update Kodetr: Kodetr

Resource Template Shopping Cart: Github


Jika anda sudah mengikuti langkah-langkah sesuai artikel, berarti Anda sudah berhasil membuat aplikasi E-Laundry, demikian yang dapat saya sampaikan dari artikel ini semoga bermanfaat, untuk lebih lengkapnya anda bisa lihat langkah-langkah pembuatan di bagian video, jika ada yang ditanyakan silahkan di kolom komentar dibawah, selamat mencoba.

Share Comments
comments powered by Disqus