Wednesday, January 28, 2026

CodeIgniter: Tutorial restapi by chatgpt

Table name :tmp_harga_jual

    id Primary   int(11) 
    nama             varchar(100) 
    satuan           varchar(20) 
    harga             decimal(10,2)     
    kode_modal varchar(20)

1. create Model app/Models/TmpHargaJualModel.php

<?php

namespace App\Models;

use CodeIgniter\Model;

class TmpHargaJualModel extends Model
{
    protected $table      = 'tmp_harga_jual';
    protected $primaryKey = 'id';

    protected $allowedFields = [
        'nama',
        'satuan',
        'harga',
        'kode_modal'
    ];

    protected $returnType = 'array';
}

2. create Controller app/Controllers/TmpHargaJual.php

<?php

namespace App\Controllers;

use App\Models\TmpHargaJualModel;
use CodeIgniter\RESTful\ResourceController;

class TmpHargaJual extends ResourceController
{
    protected $modelName = TmpHargaJualModel::class;
    protected $format    = 'json';

    public function index()
    {
        $data = $this->model->findAll();

        return $this->respond([
            'status' => 'success',
            'data'   => $data
        ]);
    }
}

3. Add route app/Config/Routes.php

<?php

use CodeIgniter\Router\RouteCollection;

/**
 * @var RouteCollection $routes
 */
$routes->get('/', 'Home::index');

$routes->get('hello', 'Hello::index');

$routes->get('harga-jual', 'TmpHargaJual::index');

Add search

modify app/Controllers/TmpHargaJual.php

<?php

namespace App\Controllers;

use App\Models\TmpHargaJualModel;
use CodeIgniter\RESTful\ResourceController;

class TmpHargaJual extends ResourceController
{
    protected $modelName = TmpHargaJualModel::class;
    protected $format    = 'json';

    public function index()
    {
        $nama = $this->request->getGet('nama');

        if ($nama) {
            $data = $this->model
                ->like('nama', $nama)
                ->findAll();
        } else {
            $data = $this->model->findAll();
        }

        return $this->respond([
            'status' => 'success',
            'count'  => count($data),
            'data'   => $data
        ]);
    }
}

Test using curl 

$ curl "http://localhost/myapp/harga-jual?nama=kopi

 

Common functions:

| HTTP   | URL             | Meaning  |
| ------ | --------------- | -------- |
| GET    | `/harga-jual`   | List all |
| GET    | `/harga-jual/1` | Get one  |
| POST   | `/harga-jual`   | Create   |
| PUT    | `/harga-jual/1` | Update   |
| DELETE | `/harga-jual/1` | Delete   |

 

 

Friday, January 23, 2026

Android java: creating icon launcher and icon for Play Store

Prepare your file, it preferred to use transparent background. I use png for image source. 

Open your Android Studio, follow these steps: 

Step 1 open Resource Manager: 

Tool -> Resource Manager

Press "+" -> Image Asset it will open Configure Image Asset

Fill

  • Icon type: Launcer icons (Adaptive and Legacy)
  • Name: my_ic_launcher 

Step 2 Set foreground with your png file:

Tab: Foreground Layer

  • Source Asset
  • Asset type: Image
    Path: /Workspace/[your_project]/iconLauncher.png
  • Scalling
    Trim: Yes for automatic scalling
    If Trim No, you can adjust scaling by scrolling Resize until it doesn't get cuted.

Step 3 set background: 

Tab: Background Layer
Asset type:
Color: directing choose color (recommended)
Image: xml file to edit or put image for background

Step 4: Finish

Attach your icon into your application, open your AndroidManifest.xml

...
    <application
...
        android:roundIcon="@mipmap/my_ic_launcher"
...

Icon for playstore can found at Workspace/[your_project]/app/src/main/my_ic_launcher-playstore.png 

Android java: gradle corrupted, how to fix it

This is error message when gradle corrupted due to accidentally deleted or fail updated.

"The contents of the immutable workspace '~/.gradle/caches/9.1.0/groovy-dsl/52b1078aa736f3877f522d2046a7f837' have been modified" 

To make easier to fix gradle, exit Android Studio, open shell and go to project root directory. You will find file gradlew.

Clean up your gradle

