Thursday, February 26, 2026

Android java: using sqliteopenhelper pattern code

Using additional Room may increase size of application, means more lines required to executed. implement SQLiteOpenHelper rather then using Room Library, will give you more controll data for performance.
Please note that: 

  • deleting column require minimum sqlite version 3.35.0
  • every android vendor may use different version of sqlite, it is possible uses lower then sqlite version 3.35.0 

Suppose you have application that has published and install in world wide, with sqlite database 

version 1 

  1. id (auto increment)
  2. name

version 2

  1. id (auto increment)
  2. name
  3. datebirth

version 3

  1. id (auto increment)
  2. name
  3. datebirth
  4. address

The code

public class MySQLHelper extends SQLiteOpenHelper {

    private static final String dbName = "[your_database_file_name].db";
    private static final int DATABASE_VERSION = 3; // MUST HAVE


    /*
     * constructor
     */
    public MySQLHelper(@Nullable Context context) {
        // name String: of the database file, or null for an in-memory database
        super(context, dbName, null, DATABASE_VERSION);
    }

    /*
     * only call once when database not exist 
     */
    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        createDB(sqLiteDatabase);
    }

    /*
     * use latest latest database structure
     */
    private void createDB(SQLiteDatabase sqLiteDatabase) {
        String CREATE_TABLE = "CREATE TABLE user (" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                "name TEXT, " +
                "datebirth TEXT, " +
                "address TEXT)";
        db.execSQL(CREATE_TABLE);
    }

    /*
     * only call when database exist 
     */
    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int curVersion, int newVersion) {
        if (oldVersion < 2) {
            db.execSQL("ALTER TABLE user ADD COLUMN datebirth TEXT");
        }

        if (oldVersion < 3) {
            db.execSQL("ALTER TABLE user ADD COLUMN address TEXT");
        }
    }

    /*
     * only call when database exist
     * delete / drop column only available to sqlite 3.35.0 or higher
     * it will crash run on lower sqlite version
     * NOT RECOMMENDED TO USE DOWNGRADE
     */
    @Override
    public void onDowngrade(SQLiteDatabase sqLiteDatabase, int curVersion, int newVersion) {
        ...
    }

if you wish to drop column on device with sqlite lower then 3.35.0 use rename table, create table, copy values and drop table. Don't use lower DATABASE_VERSION, just increase version and remove unused column using onUpgrade. Here is the example code onUpgrade() to remove column:

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    if (oldVersion < 2) {
        db.execSQL("ALTER TABLE my_table ADD COLUMN datebirth TEXT");
    }
    if (oldVersion < 3) {
        db.execSQL("ALTER TABLE my_table ADD COLUMN address TEXT");
    }
    if (oldVersion < 4) {
        // --- STEP 1: Rename current table ---
        db.execSQL("ALTER TABLE my_table RENAME TO temp_table");

        // --- STEP 2: Create new table without 'address' ---
        db.execSQL("CREATE TABLE my_table ("
                + "id INTEGER PRIMARY KEY,"
                + "name TEXT,"
                + "datebirth TEXT)");

        // --- STEP 3: Copy data (only the columns you want to keep) ---
        db.execSQL("INSERT INTO my_table (id, name, datebirth) " +
                   "SELECT id, name, datebirth FROM temp_table");

        // --- STEP 4: Remove the old table ---
        db.execSQL("DROP TABLE temp_table");
    }

 

Monday, February 23, 2026

Android java: boot on boot receiver

This is a table on all android boot where on receive event trigger your application:

State Security Status getMySharedPreferences Result Download/ Folder Access
1. SIM PIN OS Kernel is paused Receiver won't run. Blocked.
2. Pattern/PIN Direct Boot Mode Success! (With your new code) Fail. (Shared Storage is still encrypted)
3. No Security Full Boot Success! Success!
4. SIM + Pattern Double Lock Receiver won't run. Blocked.    

