Monday, December 18, 2023

Sablon manual screen

Screen

Ukuran screen yang siap pakai

  1. 20x30
  2. 40x60
  3. lebih besar

Kerapatan screen

kerapatanmedia yang disablon
T35, T30, T36, T40
handuk, karung
T48, T54, T61, T62, T65, T77
kain, kaos
T90, T120
kayu, gelas
T150, T165, T180, T200 (200s)
kertas, plastik, stiker

Bahan afdruk screen

Obat afdruk 

  1. Diazol
  2. Rainbow
  3. Ulano
  4. Photazol
  5. TX-super
  6. Meta-X
  7. Bremol tex (cat water) / Bremol RN (cat minyak/plastisol)

Ulano

Afdruk Ulano

  1. oil resistence (untuk cat berbasis minyak/plastisol): Ulano 133, 569, FX88, QFX, DLX, QX1, QX3
  2. water resistence (untuk cat berbasis air): Ulano TZ, QTZ, QX5
  3. water & oil resistence: Ulano LX 660, LX758, LX 836, LX892

Fabric abrader,& de-greaser Ulano 3, Gel 23

Remover Ulano 4 (liquid) & Ulano 5 (pasta)

Fabric regeneartor Ulano 8

Screen filler Ulano 6

Hardener afdruk Ulano X

Bahan kimia lain

Bahan kimia lain untuk reducer (menghapus) screen

  1. soda api 1:1
  2. pregant paste 1:1
  3. reducer pvc
  4. natrium hipolorit

Tinta

Bedasarkan media sablon:

  1. kain
  2. kertas
  3. kayu, seng, triplex
  4. kulit, mika

Tool

Alat pendukung lainnya

  1. rakel
  2. meja sablon
  3. gelas, mangkuk, botol gelap, alat aduk (sendok plastik)
  4. kaca bening penekan negatif
  5. busa penyangga screen
  6. triplex untuk menekan busa ke screen

Penyablonan

  1. Desain gambar yang akan disablon dikertas tembus cahaya UV, Bila desain diceta di kertas HVS, gunakan minyak goreng bersih untuk mengolesi hasil print di kertas HVS. posisi gambar yang disablon mirror kiri kanan.
  2. Olesi screen dengan bahan afdruk secara merata dan tunggu kering 
  3. Susun screen dengan urutan paling atas
    1. kaca penjepit
    2. desain gambar
    3. screen
    4. busa
    5. triplex
  4. Sinari dibawah terik matahari +- 30 detik (tergantung kecerahan), lampu UV 40 watt selama 3-10 menit
  5. perbaiki negatif bila ada yang rusak
  6. siram dengan air panas dan semprot dengan air bersih


Referensi

  1. website: centralsps.co.id
  2. website: semua25.blogspot.com 
  3. website: oscas.co.id
  4. pdf "Teknik Dasar Cetak Sablon", Noor Fitrihana, ST & Widihastuti, M.Pd
  5. pdf "Teknik Pencelupan dan Pencapan untuk Sekolah Menengah Kejuruan Jilid 3", Sunarto, Direktorat Pembinaan Sekolah Menengah Kejuruan


Wednesday, December 13, 2023

Android java: SQLiteOpenHelper, Fragment & RecyclerView in java

 

Android Studio Hedgehog | 2023.1.1

Note: For personal reference using SearchView, Filter (text), SQLiteOpenHelper, RecyclerView and Fragment

Create new Project (No Activity)

Name CA Price List
Package Name com.dedetok.capricelist

Create Activity -> Empty View Activity

Activity Name: Main Activity
Layout Name activity_main
Laucher Activity checked

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- for >= android 6 api level 23 -->
    <uses-permission-sdk-23 android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission-sdk-23 android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.CAPriceList"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

build.gradle.kts(Module :app)

plugins {
    id("com.android.application")
}