$ ./gradlew --stop
Stopping Daemon(s)
2 Daemons stopped
$ rm -rf ~/.gradle/caches/9.1.0/

Run help to force gradlew download and rebuild library required.

$ ./gradlew help
Starting a Gradle Daemon, 2 stopped Daemons could not be reused, use --status for details

> Configure project :app
WARNING: The option setting 'android.usesSdkInManifest.disallowed=false' is deprecated.
The current default is 'true'.
It will be removed in version 10.0 of the Android Gradle plugin.
WARNING: The option setting 'android.sdk.defaultTargetSdkToCompileSdkIfUnset=false' is deprecated.
The current default is 'true'.
It will be removed in version 10.0 of the Android Gradle plugin.
WARNING: The option setting 'android.enableAppCompileTimeRClass=false' is deprecated.
The current default is 'true'.
It will be removed in version 10.0 of the Android Gradle plugin.
WARNING: The option setting 'android.builtInKotlin=false' is deprecated.
The current default is 'true'.
It will be removed in version 10.0 of the Android Gradle plugin.
WARNING: The option setting 'android.newDsl=false' is deprecated.
The current default is 'true'.
It will be removed in version 10.0 of the Android Gradle plugin.
WARNING: The option setting 'android.r8.optimizedResourceShrinking=false' is deprecated.
The current default is 'true'.
It will be removed in version 10.0 of the Android Gradle plugin.
WARNING: The option setting 'android.defaults.buildfeatures.resvalues=true' is deprecated.
The current default is 'false'.
It will be removed in version 10.0 of the Android Gradle plugin.
w: file://~/Workspace/Android/WredaContactBackup/app/build.gradle.kts:5:1: 'fun Project.android(configure: Action<BaseAppModuleExtension>): Unit' is deprecated. Replaced by com.android.build.api.dsl.ApplicationExtension.
This class is not used for the public extensions in AGP when android.newDsl=true, which is the default in AGP 9.0, and will be removed in AGP 10.0.
WARNING: The property android.dependency.excludeLibraryComponentsFromConstraints improves project import performance for very large projects. It should be enabled to improve performance.
To suppress this warning, add android.generateSyncIssueWhenLibraryConstraintsAreEnabled=false to gradle.properties
WARNING: The property android.dependency.excludeLibraryComponentsFromConstraints improves project import performance for very large projects. It should be enabled to improve performance.
To suppress this warning, add android.generateSyncIssueWhenLibraryConstraintsAreEnabled=false to gradle.properties
WARNING: The property android.dependency.excludeLibraryComponentsFromConstraints improves project import performance for very large projects. It should be enabled to improve performance.
To suppress this warning, add android.generateSyncIssueWhenLibraryConstraintsAreEnabled=false to gradle.properties
WARNING: The property android.dependency.excludeLibraryComponentsFromConstraints improves project import performance for very large projects. It should be enabled to improve performance.
To suppress this warning, add android.generateSyncIssueWhenLibraryConstraintsAreEnabled=false to gradle.properties

> Task :help

Welcome to Gradle 9.1.0.

To run a build, run gradlew <task> ...

To see a list of available tasks, run gradlew tasks

To see more detail about a task, run gradlew help --task <task>

To see a list of command-line options, run gradlew --help

For more detail on using Gradle, see https://docs.gradle.org/9.1.0/userguide/command_line_interface.html

For troubleshooting, visit https://help.gradle.org

BUILD SUCCESSFUL in 28s
1 actionable task: 1 executed
Consider enabling configuration cache to speed up this build: https://docs.gradle.org/9.1.0/userguide/configuration_cache_enabling.html

Open your Android Studio, repair android studio project choose index repair.

If this error occurred

"Ambiguous method call: both 'FragmentActivity.onCreate(Bundle) (In ~/.gradle/caches/9.1.0/transforms/dae60150ff0476652c6b13819aa96f10/transformed/fragment-1.5.4/jars/classes.jar!/androidx/fragment/app/FragmentActivity.class)' and 'ComponentActivity.onCreate(Bundle) (In ~/.gradle/caches/9.1.0/transforms/48c7a661e0aa50fff79a603bd45cc34c/transformed/activity-1.12.2/jars/classes.jar!/androidx/activity/ComponentActivity.class)' match"

