Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Tuesday, December 2, 2025

Android java: Releasing resource Activity/Fragment <-> AndroidViewModel <-> MyController

Activity/Fragment <-> AndroidViewModel <-> MyController

MyController will hold

  1. Executor
  2. MediaController 
  3. Network access
  4. Database access
  5. Application context for above operation  

Proper way to clear the resource

  1. Executor
            // MyAndroidViewModel onClear() has been reached
            if (myExecutor!=null) {
                myExecutor.shutdown();
                myExecutor = null;
            }
  2. Media Controller
            if (myMediaController!=null) {
                myMediaController.releaseMediaSession(); // MUST BE CLEAR
                myMediaController = null;
            }
  3. Network access
            myGetRadioLogo=null;
  4. Database access  
            myDBAccess=null;
  5. Application context
            appContext=null; // MUST SET NULL AT ONCLEAR

Fail to release these resources may lead to memory leak

Fail to release resource with not properly sequence may lead application crashed, this crash can be found on logcat.  

Tuesday, November 11, 2025

Android java: using DataLive for orientation change

I test it in Android 10 xiaomi mia2

What will happen when orientation change e.g. screen change or language change?

the object in activity will be destroyed. here is the sequence:

  • after orientation change
    V  onPause
    V  onStop
    V  onDestrouy
  • after flusing all data
    V  onCreate
    V  onCreate myClass :null
    V  MyClass constructor
    V  onCreateView myClass.getRandom
    V  onStart
    V  onResume 

At xiomi mia2, override public void onConfigurationChanged(Configuration newConfig) method did not called at all during screen orientation change, not appearred in logcat. except, you put android:configChanges in your layout.

Base on the android behave, these are the solution can be used to keep the object during configuration change:

  1. Local persistence to handle process death for complex or large data. Persistent local storage includes databases or DataStore.
  2. Retained objects such as ViewModel instances to handle UI-related state in memory while the user is actively using the app.
  3. Saved instance state to handle system-initiated process death and keep transient state that depends on user input or navigation. 

ViewModel is recommended used for today an future, These are some options:

  1. ViewModel    Holds UI data & logic    Core architecture component
  2. ComputableLiveData (Deprecated)
  3. MediatorLiveData    Combines multiple LiveData sources    Merging streams
  4. MutableLiveData    Observable, mutable data    UI updates
  5. SavingStateLiveData    LiveData with saved-state persistence    Restore after process death 

Comparison between AndroidViewModel vss ViewModel

AndroidViewModel require Context for DB, prefs, etc. gemini: Avoid if possible. Only use when you absolutely need Context. violates a core principle of good architecture: separation of concerns and testability.
ViewModel UI logic without needing Context Network requirement may use this, it does not need context

NOTE: Don’t store Activity or Fragment in VieModel nor AndroidViewModel! 

These are the java code for education, first text using direct access to object in activity, and second text using live data and AndroidViewModel. this code used AndroidViewModel, because I need application's context to access sqlite.

-- MyClass.java

package com.dedetok.testdataorientation;

import android.content.Context;
import android.util.Log;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

import java.util.Random;

public class MyClass {
    // dummy
    Context context;

    boolean isCreated = false;
    int randomNumber=-1;

    public MyClass(Context context) {
        this.context = context;
        Log.v("deddetok", "MyClass constructor"); // debug

    }

    public String getRandom() {
        if (!isCreated) {
            Random random = new Random();
            randomNumber = random.nextInt(100);
            isCreated = true;
        }
        return String.valueOf(randomNumber);
    }

    public LiveData<String> getRandomLive() {
        if (!isCreated) {
            Random random = new Random();
            randomNumber = random.nextInt(100);
            isCreated = true;
        }
        MutableLiveData<String> returnValue = new MutableLiveData<>();
        returnValue.setValue(String.valueOf(randomNumber));

        return returnValue;
    }
}

-- MyAndroidViewModel

package com.dedetok.testdataorientation;

import android.app.Application;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;

public class MyAndroidViewModel extends AndroidViewModel {
    MyClass myClass;

    public MyAndroidViewModel(@NonNull Application application) {
        super(application);
        myClass = new MyClass(application);
    }