android {
    namespace = "com.dedetok.capricelist"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.dedetok.capricelist"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"
,,,

Resource -> Values -> strings.xml

<resources>
    <string name="app_name">CA Price List</string>
    <string name="t_cancel">Cancel</string>
    <string name="t_save">Save</string>
    <string name="t_delete">Delete</string>
    <string name="t_product_name">Product Name</string>
    <string name="t_product_price">Product Price</string>
    <string name="t_product_code">Product Code</string>
    <string name="t_product_id">Product ID</string>
    <string name="t_new_product">New Product</string>
</resources>

Menu

my_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id = "@+id/menu_add_pricelist"
        android:title="@string/t_new_product"
        />
</menu>

Layout

layout_price_list.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:id="@+id/pricelist_item"
    android:minHeight="48dp"
    android:layout_margin="5dp"
    >

</TextView>

layout_fragment_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <SearchView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/sv_text"
        />
    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/my_recycler_view"
        android:scrollbars="vertical"
    />
</LinearLayout>

layout_fragment_detail.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:id="@+id/product_id"
        android:text="@string/t_product_id"
        android:layout_margin="15dp"
        />
    <EditText
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:id="@+id/product_name"
        android:minHeight="48dp"
        android:hint="@string/t_product_name"
        android:autofillHints="@string/t_product_name"
        android:inputType="text"
        android:layout_margin="15dp"
        />

    <EditText
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:id="@+id/product_price"
        android:minHeight="48dp"
        android:autofillHints="@string/t_product_price"
        android:hint="@string/t_product_price"
        android:inputType="text"
        android:layout_margin="15dp"
        />
    <EditText
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:id="@+id/product_code"
        android:minHeight="48dp"
        android:autofillHints="@string/t_product_code"
        android:hint="@string/t_product_code"
        android:inputType="text"
        android:layout_margin="15dp"
        />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_margin="15dp"
        android:gravity="center_horizontal"
        >
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/t_cancel"
            android:id="@+id/b_cancel_fragment_detail"
            android:contentDescription="@string/t_cancel"
            android:layout_margin="5dp"
            />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/t_save"
            android:id="@+id/b_save_fragment_detail"
            android:contentDescription="@string/t_save"
            android:layout_margin="5dp"
            />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/t_delete"
            android:id="@+id/b_delete_fragment_detail"
            android:contentDescription="@string/t_delete"
            android:layout_margin="5dp"
            />
    </LinearLayout>
</LinearLayout>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/my_fragmentcontainerview"
        android:scrollbars="vertical"
        app:layoutManager="LinearLayoutManager"
        />
</androidx.constraintlayout.widget.ConstraintLayout>

Create java package: New -> Packages : com.dedetok.capricelist.DBHelper

Java in com.dedetok.capricelist.DBHelper packages

PriceList.java

package com.dedetok.capricelist.DBHelper;


/*
 * Table for price_list
 */
public class PriceList {
    public static final String TABLENAME = "price_list";

    /*
     * column name and mapping
     */
    // Primary Key
    public static final String IDPRODUCT = "id_product";
    public long idProduct;

    public static final String NAMEPRODUCT = "name_product";
    public String nameProduct;

    public static final String PRICEPRODUCT = "price_product";
    public String priceProduct;

    public static final String CODEPRODUCT = "code_product";
    public String codeProduct;

    //debug
    public String getText() {
        return idProduct+" "+nameProduct+" "+priceProduct+" "+codeProduct;
    }
}

MySQLiteHelper.java

package com.dedetok.capricelist.DBHelper;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import androidx.annotation.Nullable;

public class MySQLiteHelper extends SQLiteOpenHelper {

    public static final int DATABASE_VERSION = 1;
    public static final String DATABASE_NAME = "CAPriceList.db";
    private static final String SQLCREATEDB = "create TABLE "+PriceList.TABLENAME +"(" +
            PriceList.IDPRODUCT+ " INTEGER PRIMARY KEY AUTOINCREMENT," +
            PriceList.NAMEPRODUCT+ " TEXT NOT NULL," +
            PriceList.PRICEPRODUCT+ " TEXT NOT NULL," +
            PriceList.CODEPRODUCT+ " Text);";

    public MySQLiteHelper(@Nullable Context myAppContext, @Nullable String myDBName, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
        super(myAppContext, myDBName, factory, version);
    }

    /*
     * Mandatory to create   table
     */
    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL(SQLCREATEDB);

    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
        // TODO

    }
}

BoyerMooreTextSearch.java -> fast match string better then indexOf() or contain()

package com.dedetok.capricelist.DBHelper;