Invalidate cache (everything) and restart.

If you find some deprecated gradle option like these:

$ ./gradlew help
Starting a Gradle Daemon, 1 incompatible and 1 stopped Daemons could not be reused, use --status for details

> Configure project :app
WARNING: The option setting 'android.usesSdkInManifest.disallowed=false' is deprecated.
The current default is 'true'.
It will be removed in version 10.0 of the Android Gradle plugin.
WARNING: The option setting 'android.sdk.defaultTargetSdkToCompileSdkIfUnset=false' is deprecated.
The current default is 'true'.
It will be removed in version 10.0 of the Android Gradle plugin.
WARNING: The option setting 'android.enableAppCompileTimeRClass=false' is deprecated.
The current default is 'true'.
It will be removed in version 10.0 of the Android Gradle plugin.
WARNING: The option setting 'android.builtInKotlin=false' is deprecated.
The current default is 'true'.
It will be removed in version 10.0 of the Android Gradle plugin.
WARNING: The option setting 'android.newDsl=false' is deprecated.
The current default is 'true'.
It will be removed in version 10.0 of the Android Gradle plugin.
WARNING: The option setting 'android.r8.optimizedResourceShrinking=false' is deprecated.
The current default is 'true'.
It will be removed in version 10.0 of the Android Gradle plugin.
WARNING: The option setting 'android.defaults.buildfeatures.resvalues=true' is deprecated.
The current default is 'false'.
It will be removed in version 10.0 of the Android Gradle plugin.
WARNING: The property android.dependency.excludeLibraryComponentsFromConstraints improves project import performance for very large projects. It should be enabled to improve performance.
To suppress this warning, add android.generateSyncIssueWhenLibraryConstraintsAreEnabled=false to gradle.properties
WARNING: The property android.dependency.excludeLibraryComponentsFromConstraints improves project import performance for very large projects. It should be enabled to improve performance.
To suppress this warning, add android.generateSyncIssueWhenLibraryConstraintsAreEnabled=false to gradle.properties
WARNING: The property android.dependency.excludeLibraryComponentsFromConstraints improves project import performance for very large projects. It should be enabled to improve performance.
To suppress this warning, add android.generateSyncIssueWhenLibraryConstraintsAreEnabled=false to gradle.properties
WARNING: The property android.dependency.excludeLibraryComponentsFromConstraints improves project import performance for very large projects. It should be enabled to improve performance.
To suppress this warning, add android.generateSyncIssueWhenLibraryConstraintsAreEnabled=false to gradle.properties

> Task :help

Welcome to Gradle 9.1.0.

To run a build, run gradlew <task> ...

To see a list of available tasks, run gradlew tasks

To see more detail about a task, run gradlew help --task <task>

To see a list of command-line options, run gradlew --help

For more detail on using Gradle, see https://docs.gradle.org/9.1.0/userguide/command_line_interface.html

For troubleshooting, visit https://help.gradle.org

BUILD SUCCESSFUL in 5s
1 actionable task: 1 executed
Consider enabling configuration cache to speed up this build: https://docs.gradle.org/9.1.0/userguide/configuration_cache_enabling.html

Edit file gradle.properties, find those configuration and take some adjustment. You can close Android Studio and back to shell, this is my gradle.properties:

# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. For more details, visit
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
#android.defaults.buildfeatures.resvalues=true #deprecated
#android.sdk.defaultTargetSdkToCompileSdkIfUnset=false #deprecated
#android.enableAppCompileTimeRClass=false #deprecated
#android.usesSdkInManifest.disallowed=false #deprecated
android.uniquePackageNames=false
android.dependency.useConstraints=true
android.r8.strictFullModeForKeepRules=false
#android.r8.optimizedResourceShrinking=false #deprecated
#android.builtInKotlin=false #deprecated
#android.newDsl=false #deprecated
# edited dedetok
#android.enableJetifier=true
android.generateSyncIssueWhenLibraryConstraintsAreEnabled=false

Validate gradle is clear

$ ./gradlew help

> Task :help

Welcome to Gradle 9.1.0.

To run a build, run gradlew <task> ...