Base of state of boot above, here is onReceive() skeleton code

public class MyBootReceiver extends BroadcastReceiver {

    // simple state to prevent double running
    private static boolean isRunning = false;

    @Override
    public void onReceive(Context context, Intent intent) {
        // state of boot device
        String action = intent.getAction();
        if (action == null) return;

        switch (action) {
            case Intent.ACTION_LOCKED_BOOT_COMPLETED:
                // STAGE 1: Phone just turned on, PIN screen is visible.
                // DO: Check SharedPreferences (Device Protected) only.
                break;

            case Intent.ACTION_USER_UNLOCKED:
                // STAGE 2: User just entered PIN.
                // DO: Access Contacts and Folders now.
                break;

            case Intent.ACTION_BOOT_COMPLETED:
                // STAGE 3: System is fully initialized.
                if (isRunning) return; // Prevent double execution
        
        isRunning = true;
                // DO: Final cleanup or scheduling.
                break;

            case "android.intent.action.ALARM_MATCHED": // Your custom alarm action
                // DO: The actual backup if triggered by AlarmManager.
                break;
        }

 Permission and service for on boot receiver in android manifest 

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

...
    <application
...
        <receiver
            android:name=".MyBootReceiver"
            android:enabled="true"
            android:exported="true"
            android:directBootAware="true"
            >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
                <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/>
                <action android:name="android.intent.action.USER_UNLOCKED" />
            </intent-filter>
        </receiver>
...

Recommended boot receiver (gemini ai)

public class MyBootReceiver extends BroadcastReceiver {

    // simple state to prevent double running
    private static boolean isRunning = false;

    @Override
    public void onReceive(Context context, Intent intent) {
        // state of boot device
        String action = intent.getAction();
        if (action == null) return;

        switch (action) {
            case Intent.ACTION_LOCKED_BOOT_COMPLETED:
                // Phone is locked. Use Device Protected Storage to schedule the next alarm.
                // Do NOT try to read contacts or write files here.

                break;

            case Intent.ACTION_USER_UNLOCKED:
            case Intent.ACTION_BOOT_COMPLETED:
                // FULL BOOT / UNLOCKED.
                if (isRunning) return; // Prevent double execution
        
        isRunning = true;
                // This is where you run your overdue check logic.
                break;

            case "android.intent.action.ALARM_MATCHED":
                // DO something, it is the time
                break;
        }


    }

 

 

 

Android java: Work Manager and Alarm Manager

Java Android Scheduling: Work Manager and Alarm Manager

WorkManager: 

  • Constraints: You can specify conditions like "only run when charging" or "only on Wi-Fi".
  • Persistence: Tasks survive device reboots and app crashes.
  • Backward Compatibility: It automatically chooses the best underlying API (JobScheduler, AlarmManager, etc.) based on the device's API level.
  • Limitation: It does not guarantee exact timing. The system may delay execution to optimize battery life. 

AlarmManager

  • Precision: Can wake the device from Doze mode to trigger a notification exactly when scheduled.
  • Lifecycle Independent: Operates outside your app's lifecycle once set.
  • It is resource-intensive because it wakes the device.
  • It does not support execution constraints (like network requirements).
  • For long-running tasks triggered by an alarm, Google recommends handing off the work to WorkManager from the alarm's BroadcastReceiver.  
Feature  WorkManager AlarmManager
Timing Deferrable (inexact) Precise (exact)
Guaranteed? Yes, even after reboot Not inherently (must reset on reboot)
Constraints Battery, Network, Storage None (time-only)
Power Efficiency High (optimized by OS) Low (wakes device)
Minimum Interval 15 minutes for periodic work None (can be immediate)
Supported Version Known stable on Android 13+ All

 

 

 

Debian 13: debsecan to check vunerablities

This is MUST know for server admin.

Debin profide debsecan to analyzes the list of installed packages on the current host and reports vulnerabilities found on the system.

It runs locally and downloads vulnerability information over the Internet. It can send mail to interested parties when new vulnerabilities are discovered or when security updates become available. 

Installing debsecan

# apt install debsecan
Installing:                     
  debsecan

Using debsecan for linux-image (kernel)

# debsecan --suite trixie --only-fixed | grep -i linux-image 

If empty, don't have to upgrade linux-image 

e.g for 

CVE-2024-XXXXX linux-image-6.12.57+deb13-amd64 (high urgency)
CVE-2024-YYYYY linux-image-6.12.57+deb13-amd64 (medium urgency)

you MUST upgrade your linux-image, and plan for reboot system for kernel upgrade! 

 to list vulnerable software 

# debsecan --suite trixie --only-fixed --format packages
libavcodec59
libavfilter8
libavformat59
libavutil57
libpoppler126
libpostproc56
libssh-gcrypt-4
libswresample4
libswscale6
libvpx7
linux-headers-6.12.31-amd64
linux-headers-6.12.31-common
linux-headers-6.12.57+deb13-amd64
linux-headers-6.12.57+deb13-common
linux-kbuild-6.12.31
linux-kbuild-6.12.57+deb13

Note: they don't pose a runtime security risk to your system, but they are "out of date." . Consider to upgrade package.

Wednesday, February 18, 2026

Java Netbeans: java with maven vs java with gradle

Comparison

Feature  Java with Maven Java with Gradle
Configuration Uses XML (pom.xml). Uses Groovy or Kotlin DSL (build.gradle).
Philosophy Convention over configuration; strict, linear lifecycle. Flexibility; task-oriented and highly customizable.
Performance Slower; typically lacks advanced incremental build tracking. Faster; uses build caching, daemons, and incremental compilation.
Learning Curve Easy for beginners due to standardized structure. Steeper; requires understanding the DSL and build logic.
IDE Integration Deeply integrated; NetBeans was a pioneer in native Maven support. Fully supported; provides fast project synchronization.
Start with default template Yes No

 

 

When Java with Maven 

  1.     Onboarding & Stability: Because Maven follows strict convention-over-configuration, any Java developer can join a project and immediately know where everything is and how to run it.
  2.     Predictability: Maven’s XML is declarative and "logic-free," making it very reliable for CI/CD pipelines where "zero-surprise" builds are critical.
  3.     Low Maintenance: Maven projects from 10 years ago often still build perfectly today. Gradle’s high-speed evolution means its build scripts can sometimes require updates when you upgrade versions.
  4.     Small Projects: For a single-module project, the speed difference is negligible. In these cases, the extra complexity and learning curve of Gradle may not be worth it. 

When Java with Gradle

  1.     Large, Multi-Module Projects: Gradle is significantly faster (sometimes 100x faster) because it only recompiles what changed and can share build results between different developers via a Build Cache.
  2.     Custom Workflows: If you need to do something non-standard (like custom file manipulations or complex deployment steps), Gradle’s Groovy/Kotlin DSL is much more powerful than writing custom Maven plugins.
  3.     Android Development: Gradle is the official build tool for Android, making it the only real choice for that ecosystem.

This content is created from AI chat. 

Monday, February 16, 2026

Privacy Policy for Wreda Text Helper

 

Privacy Policy


Effective Date: 16 February 2026


Thank you for using Wreda Text Helper.


1. Overview


Wreda Text Helper is designed to assist users with text accessibility features. We respect your privacy and are committed to protecting it.


2. No Data Collection


Wreda Text Helper:


Does NOT collect any personal information


Does NOT collect usage data


Does NOT track users


Does NOT store any personal data



We do not collect names, emails, phone numbers, device identifiers, location data, or any other personal information.


3. No Internet Usage


This application:


Does NOT require an internet connection


Does NOT transmit any data to external servers


Works fully offline



All functionality runs locally on your device.


4. No Advertising


Wreda Text Helper:


Does NOT contain advertisements


Does NOT use AdMob or any third-party advertising services



5. No Data Sharing


Since we do not collect any data, we do not share, sell, rent, or distribute any user information to third parties.


6. Accessibility Service Usage


Wreda Text Helper uses Android Accessibility Service solely to provide its core functionality.


The service is used only to assist users with text-related features and does not collect, store, or transmit any user data.


7. Children’s Privacy


Wreda Text Helper does not knowingly collect any personal information from children or adults. The app is safe for general use.


8. Changes to This Privacy Policy


If this Privacy Policy is updated, changes will be reflected in the updated version of the app listing.


9. Contact


If you have any questions about this Privacy Policy, you may contact:


Developer: dedeetok

Email: dedetoke@gmail.com

 


 

Android java: local webview from local html e.g help page

Create directory and html files

Switch to Project View (not Android view)

  1. Go to: [your_project]
  2. app/src/main/
  3. Right click on main
  4. → New
  5. → Directory
  6. → select or type "assets"
  7. right click "assets" 
  8. → New
  9. → Directory
  10. → type "webhelp"
  11. right click "webhelp" 
  12. → New
  13. → File
  14. → type "htmlhelp.html"
  15. Repeat step 11 for your language e.g. id for Indonesia e.g. "htmlhelp_id.html" 

You can copy paste html code into htmlhelp.html" and "htmlhelp_id.html"

here is layout to show the webview

    ...
    <WebView
        android:id="@+id/my_web_help"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
    ... 

here is code to show the webview

...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
...
        WebView webView = view.findViewById(R.id.my_web_help);

        // Safe defaults
        webView.getSettings().setJavaScriptEnabled(false);
        webView.getSettings().setAllowFileAccess(true);
        webView.getSettings().setDomStorageEnabled(false);

        loadHelpPage(webView);
...
    }

    /*
     * file html helper
     */
    private void loadHelpPage(WebView webView) {

        String lang = Locale.getDefault().getLanguage();
        String fileName;

        if ("id".equals(lang)) {
            fileName = "htmlhelp_id.html";
        } else {
            fileName = "htmlhelp.html";
        }

        webView.loadUrl("file:///android_asset/webhelp/" + fileName);
    }
... 

Optional configuration webview when your application got trouble when submit to application store e.g google play store

WebSettings settings = webView.getSettings();

settings.setJavaScriptEnabled(false);
settings.setDomStorageEnabled(false);
settings.setAllowFileAccess(true);          // needed for assets
settings.setAllowContentAccess(false);
settings.setAllowFileAccessFromFileURLs(false);
settings.setAllowUniversalAccessFromFileURLs(false);
settings.setSupportZoom(false);
settings.setBuiltInZoomControls(false);

  

Friday, February 13, 2026

java: comparison between database api in java android and jdbc in java standard edition

Here is comparison between database api in java android and jdbc in java standard edition  

Operation

Java Android

Java SE (JDBC)

INSERT

Returns new row ID (long)

Returns affected rows (int)

UPDATE

Returns affected rows (int)

Same

DELETE

Returns affected rows (int)

Same                      

In java SE (JDBC) id can be retrive using jse ps.getGeneratedKeys() return long where ps is PreparedStatement.

 

Android java: notification pattern code

add permission in project 

AndroidManifest.xml

<manifest ...>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

MainActivity.java 

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        checkPermissions();
    ....
    }