public class BoyerMooreTextSearch {
    /**
     * https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm#Java_implementation
     *
     * Returns the index within this string of the first occurrence of the
     * specified substring. If it is not a substring, return -1.
     *
     * There is no Galil because it only generates one match.
     *
     * @param haystack The string to be scanned
     * @param needle The target string to search
     * @return The start index of the substring
     */
    public static int indexOf(char[] haystack, char[] needle) {
        if (needle.length == 0) {
            return 0;
        }
        int charTable[] = makeCharTable(needle);
        int offsetTable[] = makeOffsetTable(needle);
        for (int i = needle.length - 1, j; i < haystack.length;) {
            for (j = needle.length - 1; needle[j] == haystack[i]; --i, --j) {
                if (j == 0) {
                    return i;
                }
            }
            // i += needle.length - j; // For naive method
            i += Math.max(offsetTable[needle.length - 1 - j], charTable[haystack[i]]);
        }
        return -1;
    }

    /**
     * Makes the jump table based on the mismatched character information.
     */
    private static int[] makeCharTable(char[] needle) {
        final int ALPHABET_SIZE = Character.MAX_VALUE + 1; // 65536
        int[] table = new int[ALPHABET_SIZE];
        for (int i = 0; i < table.length; ++i) {
            table[i] = needle.length;
        }
        for (int i = 0; i < needle.length; ++i) {
            table[needle[i]] = needle.length - 1 - i;
        }
        return table;
    }

    /**
     * Makes the jump table based on the scan offset which mismatch occurs.
     * (bad-character rule).
     */
    private static int[] makeOffsetTable(char[] needle) {
        int[] table = new int[needle.length];
        int lastPrefixPosition = needle.length;
        for (int i = needle.length; i > 0; --i) {
            if (isPrefix(needle, i)) {
                lastPrefixPosition = i;
            }
            table[needle.length - i] = lastPrefixPosition - i + needle.length;
        }
        for (int i = 0; i < needle.length - 1; ++i) {
            int slen = suffixLength(needle, i);
            table[slen] = needle.length - 1 - i + slen;
        }
        return table;
    }

    /**
     * Is needle[p:end] a prefix of needle?
     */
    private static boolean isPrefix(char[] needle, int p) {
        for (int i = p, j = 0; i < needle.length; ++i, ++j) {
            if (needle[i] != needle[j]) {
                return false;
            }
        }
        return true;
    }

    /**
     * Returns the maximum length of the substring ends at p and is a suffix.
     * (good-suffix rule)
     */
    private static int suffixLength(char[] needle, int p) {
        int len = 0;
        for (int i = p, j = needle.length - 1;
             i >= 0 && needle[i] == needle[j]; --i, --j) {
            len += 1;
        }
        return len;
    }

}

Java in com.dedetok.capricelist packages

DBCARepository.java

package com.dedetok.capricelist;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.dedetok.capricelist.DBHelper.MySQLiteHelper;
import com.dedetok.capricelist.DBHelper.PriceList;
import java.util.ArrayList;

// https://www.geeksforgeeks.org/singleton-class-in-android/
// https://www.androidcode.ninja/android-sqlite-transaction-tutorial/


public class DBCARepository {

    private static MySQLiteHelper mySQLiteHelper = null;

    // private static instance variable to hold the singleton instance
    private static volatile DBCARepository myDBCARepository = null;

    // private constructor to prevent instantiation of the class
    private DBCARepository(Context appContext) {
        if (mySQLiteHelper == null) {
            mySQLiteHelper = new MySQLiteHelper(appContext,
                MySQLiteHelper.DATABASE_NAME, null,
                MySQLiteHelper.DATABASE_VERSION);
        }
    }

    // public static method to retrieve the singleton instance
    public static DBCARepository getInstance() {
        return myDBCARepository;
    }

    public static void prepareDB(Context myAppContext) {
        // Check if the instance is already created
        if(myDBCARepository == null) {
            // synchronize the block to ensure only one thread can execute at a time
            synchronized (DBCARepository.class) {
                // check again if the instance is already created
                if (myDBCARepository == null) {
                    // create the singleton instance
                    myDBCARepository = new DBCARepository(myAppContext);
                }
            }
        }
    }