    public LiveData<String> getData() {
        return myClass.getRandomLive();
    }
}

-- MainActivity.java

package com.dedetok.testdataorientation;

import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.lifecycle.ViewModelProvider;

public class MainActivity extends AppCompatActivity {

    MyClass myClass = null;

    AppCompatTextView myTextView1, myTextView2;

    MyAndroidViewModel myAndroidViewModel;

    /* to preserve myclass
     * 1. Local persistence to handle process death for complex or large data. Persistent local storage includes databases or DataStore.
     * 2. Retained objects such as ViewModel instances to handle UI-related state in memory while the user is actively using the app.
     * 3. Saved instance state to handle system-initiated process death and keep transient state that depends on user input or navigation.
     */

    @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;
        });

        Log.v("dedetok", "onCreate"); // debug


        myTextView1 = findViewById(R.id.textView1);
        myTextView2 = findViewById(R.id.textView2);

        Log.v("dedetok", "onCreate myClass :"+myClass); //
        // myTextView1
        if (myClass==null) {
            myClass = new MyClass(this);
        }
        myTextView1.setText(myClass.getRandom()); // work, created in memory

        // ✅ Get ViewModel (it survives rotation)
        // myTextView2
        myAndroidViewModel = new ViewModelProvider(this).get(MyAndroidViewModel.class);
        myAndroidViewModel.getData().observe(this, value -> {
            // 'value' is a plain String here
            myTextView2.setText(value);
        }); //

    }

    @Nullable
    @Override
    public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) {
        Log.v("dedetok", "onCreateView"); // debug
        //Log.v("dedetok", "onCreateView myClass.getRandom"); // debug
        //myTextView.setText(myClass.getRandom()); // crash textview not ready
        return super.onCreateView(parent, name, context, attrs);
    }

    /*
     * ## activity life cycle 2 ##
     */
    @Override
    protected void onStart() {
        super.onStart();
        Log.v("dedetok", "onCreateView myClass.getRandom"); // debug
        //myTextView1.setText(myClass.getRandom()); // work, view has been created
        myAndroidViewModel.getData().observe(this, value -> {
            // 'value' is a plain String here
            myTextView2.setText(value);
        }); // this is fine place

        Log.v("dedetok", "onStart"); // debug

    }

    /*
     * ## activity life cycle 3 ##
     */
    @Override
    protected void onResume() {
        super.onResume();

        Log.v("dedetok", "onResume"); // debug


    }

    /*
     * ## activity life cycle 4 ##
     */
    @Override
    protected void onPause() {
        Log.v("dedetok", "onPause"); // debug
        super.onPause();

    }

    /*
     * ## activity life cycle 5 ##
     */
    @Override
    protected void onStop() {
        Log.v("dedetok", "onStop"); // debug
        super.onStop();

    }

    /*
     * ## activity life cycle 6 ##
     */
    @Override
    protected void onDestroy() {
        Log.v("dedetok", "onDestrouy"); // debug

        super.onDestroy();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.v("dedetok", "onConfigurationChanged"); // debug
    }
}

-- 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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        android:minHeight="50dp"
        android:textSize="24sp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        />
    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView2"
        android:minHeight="50dp"
        android:textSize="24sp"
        app:layout_constraintTop_toBottomOf="@+id/textView1"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        />
</androidx.constraintlayout.widget.ConstraintLayout>

Some of parts of this content generated by ChatGPT and Gemini with some modification.

Tuesday, November 4, 2025

Java Netbeans: using opencsv from url

Create a new Project Java with Maven project name e.g. TestOpenCSV

At Project Files, open pom.xml and add

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.dedetok</groupId>
    <artifactId>TestOpenCSV</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.release>17</maven.compiler.release>
        <exec.mainClass>com.dedetok.testopencsv.TestOpenCSV</exec.mainClass>
    </properties>
    <dependencies>
    <dependency>
        <groupId>com.opencsv</groupId>
        <artifactId>opencsv</artifactId>
        <version>5.9</version> <!-- or latest -->
    </dependency>
</dependencies>
</project>