    /*
     * check permission to read contact and write shared folder Download
     */
    private void checkPermissions() {
        List<String> permissions = new ArrayList<>();

        // 1. Android 13+ Notification Permission
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            permissions.add(Manifest.permission.POST_NOTIFICATIONS);
        }

        // API require string convert List<String> to String[];
        requestPermissionLauncher.launch(permissions.toArray(new String[0]));
    }

    /*
     * callback permission request
     */
    private final ActivityResultLauncher<String[]> requestPermissionLauncher =
            registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), result -> {
                // Handle Notification permission (Android 13+)
                boolean notificationsGranted = true;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                    notificationsGranted = result.getOrDefault(Manifest.permission.POST_NOTIFICATIONS, false);
                }

            }); 

create MyNotificationService.java class

public class MyNotificationService {

    public static final String CHANNEL_ID = "wredatexthelper"; //
    public static final int NOTIF_ID = 1846496091;  // crc from wredatexthelper

    /*
     * to send notification directly
     */
    public static void sendNotification(Context context, String title, String message) {
        NotificationManager notificationManager =
                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        NotificationCompat.Builder myNotifBuilder = getNotificationBuilder(context);
        myNotifBuilder
                .setContentTitle(title)
                .setContentText(message);

        notificationManager.notify(NOTIF_ID, myNotifBuilder.build());
    }