    public ArrayList<PriceList> getPriceLists() {
        // https://developer.android.com/training/data-storage/sqlite
        SQLiteDatabase myDB = mySQLiteHelper.getReadableDatabase();

        String[] projection =   {
                PriceList.IDPRODUCT, //0
                PriceList.NAMEPRODUCT, //1
                PriceList.PRICEPRODUCT, //2
                PriceList.CODEPRODUCT //3
        };

        // Filter results WHERE "title" = 'My Title'
        // String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";
        String selection = null; // Not Used set null
        // String[] selectionArgs = { "My Title" };
        String[] selectionArgs = null; // Not used set null

        // How you want the results sorted in the resulting Cursor
        //String sortOrder =
        //        FeedEntry.COLUMN_NAME_SUBTITLE + " DESC";
        String sortOrder = null; // Not used set null

        Cursor cursor =  myDB.query(
                PriceList.TABLENAME,
                projection,             // The array of columns to return (pass null to get all)
                selection,              // The columns for the WHERE clause
                selectionArgs,          // The values for the WHERE clause
                null,                   // don't group the rows
                null,                   // don't filter by row groups
                sortOrder               // The sort order
        );

        ArrayList itemList = new ArrayList<>();
        while(cursor.moveToNext()) {
            PriceList mPriceList = new PriceList();
            mPriceList.idProduct = cursor.getLong(0);
            //Log.e("dedetok", Long.toString(cursor.getLong(0))); // debug
            mPriceList.nameProduct = cursor.getString(1);
            //Log.e("dedetok", cursor.getString(1)); // debug
            mPriceList.priceProduct = cursor.getString(2);
            mPriceList.codeProduct = cursor.getString(3);
            itemList.add(mPriceList);
            //Log.e("dedetok", "getPriceLists()"+ mPriceList.getText()); //debug
        }
        cursor.close();
        myDB.close();

        return itemList;
    }

    /*
     * insert a new Price List, ID is auto number
     */
    public long insertPriceList(PriceList newPriceList) {
        //Log.e("dedetok", "DBACARepository insertPriceList get DB"); // debug
        SQLiteDatabase myDB = mySQLiteHelper.getWritableDatabase();
        ContentValues myContent = new ContentValues();
        myContent.put(PriceList.NAMEPRODUCT, newPriceList.nameProduct);
        myContent.put(PriceList.PRICEPRODUCT, newPriceList.priceProduct);
        myContent.put(PriceList.CODEPRODUCT, newPriceList.codeProduct);
        //Log.e("dedetok", "DBACARepository insertPriceList insert data"); // debug
        long retValue = myDB.insert(PriceList.TABLENAME, null, myContent);
        //Log.e("dedetok", "DBACARepository insertPriceList insert data result "+retValue); // debug
        //Log.e("dedetok", "DBACARepository insertPriceList close db"); // debug
        myDB.close();
        return retValue;
    }

    // Save / update
    /*
     * To update price list after editing
     */
    public int updatePriceList(PriceList myPriceList) {
        SQLiteDatabase myDB = mySQLiteHelper.getWritableDatabase();
        ContentValues myContent = new ContentValues();
        myContent.put(PriceList.IDPRODUCT, myPriceList.idProduct);
        myContent.put(PriceList.NAMEPRODUCT, myPriceList.nameProduct);
        myContent.put(PriceList.PRICEPRODUCT, myPriceList.priceProduct);
        myContent.put(PriceList.CODEPRODUCT, myPriceList.codeProduct);
        // Filter results WHERE "title" = 'My Title'
        // String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";
        String selection = PriceList.IDPRODUCT + " = ?";
        // String[] selectionArgs = { "My Title" };
        String[] selectionArgs = {Long.toString(myPriceList.idProduct)}; // Not used set null
        int retVal = myDB.update(PriceList.TABLENAME, myContent, selection, selectionArgs);
        return retVal;
    }

    /*
     * delete
     */
    public int deletePriceList(PriceList myPriceList) {
        SQLiteDatabase myDB = mySQLiteHelper.getWritableDatabase();
        // Filter results WHERE "title" = 'My Title'
        // String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";
        String selection = PriceList.IDPRODUCT + " = ?";
        // String[] selectionArgs = { "My Title" };
        String[] selectionArgs = {Long.toString(myPriceList.idProduct)};
        int retVal = myDB.delete(PriceList.TABLENAME, selection, selectionArgs);
        return retVal;
    }
}