To see a list of available tasks, run gradlew tasks

To see more detail about a task, run gradlew help --task <task>

To see a list of command-line options, run gradlew --help

For more detail on using Gradle, see https://docs.gradle.org/9.1.0/userguide/command_line_interface.html

For troubleshooting, visit https://help.gradle.org

BUILD SUCCESSFUL in 503ms
1 actionable task: 1 executed
Consider enabling configuration cache to speed up this build: https://docs.gradle.org/9.1.0/userguide/configuration_cache_enabling.html

Now you have clean project.  

Saturday, January 17, 2026

adb command references (android debug bridge)

to add path for platform-tool edit .bashrc (for Debian), append at the end. log out and sign in again to get affect or run $ source ~/.bashrc

export PATH=$PATH:$HOME/AndroidStudio/Sdk/platform-tools/

to show devices attached 

$ adb devices
* daemon not running; starting now at tcp:5037
adb * daemon started successfully
List of devices attached
A6501D9863TH039779    unauthorized -> Not allow, please allow on your device
$ adb devices
List of devices attached
A6501D9863TH039779    device -> your device allow to debug

to install application (don't support aab, compile to apk) 

$ adb install example.apk
Performing Streamed Install
Success

to uninstall package 

$ adb uninstall com.dedetok.radiowalkman
Success

to uninstall package (bloatware) for user 0 (default user). Use adb shell pm instead directly adb uninstall. Note: it just remove for current default user, it does not remove from firmware, you need to root your device for fully removal. After reset your device, the bloatware will come again.

$ adb uninstall --user 0 com.example.app vs 
$ adb shell uninstall --user 0 com.example.app
$ adb shell pm uninstall --user 0 net.bat.store
Success
com.transsion.aivoiceassistant

to list packages 

$ adb shell pm list packages | grep dedetok
package:com.dedetok.turuntirta
package:com.dedetok.pitrapuja

to list directory Download 

$ adb shell ls /sdcard/Download/com.dedetok.wredacontactbackup
test.txt

to get error Logcat 

$ adb logcat | grep com.dedetok.wredacontactbackup

  • WorkManager
    $ adb logcat -s dedetok WorkManager WM-WorkerWrapper
    --------- beginning of system
    --------- beginning of crash
    --------- beginning of main
    01-20 14:51:59.303 25534 25561 E WM-WorkerWrapper: Didn't find WorkSpec for id 6548d3d5-2c2e-47a0-a795-ff4f9365b2be
    01-20 14:51:59.311 25534 25561 E WM-WorkerWrapper: Didn't find WorkSpec for id e42ac4be-f95e-41e8-9694-e4eb6bc40671
    01-20 14:51:59.319 25534 25561 E WM-WorkerWrapper: Didn't find WorkSpec for id e42ac4be-f95e-41e8-9694-e4eb6bc40671

to get error

$ adb shell dumpsys dropbox | grep com.dedetok.wredacontactbackup

to clear data include cache for package com.dedetok.wredacontactbackup

$ adb shell pm clear com.dedetok.wredacontactbackup

Check JobScheduler (where WorkManager stores jobs)

$ adb shell dumpsys jobscheduler | grep -A 30 "com.dedetok.wredacontactbackup"
  JOB #u0a124/1: 7b2babf com.dedetok.wredacontactbackup/androidx.work.impl.background.systemjob.SystemJobService
    u0a124 tag=*job*/com.dedetok.wredacontactbackup/androidx.work.impl.background.systemjob.SystemJobService

#u0a124/1 last digit /1 is job id  

Force scheduler to run, use job id

$ adb shell cmd jobscheduler run -f com.dedetok.wredacontactbackup 1
Running job [FORCED] 

 

 

 

 

Android java: dealing with Trusted credentials on Android 7.1.1 below for letencrypt

Dealing with connection to https on old android may depend on Trusted credentials on the the device. 

Letsencrypt root certificate does not installed on Trusted credentials prior android 7.1.1.

Some application may throw:

java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

Solution 1 add user Trusted credentials 

Install Letsencrypt pem manually on old phone i.e android version 7.1.1 or older. You can download from https://letsencrypt.org/certs/isrgrootx1.pem?hl=en-US .

On your device (mine is evercoss gen pro x pro android 7.0). Go to setting -> Security.

You may find:

  • Trusted Credentials 
  • User Credentials
  • Install from SD Card

Choose "Install from SD Card":

  • Filed Name of Certification e.g ISGR ROOT X1 or Letsecrypt.
  • Credentials Use select VPN and aps

Open your Android Studio project and create res/xml/network_security_config.xml.

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </base-config>
</network-security-config>

Edit AndroidManifest.xml

...
   <application
    ... 
   android:networkSecurityConfig="@xml/network_security_config"
   ...
   >
...

If https server ever falls back to http (not https), you should add cleartextTrafficPermitted="true" to the <base-config> tag (Not Recommended except your application need to access http):

...
<base-config 
   ...
   cleartextTrafficPermitted="true"
   ...
>
...

if this XML fix doesn't work, it's not the certificate—it's the Android 7.0 Cipher bug. In that specific case, you will have to use Conscrypt library (Solution 2).

Solution 2 using Conscript Library

Add dependency into gradle.build app

dependencies {
    ...
    implementation 'org.conscrypt:conscrypt-android:2.5.2'
    ...
}

Initialize at application startup or foreground

...
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) { 
    // Below Android 7.1.1
    Security.insertProviderAt(Conscrypt.newProvider(), 1);
}
...