    /*
     * Helper that returns the BUILDER so you can customize it before building
     */
    private static NotificationCompat.Builder getNotificationBuilder(Context context) {
        NotificationManager notificationManager =
                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (notificationManager.getNotificationChannel(CHANNEL_ID) == null) {
                NotificationChannel notifChannel = new NotificationChannel(
                        CHANNEL_ID,
                        "Background Sync Service",
                        NotificationManager.IMPORTANCE_LOW
                );
                notificationManager.createNotificationChannel(notifChannel);
            }
        }

        return new NotificationCompat.Builder(context, CHANNEL_ID)
                .setSmallIcon(android.R.drawable.stat_notify_sync)
                .setPriority(NotificationCompat.PRIORITY_LOW)
                .setCategory(Notification.CATEGORY_SERVICE);
        //.setOngoing(true); // can not swap away
    }

    /*
     * get notification for foreground called from worker
     */
    public static Notification getForegroundNotification(Context context) {
        Notification myNotif = getNotificationBuilder(context).build();
        return myNotif;
    }
}

 

 

Thursday, February 12, 2026

Android java: fragment pattern code

Fragment

use just portrait for phone and w600 for tablet (bigger screen)   

for portrait (phone) 

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat
    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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical"
    >
    
    <!-- Top TextView -->
    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
        <androidx.appcompat.widget.AppCompatTextView
            android:id="@+id/top_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/app_name"
            android:textSize="18sp"
            android:textStyle="bold"
            android:padding="12dp"
            android:gravity="start|center_vertical"
            android:background="#DDDDDD"
            android:layout_weight="1"
            />

        <androidx.appcompat.widget.AppCompatImageButton
            android:id="@+id/button_help"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:background="?android:attr/selectableItemBackground"
            android:src="@android:drawable/ic_menu_help" />
    </androidx.appcompat.widget.LinearLayoutCompat>
    
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/my_fragment_container_main"
        android:name="[Your java main fragment with full package name]"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        />