PriceListsAdapter.java

package com.dedetok.capricelist;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.RecyclerView;

import com.dedetok.capricelist.DBHelper.BoyerMooreTextSearch;
import com.dedetok.capricelist.DBHelper.PriceList;

import java.util.ArrayList;

// https://developer.android.com/develop/ui/views/layout/recyclerview

public class PriceListsAdapter extends RecyclerView.Adapter implements Filterable {

    // variable
    private ArrayList<PriceList> myPriceListsFiltered;
    private ArrayList<PriceList> myPriceListsOriginal;

    /*
     * construtor to initilize array list
     */
    public PriceListsAdapter() {
        myPriceListsOriginal = DBCARepository.getInstance().getPriceLists();
        myPriceListsFiltered = myPriceListsOriginal;
    }

    /*
     * mandatory extending RecyclerView.Adapter
     * constructor
     */
    @NonNull
    @Override
    public PriceListsAdapter.PriceListHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // Create a new view, which defines the UI of the list item
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.layout_price_list, parent, false);
        return new PriceListsAdapter.PriceListHolder (view);
    }

    /*
     * mandatory extending RecyclerView.Adapter
     */
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        // Get element from your dataset at this position and replace the
        // contents of the view with that element
        PriceList myPriceList = myPriceListsFiltered.get(position);
        String tmpString = Long.toString(myPriceList.idProduct)+" "+
                myPriceList.nameProduct+" "+
                myPriceList.priceProduct+" "+
                myPriceList.codeProduct;
        ((PriceListHolder) holder).textView.setText(tmpString);
        // https://www.geeksforgeeks.org/how-to-apply-onclicklistener-to-recyclerview-items-in-android/
        holder.itemView.setOnClickListener(
                new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        PriceList mPriceList = myPriceListsFiltered.get(holder.getAdapterPosition());
                        FragmentDetail myFragmentDetail = new FragmentDetail(mPriceList);
                        ((FragmentActivity) view.getContext()).getSupportFragmentManager().
                                beginTransaction().
                                replace(R.id.my_fragmentcontainerview, myFragmentDetail).
                                addToBackStack("List").
                                commit();


                        //Log.e("dedetok", "position "+ Integer.toString(holder.getAdapterPosition())); // debug
                    }
                }
        );

    }

    /*
     * mandatory extending RecyclerView.Adapter
     * return number of item on ArrayList<PriceList> myPriceLists
     */
    @Override
    public int getItemCount() {
        return myPriceListsFiltered.size();
    }

    /*
     * implements android.widget.Filterable;
     * https://codingwithmitch.com/blog/filtering-recyclerview-searchview/
     */
    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence charSequence) {
                String tmpPattern = charSequence.toString().toUpperCase();

                if (tmpPattern.isEmpty()) {
                    myPriceListsFiltered = myPriceListsOriginal;
                } else {
                    myPriceListsFiltered = new ArrayList<PriceList>();
                    for (PriceList myPriceList : myPriceListsOriginal) {
                        String tmpText = myPriceList.nameProduct.toUpperCase();
                        if (BoyerMooreTextSearch.indexOf(tmpText.toCharArray(), tmpPattern.toCharArray())> -1) {
                            myPriceListsFiltered.add(myPriceList);
                        }
                    }
                }
                FilterResults results = new FilterResults();
                results.values = myPriceListsFiltered;
                return results;
            }

            @Override
            protected void publishResults(CharSequence charSequence, FilterResults results) {
                myPriceListsFiltered = (ArrayList<PriceList>) results.values;
                notifyDataSetChanged();
            }
        };
    }

    private class PriceListHolder extends RecyclerView.ViewHolder {

        public TextView textView;

        /*
         * mandatory extends RecyclerView.ViewHolder
         */
        public PriceListHolder(@NonNull View view) {
            super(view);
            textView = view.findViewById(R.id.pricelist_item);
        }


    }
}

FragmentDetail.java

package com.dedetok.capricelist;

import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;

import com.dedetok.capricelist.DBHelper.PriceList;

public class FragmentDetail extends Fragment {