Tuesday, January 13, 2026

Android java: input events processing (collection)

There 2 ways to handle input events from user input

  1. Immediate: using listener
  2. At process: at the end to process data, usually user fire "process" button 

TimePicker

1. Immediate 

...
        //on view created
        // date picker
        TimePicker myTimePicker = view.findViewById(R.id.my_time_picker);
        myTimePicker.setOnTimeChangedListener(timeChangeListener);
...
    // TimePicker Listeneer
    private final TimePicker.OnTimeChangedListener timeChangeListener =
            new TimePicker.OnTimeChangedListener() {
        @Override
        public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
            // TODO
        }
    };

2.  At process

    // Pull the values exactly as they are right now
    int hour = timePicker.getHour();
    int minute = timePicker.getMinute();

RadioGroup

1. Immediate

...
        //on view created
        // date picker
        RadioGroup radioSelectModeBackup = view.findViewById(R.id.radio_select_mode_backup);
        radioSelectModeBackup.setOnCheckedChangeListener(radioListener);
...
    // RadioGroup Listener
    RadioGroup.OnCheckedChangeListener radioListener = new RadioGroup.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(@NonNull RadioGroup radioGroup, int idSelected) {
            if (idSelected==R.id.radio_weekly) {
                // weekly
            } else if (idSelected==R.id.radio_moonthly) {
                // monthly
            }
        }
    };

2. At process 

    // Inside your "Save" or "Submit" button click listener
    int selectedId = radioSelectModeBackup.getCheckedRadioButtonId();

    if (selectedId == R.id.radio_weekly) {
        // Logic for weekly backup
    } else if (selectedId == R.id.radio_moonthly) {
        // Logic for monthly backup
    } else {
        // Nothing is selected (returns -1 if no default is set in XML)
    }

 

 

 

 

Monday, January 12, 2026

Debian 13: using systemd timesync to update ntp client time

Since Debain Jessie, Debian use systemd to synchronize ntp clinet

To list timezone:

# timedatectl list-timezones | grep Jakarta
Asia/Jakarta

To set timezone:

# timedatectl set-timezone Asia/Jakarta

To show

# timedatectl 
               Local time: Mon 2026-01-12 12:44:27 WIB
           Universal time: Mon 2026-01-12 05:44:27 UTC
                 RTC time: Mon 2026-01-12 05:44:27
                Time zone: Asia/Jakarta (WIB, +0700)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

Edit /etc/systemd/timesyncd.conf

...
[Time]
#NTP=
NTP=0.id.pool.ntp.org 1.id.pool.ntp.org 2.id.pool.ntp.org 2.id.pool.ntp.org 3.id.pool.ntp.org
#FallbackNTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org
...

To restart service

# systemctl restart systemd-timesyncd

you can use old way using ntp client, but you need to remove this package

# timedatectl set-ntp false

Now you can install ntp and edit configuration /etc/ntp.conf.

 

 