</androidx.appcompat.widget.LinearLayoutCompat>

for w600 tablet

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

    <!-- Top TextView -->
    <TextView
        android:id="@+id/top_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Your Title Here"
        android:textSize="18sp"
        android:textStyle="bold"
        android:padding="12dp"
        android:gravity="center"
        android:background="#DDDDDD" />

    <!-- Horizontal Content -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="horizontal"
        android:weightSum="3">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/my_fragment_container_left"
        android:name="[Your java left fragment with full package name]"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <View
        android:id="@+id/divider_line"
        android:layout_width="1dp"
        android:layout_height="match_parent"
        android:background="#000000" />

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/my_fragment_container_main"
        android:name="[Your java main fragment with full package name]"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="2" />

    </LinearLayout>

</LinearLayout> 

Guideline for layout LinearLayout

  • orientation android:orientation="vertical" 
    android:layout_width="match_parent" 
    android:layout_height="0dp" 
    android:layout_weight="1"
  • orientation android:orientation="horizontal"
    android:weightSum="3" 
    android:layout_width="0dp" 
    android:layout_height="match_parent" android:layout_weight="1"  

in main activity on onCreate(Bundle savedInstanceState)

    public boolean isDualPane = false; // default is portrait
    
AppCompatImageButton buttonHelp;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        isDualPane = findViewById(R.id.my_fragment_container_left) != null;

        // R.id.main id for root portrait & w600

        buttonHelp = findViewById(R.id.button_help);
        buttonHelp.setOnClickListener(buttonHelpListener);        

        // Load Fragments only if this is a fresh start (savedInstanceState == null)
        // This prevents overlapping fragments during screen rotation
        if (savedInstanceState == null) {

            // If system loaded the w600 layout, load both Fragment
            if (isDualPane) {
                replaceFragment(
                        R.id.my_fragment_container_left,
                        new FragmentLeft(),
                        null);
                replaceFragment(
                        R.id.my_fragment_container_main,
                        new FragmentMain(),
                        null);
            } else {
                // default load main container
                replaceFragment(
                        R.id.my_fragment_container_main,
                        new FragmentMain(),
                        null); // when navigating add "Fragment Main"
            }
        }

        View.OnClickListener buttonHelpListener = new View.OnClickListener() {
   
         @Override
       
     public void onClick(View view) {
           
     Fragment currentFragment =
               
         getSupportFragmentManager()
                   
             .findFragmentById(R.id.my_fragment_container_main);
                if (currentFragment instanceof FragmentMain) {
                    // NOTE: we still use R.id.my_fragment_container_main in portrait
  
                  replaceFragment(
                            R.id.my_fragment_container_main,
                            new FragmentLeft(),
                            "Main Menu");
                    buttonHelp.setImageResource(android.R.drawable.ic_menu_revert);
                } else {
                    getSupportFragmentManager().popBackStack();
                    buttonHelp.setImageResource(android.R.drawable.ic_menu_help);
                }
            }
        };

        /*
         * fragment helper
         */
        private void replaceFragment(
                         int containerId,
                         Fragment fragment,
                         String addToBackStackString) {
            FragmentTransaction transaction =
                    getSupportFragmentManager().beginTransaction();
            transaction.replace(containerId, fragment);
            if (addToBackStackString!=null) {
                transaction.addToBackStack(addToBackStackString);
            }
            transaction.commit();
        }
        ....