    PriceList myPriceList=null;

    /*
     * constructor to inflate fragment
     */
    public FragmentDetail() {
        super(R.layout.layout_fragment_detail);
    }

    public FragmentDetail(PriceList myPriceList) {
        super(R.layout.layout_fragment_detail);
        this.myPriceList = myPriceList;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        // EditText
        EditText etProductName = view.findViewById(R.id.product_name);
        EditText etProductPrice = view.findViewById(R.id.product_price);
        EditText etProductCode = view.findViewById(R.id.product_code);
        if (myPriceList!=null) {
            // if edit or delete data
            TextView tvProductID = view.findViewById(R.id.product_id);
            tvProductID.setText(myPriceList.codeProduct);
            etProductName.setText(myPriceList.nameProduct);
            etProductPrice.setText(myPriceList.priceProduct);
            etProductCode.setText(myPriceList.codeProduct);
        }

        // button cancel
        Button bCancelFragmentDetail = view.findViewById(R.id.b_cancel_fragment_detail);
        bCancelFragmentDetail.setOnClickListener(cancelFragmentDetail -> getParentFragmentManager().popBackStack());

        // button save
        Button bSaveFragmentDetail = view.findViewById(R.id.b_save_fragment_detail);
        bSaveFragmentDetail.setOnClickListener(saveFragmentDetail -> {
            if (myPriceList ==null) {
                // new data
                //Log.e("dedetok", "FragmentDetail insert data"); //debug
                PriceList newPriceList = new PriceList();
                newPriceList.nameProduct = etProductName.getText().toString();
                newPriceList.priceProduct = etProductPrice.getText().toString();
                newPriceList.codeProduct = etProductCode.getText().toString();
                DBCARepository.getInstance().insertPriceList(newPriceList);
                //Log.e("dedetok", "FragmentDetail insert data end"); // debug
                getParentFragmentManager().popBackStack();
            } else {
                // save existing data
                myPriceList.nameProduct = etProductName.getText().toString();
                myPriceList.priceProduct = etProductPrice.getText().toString();
                myPriceList.codeProduct = etProductCode.getText().toString();
                DBCARepository.getInstance().updatePriceList(myPriceList);
                getParentFragmentManager().popBackStack();
            }
        });

        // https://stackoverflow.com/questions/38411879/alert-confirmation-dialog-android
        // https://stackoverflow.com/questions/10207206/how-to-display-alertdialog-in-a-fragment
        Button bDeleteFragmentDetail = view.findViewById(R.id.b_delete_fragment_detail);
        if (myPriceList == null) {
            // new data, we don't have to delete
            bDeleteFragmentDetail.setVisibility(View.INVISIBLE);
        } else {
            bDeleteFragmentDetail.setVisibility(View.VISIBLE);
            bDeleteFragmentDetail.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());
                    alert.setTitle("Delete");
                    alert.setMessage("Are you sure you want to delete?");
                    alert.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            DBCARepository.getInstance().deletePriceList(myPriceList);
                            Log.e("dedetok", "deleted"); // debug
                            dialogInterface.dismiss();
                        }
                    });
                    alert.setNegativeButton("No", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            dialogInterface.dismiss();
                        }
                    });
                    alert.show();
                }
            });
        }
    }
}

FragmentList.java

package com.dedetok.capricelist;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.SearchView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

public class FragmentList extends Fragment {


    /*
     * constructor to inflate fragment
     */
    public FragmentList() {
        super(R.layout.layout_fragment_list);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        //recyclerview
        RecyclerView myRecyclerView = view.findViewById(R.id.my_recycler_view);
        LinearLayoutManager myLinearLayout = new LinearLayoutManager(getActivity().getApplicationContext());
        myLinearLayout.setOrientation(LinearLayoutManager.VERTICAL);
        myRecyclerView.setLayoutManager(myLinearLayout);
        PriceListsAdapter myPriceListAdapter = new PriceListsAdapter();
        myRecyclerView.setAdapter(myPriceListAdapter);

        // https://codingwithmitch.com/blog/filtering-recyclerview-searchview/
        // SearchView
        SearchView mySearchView = view.findViewById(R.id.sv_text);
        // add listener for searchview
        mySearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String s) {
                myPriceListAdapter.getFilter().filter(s);
                return false;
            }

