A tool to generate Android ContentProviders.

Overview

Android ContentProvider Generator (acpg)

Android Arsenal

A tool to generate Android ContentProviders. It takes a set of entity (a.k.a "table") definitions as the input, and generates:

  • a ContentProvider class
  • an SQLiteOpenHelper class
  • one Columns class per entity
  • one Cursor class per entity
  • one ContentValues class per entity
  • one Selection class per entity
  • one Model interface per entity
  • one Bean class per entity (optionally)

Usage

There are two possible ways to generate the code:

  1. as part of the build script (with a Gradle plugin)
  2. as a one-time step (using a command line tool)

The Gradle plugin is perhaps the 'cleaner' way in the sense that the generated code won't be part of the source (not checked into VCS). The configuration is declared inside the Gradle script which allows to update it easily.

Alternatively, a one-time generation can be done (typically at the beginning of the project.) The generated code is part of the source and checked into VCS: this allows you to modify it if you need to.

You can decide which option is the best for your project :)

Option 1: Gradle plugin

Add this to your app's build.gradle:

buildscript {
    dependencies {
        classpath 'org.jraf:acpg-gradle-plugin:1.13.1'
    }
}

apply plugin: 'org.jraf.acpg.gradleplugin'

(...)

// This is where you declare a few parameters used to generate the code
acpg {
    // Where to find the entity files (see 'Entity files' below)
    // Optional - default value: 'etc/acpg' in the root project
    entitiesDir file('etc/acpg-entities')

    // Java package in which all the code will be generated
    providerJavaPackage 'com.example.app.provider'

    // ContentProvider authority
    // "${applicationId}" will be substituted by BuildConfig.APPLICATION_ID in the generated code
    authority '${applicationId}.provider'

    // Name of the provider class
    providerClassName 'ExampleProvider'

    // Name of the db file
    databaseFileName 'example.db'

    // Version of the db
    databaseVersion 1

    // Name of the SQLiteOpenHelper class
    // Optional - default value: providerClassName + "SQLiteOpenHelper"
    sqliteOpenHelperClassName 'ExampleSQLiteOpenHelper'

    // Name of a subclass of BaseSQLiteOpenHelperCallbacks
    // Optional - this allows you to get called when the db is opened/created/upgraded
    sqliteOpenHelperCallbacksClassName 'ExampleSQLiteOpenHelperCallbacks'

    // Whether to enable foreign keys support (see 'Advanced usage' below)
    // Optional - default value: false
    enableForeignKeys true

    // Whether @Nullable/@NonNull annotations will be used in the generated code
    // Optional - default value: false
    useAnnotations true

    // Whether support library classes are used or the Android SDK ones (e.g. CursorLoader)
    // Optional - default value: false
    useSupportLibrary true

    // Whether to generate a 'Beans' class for each entity
    // Optional - default value: true
    generateBeans true

    // Name of a boolean field in BuildConfig to enable/disable debug logging in the generated code
    // Optional - default value: "DEBUG"
    debugLogsFieldName 'LOG_DEBUG_PROVIDER'

    // Version of the tool syntax (must be 4)
    // The allows to break the build immediately if an incompatible version of the tool is used. Safety first!
    // Optional - default value: 4
    syntaxVersion 4
}

Option 2: Command line tool

The configuration is the same, except you declare it in a file named _config.json in the same folder as the entity files.

Here is an example:

{
	"syntaxVersion": 4,
	"packageName": "com.example.app",
	"providerJavaPackage": "com.example.app.provider",
	"authority": "${applicationId}.provider",
	"providerClassName": "ExampleProvider",
	"databaseFileName": "example.db",
	"databaseVersion": 1,
	"sqliteOpenHelperClassName": "ExampleSQLiteOpenHelper",
	"sqliteOpenHelperCallbacksClassName": "ExampleSQLiteOpenHelperCallbacks",
	"enableForeignKeys": true,
	"useAnnotations": true,
	"useSupportLibrary": true,
	"generateBeans": true,
	"debugLogsFieldName": "LOG_DEBUG_PROVIDER"
}