To implement onbackpresseddispatcher pattern code, see this  https://dedetoknotes.blogspot.com/2026/01/android-java-onbackpresseddispatcher.html

 

Saturday, February 7, 2026

Debian 13: removing kernel left over after kernel upgrade

wifi and some device require compile in debian depend on kernel version. this documentation is intended as a small guide to tidy up your system.

list of ii and rc status of package

# uname -r
6.12.63+deb13-amd64
# dpkg -l | grep linux-image
rc  linux-image-6.1.0-33-amd64                         6.1.133-1                            amd64        Linux 6.1 for 64-bit PCs (signed)
rc  linux-image-6.1.0-34-amd64                         6.1.135-1                            amd64        Linux 6.1 for 64-bit PCs (signed)
rc  linux-image-6.1.0-37-amd64                         6.1.140-1                            amd64        Linux 6.1 for 64-bit PCs (signed)
rc  linux-image-6.1.0-9-amd64                          6.1.27-1                             amd64        Linux 6.1 for 64-bit PCs (signed)
rc  linux-image-6.12.38+deb13-amd64                    6.12.38-1                            amd64        Linux 6.12 for 64-bit PCs (signed)
rc  linux-image-6.12.38+deb13-amd64-unsigned           6.12.38-1                            amd64        Linux 6.12 for 64-bit PCs
rc  linux-image-6.12.41+deb13-amd64                    6.12.41-1                            amd64        Linux 6.12 for 64-bit PCs (signed)
rc  linux-image-6.12.41+deb13-amd64-unsigned           6.12.41-1                            amd64        Linux 6.12 for 64-bit PCs
rc  linux-image-6.12.43+deb13-amd64                    6.12.43-1                            amd64        Linux 6.12 for 64-bit PCs (signed)
rc  linux-image-6.12.43+deb13-amd64-unsigned           6.12.43-1                            amd64        Linux 6.12 for 64-bit PCs
rc  linux-image-6.12.48+deb13-amd64                    6.12.48-1                            amd64        Linux 6.12 for 64-bit PCs (signed)
rc  linux-image-6.12.48+deb13-amd64-unsigned           6.12.48-1                            amd64        Linux 6.12 for 64-bit PCs
rc  linux-image-6.12.57+deb13-amd64                    6.12.57-1                            amd64        Linux 6.12 for 64-bit PCs (signed)
ii  linux-image-6.12.57+deb13-amd64-unsigned           6.12.57-1                            amd64        Linux 6.12 for 64-bit PCs
ii  linux-image-6.12.63+deb13-amd64                    6.12.63-1                            amd64        Linux 6.12 for 64-bit PCs (signed)
ii  linux-image-amd64                                  6.12.63-1                            amd64        Linux for 64-bit PCs (meta-package)