Wednesday, January 7, 2026

Java: using build tool gradle wrapper to setup project and run

 

  • java application openjdk 17
  • gradle 8.7

install gradle 

# apt-get install gradle 

create folder project example

$ mkdir example
$ cd example

create init project

wrap gradle version for your project 

$ gradle wrapper --gradle-version 8.7
openjdk version "17.0.16" 2025-07-15
OpenJDK Runtime Environment (build 17.0.16+8-Debian-1deb12u1)
OpenJDK 64-Bit Server VM (build 17.0.16+8-Debian-1deb12u1, mixed mode, sharing)

init using gradle 8.7  debian 13 uses old 4.4 

$ ./gradlew init \
  --type java-application \
  --dsl groovy \
  --test-framework junit-jupiter \
  --package com.dedetok \
  --project-name example
openjdk version "17.0.16" 2025-07-15
OpenJDK Runtime Environment (build 17.0.16+8-Debian-1deb12u1)
OpenJDK 64-Bit Server VM (build 17.0.16+8-Debian-1deb12u1, mixed mode, sharing)
Downloading https://services.gradle.org/distributions/gradle-8.7-bin.zip
...............................................................................................................................
Unzipping ~/.gradle/wrapper/dists/gradle-8.7-bin/bhs2wmbdwecv87pi65oeuq5iu/gradle-8.7-bin.zip to ~/.gradle/wrapper/dists/gradle-8.7-bin/bhs2wmbdwecv87pi65oeuq5iu
Set executable permissions for: ~/.gradle/wrapper/dists/gradle-8.7-bin/bhs2wmbdwecv87pi65oeuq5iu/gradle-8.7/bin/gradle

Welcome to Gradle 8.7!

Here are the highlights of this release:
 - Compiling and testing with Java 22
 - Cacheable Groovy script compilation
 - New methods in lazy collection properties

For more details see https://docs.gradle.org/8.7/release-notes.html

Starting a Gradle Daemon, 1 incompatible Daemon could not be reused, use --status for details

Enter target Java version (min: 7, default: 21): 17

Select application structure:
  1: Single application project
  2: Application and library project
Enter selection (default: Single application project) [1..2] 1

Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no] no


> Task :init
To learn more about Gradle by exploring our Samples at https://docs.gradle.org/8.7/samples/sample_building_java_applications_multi_project.html

BUILD SUCCESSFUL in 5m 12s
1 actionable task: 1 executed

./settings.gradle

plugins {
    // Apply the foojay-resolver plugin to allow automatic download of JDKs
    id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
}

rootProject.name = 'example'
include('app')

./app/build.gradle

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
}

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation libs.junit.jupiter

    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

    // This dependency is used by the application.
    implementation libs.guava
}

// Apply a specific Java toolchain to ease working on different environments.
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

application {
    // Define the main class for the application.
    mainClass = 'com.dedetok.App'
}

tasks.named('test') {
    // Use JUnit Platform for unit tests.
    useJUnitPlatform()
}

./gradle/libs.versions.toml

[versions]
guava = "32.1.3-jre"
junit-jupiter = "5.10.1"

[libraries]
guava = { module = "com.google.guava:guava", version.ref = "guava" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }

To show current gradle version

$ ./gradlew --version

------------------------------------------------------------
Gradle 8.7
------------------------------------------------------------

Build time:   2024-03-22 15:52:46 UTC
Revision:     650af14d7653aa949fce5e886e685efc9cf97c10

Kotlin:       1.9.22
Groovy:       3.0.17
Ant:          Apache Ant(TM) version 1.10.13 compiled on January 4 2023
JVM:          17.0.16 (Debian 17.0.16+8-Debian-1deb12u1)
OS:           Linux 6.12.57+deb13-amd64 amd64

Test run

$ ./gradlew run
Path for java installation '/usr/lib/jvm/openjdk-17' (Common Linux Locations) does not contain a java executable

> Task :app:run
Hello World!

BUILD SUCCESSFUL in 473ms
2 actionable tasks: 1 executed, 1 up-to-date

or you can run using style $ ./gradlew :app:run

Now add maridb jcoonect into project, using Traditional Groovy Approach. edit ./app/build.gradle