About packageName: this must be the same as the value of the package attribute in your manifest. Not to be confused with the applicationId (see https://developer.android.com/studio/build/application-id.html)

Get and run the tool

Download the acpg-cli-1.13.1.jar file here: https://github.com/BoD/android-contentprovider-generator/releases/latest

java -jar acpg-cli-1.13.1.jar -i <input folder> -o <output folder>

  • Input folder: where to find _config.json and your entity json files
  • Output folder: where the resulting files will be generated

Entity files

Create one file per entity, naming it <entity_name>.json. Inside each file, declare your fields (a.k.a "columns") with a name and a type. You can also optionally declare a default value, an index flag, a documentation and a nullable flag.

Currently the type can be:

  • String (SQLite type: TEXT)
  • Integer (INTEGER)
  • Long (INTEGER)
  • Float (REAL)
  • Double (REAL)
  • Boolean (INTEGER)
  • Date (INTEGER)
  • byte[] (BLOB)
  • enum (INTEGER).

You can also optionally declare table constraints.

Here is a person.json file as an example:

{
	"documentation": "A human being which is part of a team.",
	"fields": [
		{
			"documentation": "First name of this person. For instance, John.",
			"name": "first_name",
			"type": "String",
			"defaultValue": "John"
		},
		{
			"documentation": "Last name (a.k.a. Given name) of this person. For instance, Smith.",
			"name": "last_name",
			"type": "String",
			"nullable": true,
			"defaultValue": "Doe"
		},
		{
			"name": "age",
			"type": "Integer",
			"index": true
		},
		{
			"name": "gender",
			"type": "enum",
			"enumName": "Gender",
			"enumValues": [
				"MALE",
				"FEMALE",
				{"OTHER": "Value to use when neither male nor female"}
			],
			"nullable": false
		}
	],

	"constraints": [
		{
			"name": "unique_name",
			"definition": "UNIQUE (first_name, last_name) ON CONFLICT REPLACE"
		}
	],
	
	"defaultOrder": "first_name, last_name, age DESC"
}

Notes:

  • An _id primary key field is automatically (implicitly) declared for all entities. It must not be declared in the json file.
  • nullable is optional (true by default).
  • if documentation is present the value will be copied in Javadoc blocks in the generated code.
  • the constraints and defaultOrder sections are optional

A more comprehensive sample is available in the sample-app/etc/acpg folder.

You can have a look at the corresponding generated code in the etc/sample-generated-code folder.

By convention, you should name your entities and fields in lower case with words separated by '_', like in the example above.

The header.txt file (optional)

If a header.txt file is present, its contents will be inserted at the top of every generated file.

Use the generated files

  • When querying a table, use the corresponding Selection class as shown in this example:
PersonSelection where = new PersonSelection();
where.firstName("John").or().age(42);
Cursor c = context.getContentResolver().query(where.uri(), projection,
        where.sel(), where.args(), null);
  • When using the results of a query, wrap the resulting Cursor in the corresponding wrapper class. You can then use the generated getters directly as shown in this example:
PersonCursor person = new PersonCursor(c);
String lastName = person.getLastName();
Long age = person.getAge();
  • You can also conveniently combine these two facilities by using the query (or delete) method:
PersonSelection where = new PersonSelection();
where.firstName("John").or().age(42).orderByFirstName();
PersonCursor person = where.query(context);
person.moveToNext();
String lastName = person.getLastName();
Long age = person.getAge();

or, use a CursorLoader:

where.getCursorLoader(context);
  • When updating or inserting into a table, use the corresponding ContentValues class as shown in this example:
PersonContentValues values = new PersonContentValues();
values.putFirstName("John").putAge(42);
context.getContentResolver().update(values.uri(), values.values(), null, null);

or

values.insert(context);

Advanced usage

Foreign key / joins

There is limited support for foreign keys and joins. Here is an example of the syntax:

{
	"fields": [
		{
			"name": "main_team_id",
			"type": "Long",
			"nullable": false,
			"foreignKey": {
				"table": "team",
				"onDelete": "CASCADE"
			}
		},
		{
			"name": "first_name",
			"type": "String",
			"nullable": false
		},

		(...)
}

In this example, the field main_team_id is a foreign key referencing the primary key of the team table.

  • The appropriate FOREIGN KEY SQL constraint is generated (if enableForeignKeys is set to true in _config.json).
  • The team table will be automatically joined when querying the person table [1].
  • Getters for team columns are generated in the PersonCursor wrapper.
  • Of course if team has foreign keys they will also be handled (and recursively).

[1] A table is automatically joined if at least one of its columns is included in the projection. If the projection is null (i.e. all columns), all the tables are joined. Caution: you should be extra careful when using a null projection with joins because you will get several columns named _id in the results!

Limitations

  • Foreign keys always reference the _id column (the implicit primary key of all tables) and thus must always be of type Long - by design.
  • Only one foreign key to a particular table is allowed per table. In the example above only one column in person can point to team.
  • Loops (i.e. A has a foreign key to B and B has a foreign key to A) aren't detected. The generator will infinitely loop if they exist.
  • Cases such as "A has a FK to B, B has a FK to C, A has a FK to C" generate ambiguities in the queries, because C columns appear twice. In the sample app you can see an example of how to deal with this case, using prefixes and aliases (SQL's AS keyword).

Sample

A sample is available in the sample-app folder, with the entities in sample-app/etc/acpg.

You can have a look at the corresponding generated code in the etc/sample-generated-code folder.

Here is the table shema of the sample: Table shema of the sample

Building

This is a Gradle project.

./gradlew install to 'install' the Gradle plugin to your local maven repo

./gradlew shadowJar to build the cli tool

Similar tools

Here is a list of other tools that try to tackle the same problem.

I did not have the chance to try them out.

Licence

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Just to be absolutely clear, this license applies to this program itself, not to the source it will generate!

Comments
  • Compiling error (maven)

    Compiling error (maven)

    Hi, when I run mvn assembly:single I get this error:

    [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 17.171s [INFO] Finished at: Thu Nov 21 18:45:59 CET 2013 [INFO] Final Memory: 7M/108M [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal org.apache.maven.plugins:maven-assembly-plugin:2.2-beta-5:single (default-cli) on project android_contentprovider_generator: Error reading assemblies: Descriptor with ID 'bundle' not found -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re-run Maven using the -X switch to enable full debug logging. [ERROR] [ERROR] For more information about the errors and possible solutions, please read the following articles: [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

    Can you help me? Thanks!

    opened by brescia123 9
  •  Another interesting feature would be POJO created by your tool

    Another interesting feature would be POJO created by your tool

    Hi,

    Using your tool, automatically would create POJO objects and the possibility of fill up the pojos using the cursor or similar object

    PD. Sorry, I don't know how to label the issue as enchantment

    enhancement 
    opened by druidamix 8
  • Adding support for CursorLoader

    Adding support for CursorLoader

    I have been playing around with the idea that the _Object_Selection class would have a method to return a CursorLoader.

    Something like this:

    public CursorLoader cursorLoader(Context context, String[] projection) {
       return new CursorLoader(context, *Object*Columns.CONTENT_URI, projection, sel(), args(), order()) {
            @Override
            public Cursor loadInBackground() {
                 return new *Object*Cursor(super.loadInBackground());
            }
       };
    }
    

    ~~I am personally stuck on how to get the AbstractSelection to come into effect. Would we need a _Object_CursorLoader that extends a _Object_AbstractCursorLoader?~~

    It seems that adding that Edited code above to the selection class would work well but I am not sure if it is nessessary.

    enhancement 
    opened by codeversed 6
  • String equality called with equal signs

    String equality called with equal signs

    The columns class for a table uses == to check string equality in hasColumns, is this intentional because it would seem risky to do this if the content provider will be used in a cross process environment.

    bug 
    opened by ansman 6
  • Package import statements do not match package declarations

    Package import statements do not match package declarations

    I have a configuration file, named entity_name.json:

    {
        "fields": [
            {
                "name": "entity_name_field",
                "type": "String"
            }
        ]
    }
    

    When the entity_nameclasses are generated, their package names lack underscores:

    package com.app.provider.entityname;
    

    However, in the generated classes that import the generated entity_name classes, the import statements include underscores:

    import com.app.provider.entity_name.*;
    

    I'm using version 1.8.0.

    opened by decubate 6
  • small improvement to use join clauses

    small improvement to use join clauses

    the second commit is maybe not very useful outside my case, but it can be used with ArrayUtils.addAll(FirstTableColumn.JOIN_FULL_PROJECTION, SecondTableColumn.JOIN_FULL_PROJECTION);

    opened by sgueniot 5
  • Add json fields and modify generation to use primitive data type

    Add json fields and modify generation to use primitive data type

    First of all, great job !

    I added/modified several things :

    • for json fields:
      • in _config.json : add boolean to manage foreign keys
        • in json table file :
          • index : boolean attribute to set if field is an index
        • nullable : boolean attribute to set if field is nullable or not
        • default_value : string to set a default value
    • refactor packages :
      • put Column interface in 'table' package
      • put all wrapper in 'wrapper' package
        • all cursorwrapper classes are in 'cursor' package
        • all selection classes are in 'selection' package
        • all contentvalues classes are in 'contentvalues' package
    • modify classes Column to java interface which extend BaseColumn
    • add a default projection for each Column interface
    • modify lot of getter/setter to avoir autoboxing (Integer != int)

    This generator is very useful ! I hope my modifications will help Thanks

    opened by julienbanse 5
  • Cannot get mandatory column after doing an

    Cannot get mandatory column after doing an "optional" join

    We have these tables:

    // table1.json
    {
      "fields": [{
        "name": "capture_id",
        "type": "Long",
        "nullable": true,
        "index": true,
        "foreignKey": {
            "table": "table2",
            "onDelete": "CASCADE"
        }
      }]
    }
    
    // table2.json
    {
      "fields": [{
        "name": "name",
        "type": "String",
        "nullable": false
      }]
    }
    

    So when we do this:

    Table1Cursor cursor = new Table1Selection()
        .id(1)
        .query(context.getContentResolver(), new String[] {Table2.name, ...});
    cursor.moveToNext();
    cursor.getTable2Name(); // Crash because it throws a NullPointerException
    

    It possible it would be great if we allowed non null columns for tables joined with nullable foreign keys to return null

    opened by ansman 4
  • Unnecessary autoboxing in AbstractCursor and generated cursors

    Unnecessary autoboxing in AbstractCursor and generated cursors

    I think it isn't very efficient:

    public Integer getIntegerOrNull(String colName) {
            Integer index = getCachedColumnIndexOrThrow(colName); // int to Integer
            if (isNull(index)) return null; // Integer to int
            return getInt(index); // Integer to int and int to Integer
    }
    

    and

    public class GeneratedCursor extends AbstractCursor {
            ...
            public String getTitle() {
                    Integer index = getCachedColumnIndexOrThrow(GeneratedColumns.TITLE); // int to Integer
                    return getString(index); // Integer to int
            }
            ...
    }
    
    enhancement 
    opened by yargray 4
  • FEATURE REQUEST: Support for SQL Views

    FEATURE REQUEST: Support for SQL Views

    Hi again. Another topic i recently came across is the support for SQL Views. I need to define a View to easily merge/Join some tables for simple usage in Lists with a CursorAdapter. Therefore i had to change the genrated classes and i think it would be possible to support Views out of the box.

    What we need to use a View: -a Type property for each .json file indicating if this is a table or a view -Selection -Cursor -Columns -SQL_CREATE -> i currently added it in the OpenHelperCallbacks class, but i think it would be nicer to add the views directly in the DatabaseHelper, e.g. with defining a method: public String getStatementFor in the OpenHelperCallbacks Interface/class -additional URI for the View and the appropriate Mappings in the Contentprovider itself, alternatively the possibility to define and Extension class to add this kind of URIs

    Would be nice to discuss it with you. Regards.

    enhancement 
    opened by dennyh89 4
  • Not qualified columns when projection is null (relevant again)

    Not qualified columns when projection is null (relevant again)

    When the projection in a query is null, there are multiple columns with name "_id". See https://github.com/BoD/android-contentprovider-generator/issues/40 for more detailed explanation.

    The referenced issue was marked as solved, but I experience this problem now. I assume it was introduced again in the commit https://github.com/BoD/android-contentprovider-generator/commit/5456651fede6670b090a618e90ee2225f20bc97e.

    opened by frigus02 3
  • What is the use of beans?

    What is the use of beans?

    Hello,

    What is the intention of the beans?

    If I have a tablet with 20 columns, I have the fill up the bean manually?

    I didn't find any method to fill up a bean form a select?

    Thanks,

    opened by druidamix 0
  • Could not find org.jraf:acpg-gradle-plugin

    Could not find org.jraf:acpg-gradle-plugin

    Error:Could not find org.jraf:acpg-gradle-plugin:1.13.1. Searched in the following locations: file:/Applications/Android Studio.app/Contents/gradle/m2repository/org/jraf/acpg-gradle-plugin/1.13.1/acpg-gradle-plugin-1.13.1.pom file:/Applications/Android Studio.app/Contents/gradle/m2repository/org/jraf/acpg-gradle-plugin/1.13.1/acpg-gradle-plugin-1.13.1.jar Required by: project :app

    opened by GeekEver 0
  • How use feature bulkInsert?

    How use feature bulkInsert?

    Hi @BoD . I check in class SampleProvider have method bulkInsert(...). You can show for me example use it. In my application, i have to insert Bulk of record in SQLite database android, record size may 3000 or larger and it take so much time. I need use feature bulkInsert for reduce insert time.

    opened by sonvp 0
  • Android Lint error

    Android Lint error

    I generated a simple content provider and I get this when i run Android Lint on the newly generated classes:

    Do not place Android context classes in static fields (static reference to MyDataStoreProviderSQLiteOpenHelper which has field mContext pointing to Context); this is a memory leak (and also breaks Instant Run)

    This is located in the generated ...SQLiteOpenHelper extends SQLiteOpenHelperclass.

    enhancement 
    opened by edwardhuerta 3
  • Notify option is not respected by *ContentValues.update method

    Notify option is not respected by *ContentValues.update method

    The generated SomeModelContentValues.update(Context context, SomeModelSelection where) method uses uri() where where.uri() should be used. And likewise the SomeModelContentValues.update(ContentProvider contentProvider, SomeModelSelection where) method.

    I've issued a pull request to fix this problem and make some other improvements.

    Or please edit the contentprovider.ftl file and fix this problem. Thanks.

    needsinfo 
    opened by zhcnxf 1
Releases(v1.13.1)
Owner
Benoit Lubek
I do mostly Android development. See http://JRAF.org for more info about me :)
Benoit Lubek
generate TestCase

generate TestCase with Kotlin Usage 1. add maven Repository to build.gradle.kts repositories { maven { url = uri("https://maven.pkg.github

kamedon 1 Jan 5, 2022
Generate MVVM-UseCase-Api-DI feature packages with 1 click

Android MVVM Generator Pre-requisites Project assumes that you use Hilt, Retrofit, Kotlin, Flows, Jetpack ViewModel and MVVM with UseCase layer. Impor

Talha Hasan Zia 16 Dec 10, 2022
Pure Java code generation tool for generating a fully functional ContentProvider for Android.

RoboCoP RoboCoP is a Java library that can generate a fully-functional ContentProvider from a simple JSON schema file. Get the latest version from our

Rain 246 Dec 29, 2022
Codegeneration tool for isomorphic server and mobile Go apps with gRPC & Protobuf. Share code between your backend, Android & iOS app! :sun_with_face:

Anakin Codegeneration tool for isomorphic server and mobile Go apps with gRPC & Protobuf. Share code between your backend, Android & iOS app! Descript

Kirill Biakov 17 Jun 25, 2020
:package: Android Parcelables made easy through code generation.

Parceler Have a question? Ask it on StackOverflow. Found an issue? Please report it. In Android, Parcelables are a great way to serialize Java Objects

John Ericksen 3.6k Dec 27, 2022
Android Parcelable models made easy

AutoParcel AutoParcel is an AutoValue extension that enables Parcelable values generation. Just add implements Parcelable to your @AutoValue annotated

Francesco Sardo 1.4k Dec 23, 2022
A custom view styling library for Android that generates the obtainStyledAttributes() and TypedArray boilerplate code for you.

DEPRECATED This project is no longer maintained. Consider using https://github.com/airbnb/paris Barber Barber is your personal custom view stylist. Si

Zac Sweers 716 Dec 30, 2022
A easy way to use android sharepreference

Favor A easy way of using Android SharedPreferences. How to use this library Using Gradle compile 'com.cocosw:favor:0.2.0@aar' Using Maven <depend

Kai Liao 467 Nov 10, 2022
A code generator to create Android ContentProvider

DatabaseCodeGenerator This project is a code generator written in Java used to generate Android code. Given a database schema JSON definition file, it

Foxykeep 297 Nov 25, 2022
Android 注解自动生成与 Flutter 通信的代码

Android-Flutter-Annotation 用于 Android 端项目,通过使用注解自动生成与 Flutter 通信的代码。 可生成的三种通信渠道有: MethodChannel EventChannel BasicMessageChannel 集成 在项目的 build.gradle

JOYY UED 4 Nov 1, 2021
A small tool to help you generate android projects that have a base code.

Volt Project A small tool to help you generate android projects that have a base code. Usage Change project in base directory. python volt-gen.py <pac

Victor Varenik 3 Feb 2, 2022
andle is an Android tool help you sync dependencies, sdk or build tool version.

andle andle is an Android tool to help you sync dependencies, SDK or build tool version. Installation Simple install by pip: $ sudo pip install andle

Jintin 58 Sep 17, 2022
This tool patches the CVE-2021-44228 Log4J vulnerability present in all minecraft versions NOTE THIS TOOL MUST BE RE-RUN after downloading or updating versions of minecraft as its not a perminent patch

WARNING THIS EXPLOIT EFFECTS BOTH CLIENTS AND SERVERS There is currently a exploit going around that affects all versions of Minecraft this exploit ab

Jacobtread 6 Aug 23, 2022
:sound: [Android Library] Easily generate pure audio tone of any frequency in android

Android library to easily generate audio tone in android. Generating pure tone of an specific frequency was never that easy. ZenTone does all the heav

Nishant Srivastava 102 Dec 19, 2022
Android library to generate image avatar from the first letter of a username. Letter avatar like Gmail Android best practice

AvatarImageGenerator Generate first letter avatar Image like gmail's contact avatar. It generates an drawable that can be be set to an ImageView. Inst

Korir Amos 61 Sep 25, 2022
Generate data-view-binding adapters of android recycler view.

Items 这个库可以为 Android 的 RecyclerView 生成基于 Data-View-Binding 的 Adapter。 对比其他一些类似的开源库,它有以下的一些优势: 更好的拓展性。这个库不需要你继承特定的 Adapter 或 ViewHolder 类,你可以继承任何第三方提供的

nekocode 253 Nov 11, 2022
🚀Plugin for Android Studio And IntelliJ Idea to generate Kotlin data class code from JSON text ( Json to Kotlin )

JsonToKotlinClass Hi, Welcome! This is a plugin to generate Kotlin data class from JSON string, in another word, a plugin that converts JSON string to

Seal 2.8k Jan 3, 2023
Simple library to generate and view PDF in Android

PDFCreatorAndroid Simple library to generate and view PDF in Android A simple library to create and view PDF with zero dependency Or native code. Add

Tej Pratap Singh 234 Dec 11, 2022
Android application that allows users to take or import photo of chessboard, recognizes the pieces and generate the Forsyth-Edwards Notation.

♛ ChessBoard Importer Frontend ♛ Repository for the frontend part of project "Chessboard Importer". The project is realized by the team of 4 students

null 8 Feb 26, 2022