            @Override
            public boolean onQueryTextChange(String s) {
                myPriceListAdapter.getFilter().filter(s);
                return false;            }
        });

    }
}

MainActivity.java

package com.dedetok.capricelist;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;


public class MainActivity extends AppCompatActivity {

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

        // initialize database
        Log.e("dedetok", "MainActivity OnCreate Initilize database"); // debug
        DBCARepository.prepareDB(getApplicationContext());
        Log.e("dedetok", "MainActivity OnCreate Initilize database end"); // debug

        getSupportFragmentManager().beginTransaction().
                add(R.id.my_fragmentcontainerview, FragmentList.class, null).
                commit();

    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.my_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId()==R.id.menu_add_pricelist) {
            // open fragment Detail for new PriceList
            getSupportFragmentManager().beginTransaction().
                    replace(R.id.my_fragmentcontainerview, FragmentDetail.class,null).
                    addToBackStack("List").
                    commit();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

}

NOTE: Some code may reference to other source code in comment (some has modified to make it run), do not remove urls if you wish to copy paste code and use the code.


Android Studio Hedgehog | 2023.1.1: Java RecyclerView & Fragment

Android Studio Hedgehog | 2023.1.1

Note: For personal reference using RecyclerView and Fragment

Create new Project (No Activity)

Name Recycler View Tutorial
Package Name com.dedetok.recyclerviewtutorial

Create Activity -> Empty View Activity

Activity Name: Main Activity
Layout Name activity_main
Laucher Activity checked

build.gradle.kts(Module :app)

plugins {
    id("com.android.application")
}

android {
    namespace = "com.dedetok.recyclerviewtutorial"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.dedetok.recyclerviewtutorial"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"
,,,

Layout

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id = "@+id/frag_container"/>
</androidx.constraintlayout.widget.ConstraintLayout>

my_single_column.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="72dp"
    android:orientation="vertical"
    >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/first_column" />

</androidx.appcompat.widget.LinearLayoutCompat>

 my_recycler_view_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id = "@+id/r_view_main_layout"/>

</androidx.appcompat.widget.LinearLayoutCompat>

Java

MyAdapter.java

package com.dedetok.recyclerviewtutorial;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

public class MyAdapter extends RecyclerView.Adapter {

    private String[] mDataSet;

    // constructor
    public MyAdapter(String[] mDataSet) {
        this.mDataSet = mDataSet;
    }

    @NonNull
    @Override
    //public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { // Original
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // Create a new view.
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.my_single_column, parent, false);
        return new MyViewHolder(v);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        ((MyViewHolder) holder).getTextView().setText(mDataSet[position]);
    }


    @Override
    public int getItemCount() {
        return mDataSet.length;
    }

    ///////////////////
    /*
     * class MyViewHolder extends from  RecyclerView.ViewHolder {
     */
    private class MyViewHolder extends RecyclerView.ViewHolder {
        private final TextView textView;

        public MyViewHolder(View v) {
            super(v);
            v.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    System.out.println("Element " + getAdapterPosition() + " clicked.");
                }
            });
            textView = (TextView) v.findViewById(R.id.first_column);
        }

        public TextView getTextView() {
            return textView;
        }
    }

    //////////////////


}

RecyclerViewFragment.java

package com.dedetok.recyclerviewtutorial;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

public class RecyclerViewFragment extends Fragment {

    // initial data
    String[] mDataSet  = {"ace", "boom", "crew", "dog", "eon"};
    protected RecyclerView mRecyclerView;
    protected MyAdapter mAdapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.my_recycler_view_layout, container, false); // conteain androidx.recyclerview.widget.RecyclerView

        // get androidx.recyclerview.widget.RecyclerView
        mRecyclerView = rootView.findViewById(R.id.r_view_main_layout);

        // Define Layout for androidx.recyclerview.widget.RecyclerView
        LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
        mRecyclerView.setLayoutManager(mLayoutManager);

        mAdapter = new MyAdapter(mDataSet);
        // Set CustomAdapter as the adapter for RecyclerView.
        mRecyclerView.setAdapter(mAdapter);
        // END_INCLUDE(initializeRecyclerView)


        return rootView;
    }

}

MainActivity.java