...
dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation libs.junit.jupiter

    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

    // This dependency is used by the application.
    implementation libs.guava

    // mariadb jconnector (single file no need change libs.versions.toml
    implementation 'org.mariadb.jdbc:mariadb-java-client:3.3.3'

    // using libs.versions.toml must editing this file 
    //implementation libs.mariadb.client

}
...

If you wish to useing Version Catalog Approach, use 2nd option for ./app/build.gradle and edit libs.version.toml

[versions]
guava = "32.1.3-jre"
junit-jupiter = "5.10.1"
mariadb = "3.3.3"

[libraries]
guava = { module = "com.google.guava:guava", version.ref = "guava" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }
mariadb-client = { group = "org.mariadb.jdbc", name = "mariadb-java-client", version.ref = "mariadb" }

use Version Catalog Approach for complex project and involving multiple developers.

Edit ./app/src/main/java/com/dedetok/App.java

/*
 * This source file was generated by the Gradle 'init' task
 */
package com.dedetok;

import java.sql.*;

public class App {
    public String getGreeting() {
        return "Hello World!";
    }

    public static void main(String[] args) throws SQLException {
        System.out.println(new App().getGreeting());
        
        String dbUrl = "jdbc:mariadb://localhost:3306/mydatabase";
        String dbUName = "myuname";
        String dbPass = "mypass";
        
        Connection conn = DriverManager.getConnection(dbUrl, dbUName, dbPass);

    }
}

To generate single file jar for your project, edit  ./app/build.gradle