You can use beans to map the result, or manually process the csv files. for data size bigger then 1 millions, there is deference about 1 second, it is better to manually process. Here are code to test, before you use it in production. Feel free to change any the code to meet your requirement.

Classs Radio

/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */
package com.dedetok.testopencsv;

import com.opencsv.bean.CsvBindByName;

/**
 *
 * @author dedetok
 */
public class Radio {
    @CsvBindByName(column = "country")
    private String country;

    @CsvBindByName(column = "city")
    private String city;

    @CsvBindByName(column = "radioname")
    private String radioname;

    @CsvBindByName(column = "url_logo")
    private String url_logo;

    @CsvBindByName(column = "url_stream")
    private String url_stream;

    public String getCountry() { return country; }
    public String getCity() { return city; }
    public String getRadioname() { return radioname; }
    public String getUrl_logo() { return url_logo; }
    public String getUrl_stream() { return url_stream; }
}

Main class

/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 */

package com.dedetok.testopencsv;

import com.opencsv.CSVParser;
import com.opencsv.CSVParserBuilder;
import com.opencsv.CSVReader;
import com.opencsv.CSVReaderBuilder;
import com.opencsv.exceptions.CsvValidationException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.opencsv.bean.CsvBindByName;
import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.CsvToBeanBuilder;
import java.util.List;

/**
 *
 * @author dedetok
 */
public class TestOpenCSV {

    static String urlString = "https://raw.githubusercontent.com/dedetok/myradiolist/refs/heads/main/myradio_radiolist.csv";
    
    public static void main(String[] args) throws CsvValidationException {
        System.out.println("Hello World!");
        
        // start function

        try {
            URL url = new URL(urlString);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            // Check response
            int status = connection.getResponseCode();
            if (status == 200) {
                // Configure parser: semicolon separator, double-quote as quote char
                InputStreamReader reader = new InputStreamReader(connection.getInputStream());
                // Start For parsing manual
                CSVParser parser = new CSVParserBuilder()
                        .withSeparator(';')
                        .withQuoteChar('"')
                        .build();
                //CSVReader csvReader = new CSVReader(reader);
                CSVReader csvReader = new CSVReaderBuilder(reader)
                        .withCSVParser(parser)
                        //.withSkipLines(1) // skip header if needed
                        .build();
                String[] nextLine;
                int i=1;
                while ((nextLine = csvReader.readNext()) != null) {
                    for (String cell : nextLine) {
                        System.out.print(cell + " | ");
                    }
                    i++;
                    System.out.println();
                    if (i==5) {
                        break;
                    }
                }
                // End For parsing manual
                // start convert directly to list<Radio> using opencsv beans
                /*
                CsvToBean<Radio> csvToBean = new CsvToBeanBuilder<Radio>(reader)
                    .withType(Radio.class)
                    .withSeparator(';')
                    .withQuoteChar('"')
                    .withIgnoreLeadingWhiteSpace(true)
                    .build();
                List<Radio> radios = csvToBean.parse();
                System.out.println("Loaded " + radios.size() + " radios!");
                
                // debug
                for (int i = 0; i < Math.min(5, radios.size()); i++) {
                    Radio r = radios.get(i);
                    System.out.println(
                        r.getCountry() + " | " +
                        r.getCity() + " | " +
                        r.getRadioname() + " | " +
                        r.getUrl_logo() + " | " +
                        r.getUrl_stream()
                    );
                }
                */
                // end convert directly to list<Radio> using opencsv beans

            }
        } catch (MalformedURLException ex) {
            Logger.getLogger(TestOpenCSV.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(TestOpenCSV.class.getName()).log(Level.SEVERE, null, ex);
        }      
    }
}

 Code is write with collaboration with chatgpt 

 

Tuesday, October 14, 2025

Android Studio 2024.1.4: backup and how you migrate your old project

Android Studio move so fast, the developer and ide tools are racing each other.

These are must you backup: 

  1. Project name
  2. Your key file i.e jks for your project
  3. your project source
    1. <root_app> remove folders .gradle .idea and build, remove file .gitignore
    2. <root_app>/app remove folders build, libs and release, remove file .gitignore
    3. <root_app>/gradle remove file ./gradle/wrapper/gradle-wrapper.jar

For upgrading from old project, and your project does not have libs.versions.toml file:

  1. Create it (see below) or copy from other project <root_app>/gradle/libs.versions.toml.
  2. change build.gradle.kts (:app)
    plugins {
        id("com.android.application")
    }
    into 
    plugins {
        alias(libs.plugins.android.application)
    }
    Change dependency 
    dependencies {

        implementation("androidx.appcompat:appcompat:1.7.1")
        implementation("com.google.android.material:material:1.12.0")
        implementation("androidx.constraintlayout:constraintlayout:2.2.1")
        testImplementation("junit:junit:4.13.2")
        androidTestImplementation("androidx.test.ext:junit:1.2.1")
        androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
        implementation("com.google.android.gms:play-services-ads-lite:24.0.0")

    }
    into
    dependencies {

        implementation(libs.appcompat)
        implementation(libs.material)
        testImplementation(libs.junit)
        androidTestImplementation(libs.ext.junit)
        androidTestImplementation(libs.espresso.core)

        // admob
        implementation(libs.play.services.ads)
    }
  3. Update your libs.versions.toml, revise the version if necessary:
    [versions]
    agp = "8.13.0"
    junit = "4.13.2"
    junitVersion = "1.3.0"
    espressoCore = "3.7.0"
    appcompat = "1.7.1"
    material = "1.13.0"
    playServicesAds = "24.7.0"

    [libraries]
    junit = { group = "junit", name = "junit", version.ref = "junit" }
    ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
    espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
    appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
    material = { group = "com.google.android.material", name = "material", version.ref = "material" }
    play-services-ads = { module = "com.google.android.gms:play-services-ads", version.ref = "playServicesAds" }

    [plugins]
    android-application = { id = "com.android.application", version.ref = "agp" }

Note: if you have other external dependency, keep the library first, and migrate after common library successfully upgraded.

To create libs.versions.toml, at left side Project Files -> gradle -> New -> File, name it with: libs.versions.toml. Continue step 2 above.

This is example of external library, how to move jsoup into libs.versions.toml

Change build.gradle.kts (:app) from  

dependencies {
    ...
    implementation("org.jsoup:jsoup:
1.21.2") // jsoup

}

change into

dependencies {
    ....
    implementation(libs.jsoup)
}

And add into libs.versions.toml

[versions]
...
# Define the version number
jsoup = "1.21.2"
...
[libraries]
...
# Define the library coordinates, referencing the version above
jsoup = { group = "org.jsoup", name = "jsoup", version.ref = "jsoup" }
...

 

Tuesday, August 5, 2025

Netbeans: using maven to connect to mariadb

  1. Create project "Java with Maven" -> "Java Application"
  2. Under tab "Project" -> Project Files, edit pom.xml and add mariadb jconnect client
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    ...
      <dependencies>
    ...
      <dependency>
        <groupId>org.mariadb.jdbc</groupId>
        <artifactId>mariadb-java-client</artifactId>
        <version>3.5.3</version>
        </dependency>
      </dependencies>
    ...
    </project>
  3. Test connection using this code
      public static void main(String[] args) throws SQLException, ClassNotFoundException {
        // TODO Auto-generated method stub
        String muser = "my_user";
        String mpass = "my_password";
        String murl = "jdbc:mariadb://localhost:3306/my_database_name";
        Class.forName("org.mariadb.jdbc.Driver");
        Connection connection = DriverManager.getConnection(murl, muser, mpass);
        System.out.println("ok");
      }

Friday, May 9, 2025

Netbeans: Netbeans 25 with JDK 17 add MariaDB Connector/J 3.5 into Java Project Maven

Netbeans 25 can be download from https://www.apache.org/dyn/closer.lua/netbeans/netbeans/25/netbeans-25-bin.zip. Extract it, it will create folder netbeans. 

On debian, to run netbeans:

[home_user]\netbeans/bin/netbeans

On window, to run netbeans (windows 64):

[folder]\netbeans\bin\netbeans64.exe

Note: on windows, if you want to clean install remove/delete folder C:\Users\[username]\AppData\Roaming\NetBeans\[any_previous].

Download mariadb-java-client-3.5.3.jar from https://mariadb.com/downloads/connectors/connectors-data-access/java8-connector/ and choose mariadb-java-client-3.5.3.jar.

Add mariadb-java-client-3.5.3.jar into project

  1. create a New Project -> Java with maven -> Java application
  2. in tab Files under your project, create folder libjar
  3. copy mariadb-java-client-3.5.3.jar
  4. in Project -> your project, right click on Dependencies, Add Dependency:
    Group ID: org.mariadb.jdbc
    Artifact ID: mariadb-java-client
    Version: 3.5.3
  5. Scope: Runtime
  6. done. 

Run your database and test your project by editing your java files:

    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        // TODO Auto-generated method stub
        String muser = "
my_user";
        String mpass = "
my_password";
        String murl = "jdbc:mariadb://localhost:3306/
my_database_name";
        Class.forName("org.mariadb.jdbc.Driver");
        Connection connection = DriverManager.getConnection(murl, muser, mpass);
        System.out.println("ok");
    }

Add mariadb to Services:

  1. to add mariadb connector j, go to Services -> Right Click Databases -> New Connection.
  2. Select MariaDB (MySQL-compatible).
  3. select jar file.
  4. next.
  5. fill/adjust username, password and database name.
  6. test connection.
  7. if successfull, next.
  8. leave default connection info and finish.

You can manage your mariadb database from Service.

Thursday, May 8, 2025

Eclipse 2025-03 (4.35.0) with JDK 17 add MariaDB Connector/J 3.5 into Project

Download mariadb-java-client-3.5.3.jar from https://mariadb.com/downloads/connectors/connectors-data-access/java8-connector/ 

Open eclipse:

  1. create folder lib
  2. copy mariadb-java-client-3.5.3.jar into [workspace_root]/myca/lib/
  3. open project properties -> Build Path -> Configure Build Path
  4. go to tab Libraries and click Classpath -> Add External JARS, i.e. [Workspace_root]/myca/lib/mariadb-java-client-3.5.3.jar 
  5. apply and close

To test mariadb-client without without Eclipse, write TesCon.java using any editor:

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.SQLException;

public class TesCon {

    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        // TODO Auto-generated method stub
        String muser = "
my_user";
        String mpass = "
my_password";
        String murl = "jdbc:mariadb://localhost:3306/
my_database_name";
        Class.forName("org.mariadb.jdbc.Driver");
        Connection connection = DriverManager.getConnection(murl, muser, mpass);
        System.out.println("ok");
    }

}

Run from command line/terminal

$> javac TesCon.java

$> java -cp ./:./mariadb-java-client-3.5.3.jar TesCon

window:

D:\>javac TesCon.java

D:\>java -cp .\;.\mariadb-java-client-3.5.3.jar TesCon

In Eclipse:

  1. create a new Java Project
  2. optional: create package
  3. add a Class with Name TesCon.java
  4. copy paste the code above. If you create a package, replace all after "package [your_package];"
  5. run project
Every major release eclipse, adding external jar, especially j connector, into eclipse is very painful. Loading mariadb connector j using eclipse is Fail.

Tuesday, May 6, 2025

Replace old Eclipse to 2025-3 (clean install windows/Debian)

Assume: you used Eclipse eclipse in tar.gz or zip, not using installer.

In Windows delete this directory

  1. c:\Users\[username]/.eclipse
  2. [home]\eclipse -> your old eclipse
  3. [workspace]\.metadata -> your existing workspace for eclipse

In Debian delete this directory

  1. [home_user]/.eclipse
  2. [home_user]/eclipse -> your old eclipse
  3. [home_user]/[workspace]/.metadata -> your existing workspace for eclipse

Extract eclipse-java-2025-03-R-linux-gtk-x86_64.tar.gz and put "eclipse" folder into [home].

Run your eclipse, select your existing workspace and install all software you need e.q. WindowBuilder.

Note: Windows will ask your permission to prevent Defender scan your Eclipse for performance. I recommended to exclude Eclipse.