package com.dedetok.recyclerviewtutorial;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentTransaction;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

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

        if (savedInstanceState == null) {
            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
            RecyclerViewFragment fragment = new RecyclerViewFragment();
            transaction.replace(R.id.frag_container, fragment);
            transaction.commit();
        }
    }
}


Thursday, November 16, 2023

Android Studio | 2022/2023 Layout 2022-2024

Recommended (modern) Layout

  1. ConstraintLayout
  2. LinearLayout (Horizontal & Vertical)
  3. FrameLayout
  4. TableLayout
  5. TableRow
  6. Space

Legacy Layout:

  1. GridLayout
  2. ListView
  3. TabHost
  4. RelaltiveLayout
  5. GridView

LinearLayout

LinearLayout also supports assigning a weight to individual children with the android:layout_weight attribute. This attribute assigns an "importance" value to a view in terms of how much space it occupies on the screen. A larger weight value lets it expand to fill the remaining space in the parent view. Child views can specify a weight value, and any remaining space in the view group is assigned to children proportionately, based on their declared weight. The default weight is zero.
Equal space

To create a linear layout in which each child uses the same amount of space on the screen, set the android:layout_height of each view to "0dp" for a vertical layout, or the android:layout_width of each view to "0dp" for a horizontal layout. Then set the android:layout_weight of each view to "1".

Unequal space

You can also create linear layouts where the child elements use different amounts of space on the screen. Consider the following examples:

  •     Suppose you have three text fields: two with a weight value of 1, and a third with the default weight value of 0. The third text field, with the weight value of 0, occupies only the area required by its content. The other two text fields, with the weight value of 1, expand equally to fill the space that remains after the contents of all three fields are measured.
  •     If instead you have three text fields where two have a weight value of 1 and the third has a weight of 2, then the space that remains after the contents of all three fields are measured is allocated as follows: half to the field with the weight value of 2, and half divided equally between the fields with the weight value of 1.

Example Linear Layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:orientation="vertical" >
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/to" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/subject" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="top"
        android:hint="@string/message" />
    <Button
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_gravity="end"
        android:text="@string/send" />
</LinearLayout>

Examples ConstraintLayout and LinearLayout (Vertical)

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Name"
        android:ems="10"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

    <EditText
        android:id="@+id/editTextText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="text"
        app:layout_constraintLeft_toRightOf="@id/tv1"
        app:layout_constraintTop_toTopOf="parent"
        android:autofillHints="Fill Name"
        android:hint="Fill Name"
        />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv1" />
    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button1"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Text just"
            />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button Just"
            />
    </androidx.appcompat.widget.LinearLayoutCompat>

</androidx.constraintlayout.widget.ConstraintLayout> 

Note: android studio will warning Hardcoded for String, you may ignore them or put your string on string resource. All the properties are mandatory. if properties does not provided, android studio will give error messages.

Working on: Android Studio Giraffe | 2022.3.1 Patch 3 maybe change in future releases

References: https://developer.android.com/develop/ui/views/layout/linear

Monday, November 6, 2023

repair grub after windows updating bios on lenovo ideaPad gaming 3 AMD 7535h (15”)

Insert Debian installer DVD/CD or USB and change boot order

  1. Keep press shift + restart windows -> troubleshoot -> UEFI Firmware Setting -> Restart
  2. Change boot order to Debian installer media

Boot into Debian

  1. Boot into Debian installer DVD/CD or USB, then press c to enter grub command. If the screen goes blank (for CD boot older then existing grub version), press ESC until you get prompt grub>
  2. Get Debian partition using grub> ls and find Filesystem type ext* or your existing filesystem (see the picture below)
  3. Get Debian grab from Debian partition e.q grub> ls (hd0,gpt7)/boot and find Debian grub folder  (see the picture below) 
  4. Run command to start existing Debian e.q
grub> set root=(hd0,gpt7)
grub> set prefix=(hd0,gpt7)/boot/grub
grub> insmod normal
grub> normal
 
Your Debian will appear in boot option

Repair grub using 

# grub-install
# update-grub

Every time you update your firmware via windows, your grub boot will lost. It's so annoying. Unfortunately, there is not much tool available to automatically check and update bios for Debian. Debian still stable using old bios, but not in windows.