Note about rc and ii 

  • rc = package removed, but config files remain
  • ii = desired state install and current state installed 

we can conclude these packages are not used anymore but still exist in our system:

  1. rc linux-image-6.1.0-*
  2. rc linux-image-6.12.38*
  3. rc linux-image-6.12.41*
  4. rc linux-image-6.12.43*
  5. rc linux-image-6.12.48*

We can remove those packages which is kernel left over, using this command:

# dpkg -l | awk '/^rc  linux-image/ {print $2}' | xargs dpkg --purge
(Reading database ... 180811 files and directories currently installed.)
Purging configuration files for linux-image-6.1.0-33-amd64 (6.1.133-1) ...
Purging configuration files for linux-image-6.1.0-34-amd64 (6.1.135-1) ...
Purging configuration files for linux-image-6.1.0-37-amd64 (6.1.140-1) ...
Purging configuration files for linux-image-6.1.0-9-amd64 (6.1.27-1) ...
dpkg: warning: while removing linux-image-6.1.0-9-amd64, directory '/lib/modules' not empty so not removed
Purging configuration files for linux-image-6.12.38+deb13-amd64 (6.12.38-1) ...
Purging configuration files for linux-image-6.12.38+deb13-amd64-unsigned (6.12.38-1) ...
rmdir: failed to remove '/lib/modules/6.12.38+deb13-amd64': No such file or directory
Purging configuration files for linux-image-6.12.41+deb13-amd64 (6.12.41-1) ...
Purging configuration files for linux-image-6.12.41+deb13-amd64-unsigned (6.12.41-1) ...
rmdir: failed to remove '/lib/modules/6.12.41+deb13-amd64': No such file or directory
Purging configuration files for linux-image-6.12.43+deb13-amd64 (6.12.43-1) ...
Purging configuration files for linux-image-6.12.43+deb13-amd64-unsigned (6.12.43-1) ...
rmdir: failed to remove '/lib/modules/6.12.43+deb13-amd64': No such file or directory
Purging configuration files for linux-image-6.12.48+deb13-amd64 (6.12.48-1) ...
Purging configuration files for linux-image-6.12.48+deb13-amd64-unsigned (6.12.48-1) ...
rmdir: failed to remove '/lib/modules/6.12.48+deb13-amd64': No such file or directory
Purging configuration files for linux-image-6.12.57+deb13-amd64 (6.12.57-1) ...
I: /vmlinuz is now a symlink to boot/vmlinuz-6.12.63+deb13-amd64
I: /initrd.img is now a symlink to boot/initrd.img-6.12.63+deb13-amd64
rmdir: failed to remove '/lib/modules/6.12.57+deb13-amd64': Directory not empty