...
jar {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE

    // change with yours
    manifest {
        attributes "Main-Class": "com.dedetok.App"
    }

    from {
        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
}
...

You can run directly using java command

$ ./gradlew build
...
$ java -jar ./app/build/libs/app.jar
Hello World!

Your jar file location is ./app/build/libs/

Now you can edit  ./app/src/main/java/com/dedetok/App.java finish your project. Use any text editor if you wish.

Tuesday, January 6, 2026

Debian 13: bash to clean up unused gradle in folder $HOME/.gradle

stop any gradle daemon (better run this after you start your Debian)

copy paste this bash and change permission to execute

#!/bin/bash

# Set the cutoff date yyyy-mm-dd
CUTOFF_DATE="2025-08-01"

# Gradle folder
GRADLE_DIR="$HOME/.gradle"

# List of folders to clean
FOLDERS=("android" "build-scan-data" "caches" "daemon" "kotilin-profile" "native" "notifications" "undefined-build" "wrapper" ".tmp")

# 1. Stop Gradle Daemons first so files aren't locked
if [ -f "$GRADLE_DIR/daemon" ]; then
    echo "Stopping Gradle daemons..."
    gradle --stop 2>/dev/null || ./gradlew --stop 2>/dev/null
fi

echo "Cleaning Gradle folders in $GRADLE_DIR modified before $CUTOFF_DATE..."

for folder in "${FOLDERS[@]}"; do
    TARGET="$GRADLE_DIR/$folder"
    if [ -d "$TARGET" ]; then
        echo "Processing $TARGET..."
        # Find and delete files modified before the cutoff date
        find "$TARGET" -type f ! -newermt "$CUTOFF_DATE" -print -delete
        # Remove empty directories
        find "$TARGET" -type d -empty -print -delete
    else
        echo "Folder $TARGET does not exist, skipping."
    fi
done

echo "Cleanup complete."

Note:

  • change CUTOFF_DATE for any desire date
  • add or remove FOLDERS depends on your folder structure 

termux android: ffmpeg split video and convert h26h video

Requirement

Install termux from 

  • play store 
  • github.com/termux/termux-app latest but require allow install from unknown source

open termux and update

$ pkg upgrade

install ffmpeg

$ pkg install ffmpeg nano

Allow termux to manage files

$ termux-setup-storage

 Split a video by time

Target:

  • every piece has 1 minutes 58 seconds
  • resolution low 480
  • output h264
  • sound mono

command to convert '172967815750944 (1).mp4'

$ ffmpeg -i 172967815750944\ \(1\).mp4 -vf scale=-2:480 -r 25 -c:v libx264 -preset veryfast -crf 28 -ac 1 -c:a aac -b:a 96k -f segment -segment_time 118 -reset_timestamps 1 out_%03d.mp4

this command will create files with name starting with name out_[number].mp4

This is fastest way to split video into smaller size, but you need to write the long command. This is bash sh to split file to avoid write long command. Open nano and copy paste it, name it with mysplitvid.sh. Make it execute $ chmod u+x mysplitvid.sh.

#!/data/data/com.termux/files/usr/bin/bash

# Check input
if [ -z "$1" ]; then
  echo "Usage: $0 inputvideo.mp4"
  exit 1
fi

INPUT="$1"
BASENAME=$(basename "$INPUT" .mp4)

# Output directory (public, visible to other apps)
OUTDIR="/sdcard/Download/${BASENAME}_split"
mkdir -p "$OUTDIR"

# FFmpeg split + encode
ffmpeg -i "$INPUT" \
  -vf scale=-2:480 \
  -r 25 \
  -c:v libx264 \
  -preset veryfast \
  -crf 28 \
  -ac 1 \
  -c:a aac \
  -b:a 96k \
  -f segment \
  -segment_time 118 \
  -reset_timestamps 1 \
  "$OUTDIR/${BASENAME}_out%02d.mp4"

# Notify Android media scanner for all outputs
for file in "$OUTDIR"/*.mp4; do
  am broadcast \
    -a android.intent.action.MEDIA_SCANNER_SCAN_FILE \
    -d "file://$file" >/dev/null
done

echo "✅ Done!"
echo "📂 Output folder: $OUTDIR”

Convert video h256 to social media

Target:

  • Video Codec: H.264 (libx264)
  • Audio Codec: AAC
  • Pixel Format: yuv420p (Required for maximum compatibility)
  • Audio Sample Rate: 48kHz or 44.1kHz
  • Container: MP4  

To convert an H.265 MP4 to a Twitter-compatible, highly compressed, lower-resolution MP4 using FFmpeg, you need to re-encode the video to H.264 video codec, AAC audio codec, a maximum resolution of 1280x720, and a lower bitrate/higher CRF value.

Assume your file is inputfile.mp4 and located at Download folder in your Android.  Here are command to convert your video to make compatible for social media. Currently social media do not support high compression like h265.

~/ $ cd ~/storage
~/storage $ ls
audiobooks downloads movies podcasts
dcim external-0 music shared
documents media-0 pictures
~/storage $ cd downloads
~/storage/downloads $ ls *.mp4
inputfile.mp4
~/storage/downloads $ ffmpeg -i inputfile.mp4 -c:v libx264 -crf 28 -preset medium -vf "scale=1280:720,format=yuv420p" -c:a aac -b:a 128k -movflags faststart output.mp4
...

This is bash sh to split file to avoid write long command. Open nano and copy paste it, name it with myconverth265.sh. Make it execute $ chmod u+x myconverth265.sh.

#!/data/data/com.termux/files/usr/bin/bash

# Usage: ./convert_video.sh videoinput.mp4

INPUT="$1"

if [ -z "$INPUT" ]; then
    echo "Usage: $0 input_video.mp4"
    exit 1
fi

# Remove extension
BASENAME=$(basename "$INPUT")
NAME="${BASENAME%.*}"

# Output path (shared storage)
OUTPUT="/sdcard/Download/${NAME}_out.mp4"

echo "Converting $INPUT → $OUTPUT"

ffmpeg -y -i "$INPUT" \
    -c:v libx264 \
    -crf 28 \
    -preset medium \
    -vf "scale=1280:720,format=yuv420p" \
    -c:a aac \
    -b:a 128k \
    -movflags faststart \
    "$OUTPUT"

# Notify Android media scanner
am broadcast \
    -a android.intent.action.MEDIA_SCANNER_SCAN_FILE \
    -d "file://$OUTPUT"

echo "✅ Done! Video available in Gallery / Files / Photos”

Make your output video accessible by other application 

Your output file is owned by Termux. To make it accessible by other applications e.g. photos, files, etc, you can try these option

• move it to Movies or Download folder

$ mv out_*.mp4 /storage/emulated/0/Movies/

• Force android system to scan your output

$ am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///sdcard/Download/output.mp4