To open existing project in your existing workspace

  1. Import -> General -> Existing Project into Workspace -> Next
  2. Point "Select root directory" to your existing project directory.
  3. Finish

 


Friday, September 27, 2024

android java: google admob policy collected data checklist

 

In May 2021, Google Play announced the new Data safety section https://developers.google.com/admob/android/privacy/play-data-disclosure. All application must declare data safety, include if application has 3rd partly like Google Google Mobile Ads.

Unfortunately, the guidance data disclosure in https://developers.google.com/admob/android/privacy/play-data-disclosure does not clear. It does mention clues what Google Mobile Ads SDK do with our client data. Here are the clues:

Data

By default, the Google Mobile Ads SDK...

IP address

Collects device's IP address, which may be used to estimate the general location of a device.

User product interactions

Collects user product interactions and interaction information, including app launch, taps, and video views.

Diagnostic information

Collects information related to the performance of your app and the SDK, including crash logs, app launch time, hang rate, and energy usage.

Device and Account identifiers

Collects Android advertising (ad) ID, app set ID, and, if applicable, other identifiers related to signed-in accounts on the device.

We must fill in Data safety section:

A.    Overview
Information about what to fill.

B.    Data Collection and Security

1.     Does your app collect or share any of the required user data types? Y

·       Is all of the user data collected by your app encrypted in transit? Y

·       þ My app does not allow users to create an account

       Can users login to your app with accounts created outside of the app? N

·       Do you provide a way for users to request that their data is deleted? (Optional) N

C.    Data Types

1.     Location:

·       Approximate location

2.     Personal info:

·       User IDs

·       Other info

3.     App activity:

·       App interactions

·       In-app search history

·       Installed apps

4.     App info and performance:

·       Crash logs

·       Diagnostics

·       Other app performance data

5.     Device or other IDs

·       Device or other IDs

D.    Data usage and handling

1.     Personal info - User IDs & Other info

·       Is this data collected, shared, or both?
þ Collected & Shared

·       Is this data processed ephemerally?
þ No, this collected data is not processed ephemerally

·       Is this data required for your app, or can users choose whether it's collected?
þ Data collection is required (users can't turn off this data collection)

·       Why is this user data collected? Select all that apply.
þ Advertising or marketing

·       Why is this user data shared? Select all that apply.
þ Advertising or marketing

2.     Location - Approximate location:

·       Is this data collected, shared, or both?
þ Collected & Shared

·       Is this data processed ephemerally?
þ No, this collected data is not processed ephemerally

·       Is this data required for your app, or can users choose whether it's collected?
þ Data collection is required (users can't turn off this data collection)

·       Why is this user data collected? Select all that apply.
þ Advertising or marketing

·       Why is this user data shared? Select all that apply.
þ Advertising or marketing

3.     App info and performance - Crash logs; Diagnostics; & Other app performance data

·       Is this data collected, shared, or both?
þ Collected & Shared

·       Is this data processed ephemerally?
þ No, this collected data is not processed ephemerally

·       Is this data required for your app, or can users choose whether it's collected?
þ Data collection is required (users can't turn off this data collection)

·       Why is this user data collected? Select all that apply.
þ Analytics

·       Why is this user data shared? Select all that apply.
þ Analytics

4.     App activity - App interactions; In-app search history; & Installed apps

·       Is this data collected, shared, or both?
þ Collected & Shared

·       Is this data processed ephemerally?
þ No, this collected data is not processed ephemerally

·       Is this data required for your app, or can users choose whether it's collected?
þ Data collection is required (users can't turn off this data collection)

·       Why is this user data collected? Select all that apply.
þ Advertising or marketing

·       Why is this user data shared? Select all that apply.
þ Advertising or marketing

5.     Device or other IDs - Device or other IDs:

·       Is this data collected, shared, or both?
þ Collected & Shared

·       Is this data processed ephemerally?
þ No, this collected data is not processed ephemerally

·       Is this data required for your app, or can users choose whether it's collected?
þ Data collection is required (users can't turn off this data collection)

·       Why is this user data collected? Select all that apply.
þ Advertising or marketing

·       Why is this user data shared? Select all that apply.
þ Advertising or marketing