Pure Java code generation tool for generating a fully functional ContentProvider for Android.

Overview

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 release page: https://github.com/mediarain/RoboCoP/releases

Setup

There are only a few steps involved in order to set up this tool to work inside of your build environment. The steps should not be difficult but should be done with care. At a high level you must:

  1. Download and install the library jar file into your project directory
  2. Include your own custom JSON schema file in your project directory
  3. Configure your build.gradle file and include a special build task to generate the necessary files
  4. Use the Gradle task to generate your ContentProvider and related files
  5. Install your new ContentProvider in your AndroidManifest.xml file
  6. Enjoy

Download And Install

Download and Install

  1. Download the latest jar from the releases section of this repo page. You can use either the jar with dependencies (recommended) or provide them yourself.
  2. Create a new folder in your project directory and name it whatever you want (eg 'RoboCoP'). In a typical Android Studio project, this will be '//RoboCoP/'
  3. Place the RoboCoP jar from step 1 into the RoboCoP directory. a. It's important that you don't put this jar in your libs folder since this is a buildscript dependency not a runtime dependency and it will cause problems if this is confused. b. Don't add the path to this jar to your regular android dependencies in any other way as it will break your build. It's not a runtime dependency; it's just used by the build script.

Create Your JSON Schema Definition

Create a JSON schema definition and place it in the same directory as the RoboCoP jar. You can name this file whatever you like, such as 'schema.json'. A sample schema file can be found in our Wiki: https://github.com/mediarain/RoboCoP/wiki/Example-JSON-schema-definition

Schema File Structure

{
  "packageName" : "<the package name you want for your ContentProvider and associated classes/>",
  "providerName" : "<the base name for your provider. eg. 'Example' will yield 'ExampleProvider.java'/>",
  "databaseVersion" : <the numeric value for the current version of your database. if you increment this, the database will upgrade/>,
  "tables" : [], //see below for table definition rules
  "relationships" : [] //see below for table definition rules
}

Table Definition Structure

{
  "name" : "<name of the table/>",
  "members" : [] //see below for member field definition
}
Table Field Definition Structure
{
  "type" : "<the ~java data type for this field. Your options are: string, double, int, boolean, long (lower case). These will map to SQLite types/>",
  "name" : "<the name of the field (lower case, underscore separated)/>"
}

Relationship Definition Structure

{
  "name" : "<the name of this relationship (lower case, underscore separated)/>",
  "left_table" : "<the left side of the join (in a one-to-many this is the 'one' side)/>",
  "right_table" : "<the right side of the join (the 'many' side)/>",
  "type" : "<the type of relationship. Your current option is 'to_many'"/>
}

Remember that for the code generation to generate nice looking code, you need to write all of your schema values in lower case and underscore-separated.

Gradle Configuration

It's important to note that the only build.gradle file you should be editing is the one lower down in your application module, not the top level build.gradle file.

The Gradle configuration involves just a few steps:

  1. Add a buildscript definition that depends on the RoboCoP library. You need to add the buildscript to your application module build.gradle file not the root level build.gradle file which already should have a build.
  2. Add an import for the RoboCoP generator class
  3. Add a build task for the code generation process

A sample build.gradle file can be found in our Wiki: https://github.com/mediarain/RoboCoP/wiki/Example-build.gradle-file

buildscript definition

// you may be temped to edit your top level build.gradle file because it sorta looks like this. Don't do it. Simply add this code block, to your application module build.gradle file and make sure the path to the jar file is correct.
buildscript {
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath 'com.android.tools.build:gradle:0.9.+'

    //this is the important part, the buildscript needs our library to generate the code.
    classpath files('RoboCoP/RoboCoP-0.5-jar-with-dependencies.jar')
  }
}

RoboCoP generator import

Place the import statement near the top of the file such as right underneath the android plugin

apply plugin: 'android'
import com.rain.utils.android.robocop.generator.*;

Build Task

Place the build task at the root level in your build.gradle file (not inside something like the android{} definition for example)

task contentProviderGen {
    description = 'Generating a beautiful ContentProvider and required classes'
    doLast {
        System.out.println("Generating ContentProvider...")
        String schemaFilename = 'RoboCoP/agenda_schema.json';//replace with the path to your schema
        String baseOutputDir = 'src/main/java/';
        //if gradle throws an error on the following line, you probably either don't have your import statement set or you have the wrong path in your buildscript definition
        ContentProviderGenerator.generateContentProvider(schemaFilename, baseOutputDir);
    }
}

Explanation of Build Task

The above task definition should be pretty easy to follow. This simply allows gradle to execute our code generation task whenever you want. inside the doLast{} block is where the main customization takes place. the schemaFilename variable should point to the location of your JSON schema file (explained previously). the baseOutputDir specifies at what root directory your generated code should be placed. What your resulting package structure (and thus file structure) will depend on what you put in your schema definition for the 'packageName' variable. So if you put 'com.mycompany.awesomeandroidapp' as your packageName, then the resulting files will be under 'src/main/java/com/mycompany/awesomeandroidapp'.

Alternative Output Directories

The generator tool can write code to any directory for which it has permission. We will often place generated code in its own source folder outside our main src directory. Some prefer to keep all their sources, generated or otherwise, in the same location. So as an example, if you want to place your ContentProvider in another directory like 'src-gen' then your build task would look like this:

task contentProviderGen {
    description = 'Generating a beautiful ContentProvider and required classes'
    doLast {
        System.out.println("Generating ContentProvider...")
        String schemaFilename = 'RoboCoP/agenda_schema.json';
        String baseOutputDir = 'src-gen/';
        ContentProviderGenerator.generateContentProvider(schemaFilename, baseOutputDir);
    }
}

However, you are also responsible to make sure that 'src-gen' is included as a source folder in your build.gradle file. This can be done as follows:

sourceSets {
    main {
        java.srcDir 'src-gen'
    }
}

We personally prefer this latter method as it keeps our main source directory uncluttered and focused on our own application code. It works either way though.

Running The Generator

Once all the above setup are complete the last step to getting things up and running is to simply run the custom Gradle build task we created. The easiest way to do this is from Android Studio by right clicking the 'contentProviderGen' task text and selecting the "Run 'gradle:contentProvid...'". This will add a new build configuration in the top menu build configuration drop down. It will also provide you with the option of saving this configuration permanently so that it can be re-run whenever you like. This is a convenient option to select as you will more than likely want to make changes throughout the course of your development process.

Caution

Whenever you run this task, all of your generated code will be replaced with the newly generated code. Also, any other classes that are in the same folders as your generated code will be removed. You should never place your hand written code in the same directories as our generated code.

Installing The Provider - Important!!!

In order to use a ContentProvider in your Android app you must install it into your application's AndroidManifest file. The generator creates a special file in the root of your generated code called 'content-provider.xml' to assist with this step. Copy the contents of this file into your AndroidManifest file inside the node. The generated code has the provider's exported property set to false. If this is true, then other applications may be able to access your data. Only set this to true if you know what you are doing.

Your Done, Enjoy! - Example Usage of a Generated ContentProvider

To learn more about the ContentProvider class and its usages, please read the official docs: http://developer.android.com/reference/android/content/ContentProvider.html and http://developer.android.com/guide/topics/providers/content-providers.html. The following are some examples that encompass most of our typical usages.

Using a CursorLoader To Populate a ListView

Using Loaders is a great way to simplify fetching data for which to populate your UI and take away many of the concerns that come with updating data when necessary and managing the lifecycle of your app.

Implement LoaderCallbacks in your Activity or Fragment

public class AgendaListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {

    @Override
    public void onCreate(Bundle savedInstanceState) {

    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {

    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {

    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {

    }

}

The LoaderCallbacks interface provides hooks for creating your Loader and responding to load events. You'll now have to create your Loader which will be a CursorLoader and handling the events.

Create a CursorLoader
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    return new CursorLoader(getActivity(), AgendaProvider.AGENDA_CONTENT_URI,new String[]{AgendaTable._ID, AgendaTable.NAME},null,null,AgendaTable._ID + " DESC");
}

The generated ContentProvider has content URI's for all your tables and code completion should help you find the one you want. After that you can specify which fields you want to be returned and any query and query params or ordering need to go into the query.

Handle the load event callbacks
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    mAdapter.swapCursor(data);
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
    mAdapter.swapCursor(null);
}

This is about the extent of it. when onLoadFinished will get called whenever a query to your table takes place which could be because you started/restarted the loader explicitly or because your backing data store changed which happens automatically with our generated code. onLoaderReset gets called during (suprise!) resets and so the current data is unavailable and so we pass in null to the cursor for the meantime.

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

    mAdapter = new AgendaAdapter(getActivity(), null, 0);
    setListAdapter(mAdapter);
    getLoaderManager().initLoader(0, null, this);
}

Once it's started, the CursorLoader will query your ContentProvider via the ContentResolver and return your data when it's ready (usually very quickly). Here is what a working ListFragment might look like.

public class AgendaListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {

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

        mAdapter = new AgendaAdapter(getActivity(), null, 0);
        setListAdapter(mAdapter);
        getLoaderManager().initLoader(0, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new CursorLoader(getActivity(), AgendaProvider.AGENDA_CONTENT_URI,new String[]{AgendaTable._ID, AgendaTable.NAME},null,null,AgendaTable._ID + " DESC");
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mAdapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mAdapter.swapCursor(null);
    }

}

Adding a new record to a table

Building off the previous example, we'll demonstrate how to insert new records that will automatically get loaded into the ListFragment once inserted.

Agenda agenda = new Agenda();
agenda.setName("New Agenda");
getContentResolver().insert(AgendaProvider.AGENDA_CONTENT_URI, agenda.getContentValues());

The generated tool includes convenient model classes that map to your table records. They can be constructed/inflated from a cursor and can also export a ContentValues object which is necessary for ContentResolver operations. Because the ListFragment CursorLoader is observing our Agenda table content URI, it, the onLoadFinished will get triggered automatically once this insert operation finishes and our new record will show up in our ListView. Sweet!

Updating an existing record

Let's now suppose that when the user taps on an agenda item from the list they are presented with a form with which to modify the details of the agenda. The following is an example of how to update the agenda item so that its values are saved and automatically reflected by our CursorLoader previously explained.

private void updateAgenda() {
    //mAgenda is an Agenda model object generated by this tool, it has all the getters and setters you need
    mAgenda.setName(mAgendaTitle.getText().toString());
    mAgenda.setPersonConducting(mPersonConducting.getText().toString());
    //there are also convenient constants on the generated Table classes for common necessary strings. AgendaTable.WHERE_ID_EQUALS resolves to "_id = ?"
    getContentResolver().update(AgendaProvider.AGENDA_CONTENT_URI,mAgenda.getContentValues(), AgendaTable.WHERE_ID_EQUALS,new String[]{agenda.getRowId().toString()});
}

Deleting a Record

Sometimes we get tired of some records and we have to let them go. Here's how to do it.

getContentResolver().delete(AgendaProvider.AGENDA_CONTENT_URI, AgendaTable.WHERE_ID_EQUALS, agenda.getRowId().toString());

Performing Batch Operations

Sometimes you have to do a lot of operations and it is much more efficient to do them all as a batch rather than one at a time. Here's a contrived example:

public void updateLotsOfAgendasAtOnce(List<Agenda> agendas) {
    ArrayList<ContentProviderOperation> operations = new ArrayList<>();
    for (int i = 0; i < agendas.size(); i++) {
        ContentProviderOperation operation = ContentProviderOperation.newUpdate(AgendaProvider.AGENDA_CONTENT_URI)
                .withSelection(AgendaTable.WHERE_ID_EQUALS, new String[]{agendas.get(i).getRowId().toString()}).build();
        operations.add(operation);
    }
    //just for fun, let's insert a few new ones while we're at it
    for (int i = 0; i < 10; i++) {
        Agenda agenda = new Agenda();
        agenda.setName("New Agenda " + i);
        ContentProviderOperation operation = ContentProviderOperation.newInsert(AgendaProvider.AGENDA_CONTENT_URI).withValues(agenda.getContentValues()).build();
        operations.add(operation);

    }
    try {
        getContentResolver().applyBatch(AgendaProvider.AUTHORITY, operations);
    } catch (RemoteException e) {
        e.printStackTrace();
    } catch (OperationApplicationException e) {
        e.printStackTrace();
    }
}

There's a couple really good blog posts about this pattern. Please chech them out:

  1. The basics: https://www.grokkingandroid.com/better-performance-with-contentprovideroperation/
  2. withBackReference explained (really useful): https://www.grokkingandroid.com/androids-contentprovideroperation-withbackreference-explained/

FAQ's

Why should I use a ContentProvider? I thought those were just for sharing code between applications.

Yes, the architecture and design decisions behind the ContentProvider is informed by the requirement that some data needs to be shared across application processes. However, we've found that the design also provides benefits for simply working with your own data and we've had great success using them. The biggest reason that we've found as to why more developers don't use them is simply because they are very labor intensive and really hard to get right when written by hand. This tool takes those drawbacks away. Here are a few of our top reasons for using a ContentProvider (not all of them are necessarily exclusive to ContentProviders).

  1. If you do need to share code between Applications or Processes (several of the apps we've built do have multiple processes) then you're already setup.
  2. Many of the Android components are designed to work with them out of the box. For example, CursorLoader takes a ContentProvider content URI and query to fetch results and automatically notify your UI of changes to backing data.
  3. ContentResolver and ContentProvider are built into the system and relied upon heavily throughout Android (Contacts and Calendar are good examples) and so we benefit from a very robust and hardened system. Our generated code is quite small compared to other data abstraction mechanisms and ORM's and we feel comfort (and have had great success) relying heavily on system components rather than a custom system.
  4. We like the client-service-oriented nature of the ContentResolver/ContentProvider architecture. Tables and data are accessed with URI's in much the same way you probably access yourserver-side data. We've observed improvements in the overall application architecture in our apps by communicating between components this way.

What are the current limitations of your tool?

Currently we only generate code that supports querying one-to-many joined table relationships. Our generated code does not allow you to insert/update joined records as a single insert/update. For example if you queried for the join between person and phone numbers you cannot send a request to insert/update to this joined relationship. You must send your insert/update requests directly to the tables they belong to. The generated code also does not currently support many-to-many join operations. These features are on the roadmap as we feel they are important for a complete solution. However, we've built this tool according to what has been needful on our applications and the nature of mobile applications predisposes our UI's to generally only consume data coming from single tables: lists of friends, lists of documents, list of tweets. Very few data presentations problems are solved more optimally than two queries: query for and show a list of people and then query for that person's contact details when the user taps on their record.

License

Copyright 2014 Crossborders, LLC. Rain (DBA)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • Is there any way to make field unique?

    Is there any way to make field unique?

    Hi!

    Thanks for great library :) I`m looking for the way to setup unique constraints on fileds(e.g. field NOT NULL).

    Is there any way to do this with RoboCoP?

    opened by tomkoptel 1
  • double is mapped to string

    double is mapped to string

    Hi! I my schema.json file I declared fields latitude and logitude:

    {
      "packageName": "pl.test",
      "providerName": "Test",
      "databaseVersion": 1,
      "tables": [
         {
           "name": "item",
           "members": [
             {
               "type": "double",
               "name": "latitude"
             },
             {
               "type": "double",
               "name": "longitude"
             }
           ]
         }
      ],
      "relationships" : [
    
      ]
    }
    

    Generated model class has:

    private double mLatitude;
    ...
    setLatitude(cursor.getString(cursor.getColumnIndex(prefix + ItemTable.LATITUDE)));
    

    and table class has CREATE TABLE query with latitude TEXT.

    There should be:

    setLatitude(cursor.getDouble(cursor.getColumnIndex(prefix + ItemTable.LATITUDE)));
    

    and latitude REAL.

    I'm using RoboCoP version 0.5.1.

    opened by ziem 1
  • General updates

    General updates

    I've done a few general changes to the code in the way that relationships are working. I fixed a bug where it was treating both types of one-to-many relationships (left to right and right to left) as the same, and so was printing out too many stings like the following in the table classes:

    String WHERE_TYPE_ID_EQUALS = TYPE_ID + "=?";
    

    I've also added in some useful functions in the generated model classes which I think will be useful:

    A constructor which takes a child class (T extends CurrentObject), so when extending a generated class, there is code to take some of the pain away from converting between child and parent classes.

    I've added a method called getXById which will query the DB for X against the row id and return a fully formed object.

    I've added methods for both types of DB relationship which will return objects based on the type of relationship.

    I wasn't too sure about naming for some of the DB relationship stuff, so would be awesome if you could have a look and let me know what you think and what changes you would like to see.

    opened by martynhaigh 0
  • Boolean data type

    Boolean data type

    When defining a boolean type in the schema the generated class file has a build error. In the constructor for the class something like this is generated:

    setSomeBoolean(cursor.getInt(cursor.getColumnIndex(prefix + DummyTable.SOME_BOOLEAN)));
    

    This of course causes a build error because it is trying to set an int as a boolean. Below should be the correct output:

    setSomeBoolean(!cursor.isNull(cursor.getColumnIndex(prefix + DummyTable.SOME_BOOLEAN)) && 
                    cursor.getInt(cursor.getColumnIndex(prefix + DummyTable.SOME_BOOLEAN)) == 1);
    
    opened by JeremiahStephenson 0
  • To Many relationship

    To Many relationship

    Looks like there is a missing break in the switch when it generates a to many relationship. This code comes from a left table of 'users' and a right table of 'messages'

            case USER_JOIN_MESSAGE_DIR:
                    queryBuilder.setTables(UserTable.TABLE_NAME + " JOIN " + MessageTable.TABLE_NAME + " ON (" + UserTable.TABLE_NAME + "." + UserTable._ID + "=" + MessageTable.TABLE_NAME + "." + MessageTable.USER_ID + ")");
    
                    projection = new String[] {
                        //add left table columns
                        UserTable.TABLE_NAME + "._id AS " + UserTable.TABLE_NAME + "__id",
                        UserTable.TABLE_NAME + "." + UserTable.FIRST_NAME + " AS " + UserTable.TABLE_NAME + "_" + UserTable.FIRST_NAME,
                        UserTable.TABLE_NAME + "." + UserTable.LAST_NAME + " AS " + UserTable.TABLE_NAME + "_" + UserTable.LAST_NAME,
                        UserTable.TABLE_NAME + "." + UserTable.EMAIL + " AS " + UserTable.TABLE_NAME + "_" + UserTable.EMAIL,
                        UserTable.TABLE_NAME + "." + UserTable.PASSWORD + " AS " + UserTable.TABLE_NAME + "_" + UserTable.PASSWORD,
                        UserTable.TABLE_NAME + "." + UserTable.USERNAME + " AS " + UserTable.TABLE_NAME + "_" + UserTable.USERNAME,
                        UserTable.TABLE_NAME + "." + UserTable.AVATAR_URL + " AS " + UserTable.TABLE_NAME + "_" + UserTable.AVATAR_URL,
                        UserTable.TABLE_NAME + "." + UserTable.API_KEY + " AS " + UserTable.TABLE_NAME + "_" + UserTable.API_KEY,
                        MessageTable.TABLE_NAME + "._id AS " + MessageTable.TABLE_NAME + "__id",
                        MessageTable.TABLE_NAME + "." + MessageTable.BODY + " AS " + MessageTable.TABLE_NAME + "_" + MessageTable.BODY,
                        MessageTable.TABLE_NAME + "." + MessageTable.LIKES + " AS " + MessageTable.TABLE_NAME + "_" + MessageTable.LIKES,
                        MessageTable.TABLE_NAME + "." + MessageTable.CREATED_AT + " AS " + MessageTable.TABLE_NAME + "_" + MessageTable.CREATED_AT,
    
                    };
                    break;
                default :
                    throw new IllegalArgumentException("Unsupported URI:" + uri);
            }
    
    
    opened by jpotts18 0
  • Many-To-Many

    Many-To-Many

    Hi, I said I'd upload an ERD of a many-to-many use case at Utah Code Camp. I'm finally getting around to it (sorry). I'm not really awesome with GitHub, so hopefully this is an acceptable place to submit this. Here it is:

    mobile erd - erd

    My use case is an app for warehouse drivers to deliver goods to locations and receive signatures. The many-to-many comes in when we have many tickets which contain the same item. For example, pencils may be on multiple tickets, but we don't want to have pencils in the DB multiple times.

    Hopefully this makes sense. I'm excited for this project, because copy/pasting a bunch of boiler plate is no fun, time consuming, hard to do right and prone to human error. I think more people would use ContentProviders if they weren't so cumbersome, and I hope Google someday writes something like the CoreData UI for building tables and relationships.

    opened by jordanroskelley 1
Releases(0.5.1)
  • 0.5.1(Apr 4, 2014)

  • 0.5(Mar 14, 2014)

    This is our first official release of our new open source ContentProvider generator tool, RoboCoP. If you're curious about the naming here's how it came about:

    Android -> Droid -> Robot -> "Robo" + ContentProvider -> C.P. -> "CoP" = RoboCoP

    This first version is capable of working as a Gradle build task and generating all the necessary classes for a working ContentProvider. The ContentProvider supports single tables and one-to-many join tables. It also generates helper files for Table definitions and also Model files that make working with your data easy. See the README for installation and usage instructions.

    Source code(tar.gz)
    Source code(zip)
    RoboCoP-0.5-jar-with-dependencies.jar(1.41 MB)
    RoboCoP-0.5.jar(19.00 KB)
Owner
Rain
RAIN is a full-service digital agency. We focus on insight-driven strategy and ideation, and the highest possible level of execution.
Rain
A Java API for generating .java source files.

JavaPoet JavaPoet is a Java API for generating .java source files. Source file generation can be useful when doing things such as annotation processin

Square 10k Jan 2, 2023
: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
Kotlin code generation for commercetools platform type-safe product-types, reference expansion and custom fields

Kotlin code generation for commercetools platform type-safe product-types, reference expansion and custom fields

null 8 Dec 15, 2022
Annotation Processor for generating Parcelable code

ParcelablePlease An AnnotationProcessor for generating Android Parcelable boilerplate code. See this blog entry for comparison with other parcel libra

Hannes Dorfmann 260 Mar 31, 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
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
A Java Code Generator for Pojo Builders

PojoBuilder - A Code Generator for Pojo Builders Author: Michael Karneim Project Homepage: http://github.com/mkarneim/pojobuilder About The PojoBuilde

Michael Karneim 330 Dec 11, 2022
A tool to generate Android ContentProviders.

Android ContentProvider Generator (acpg) A tool to generate Android ContentProviders. It takes a set of entity (a.k.a "table") definitions as the inpu

Benoit Lubek 623 Dec 13, 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
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 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
Android 注解自动生成与 Flutter 通信的代码

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

JOYY UED 4 Nov 1, 2021
AndroidQuery is an Android ORM for SQLite and ContentProvider which focuses on easy of use and performances thanks to annotation processing and code generation

WARNING: now that Room is out, I no longer maintain that library. If you need a library to easy access to default android ContentProvider, I would may

Frédéric Julian 19 Dec 11, 2021
laboratory is the next generation Minecraft server management tool fully written in Kotlin

laboratory laboratory is the next generation Minecraft server management tool fully written in Kotlin Installation Linux: Clone this repository using

mooz 5 Oct 29, 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
Utility library dedicated for functional & non-functional codebases to simplify modelling of success and failure responses for the JVM languages 🔀

Expressible Utility library, part of the panda-lang SDK, dedicated for functional codebases that require enhanced response handling. Express yourself

Panda 28 Nov 14, 2022
Functional Kotlin & Arrow based library for generating and verifying JWTs and JWSs

kJWT Functional Kotlin & Arrow based library for generating and verifying JWTs and JWSs. JWS JWT The following Algorithms are supported: HS256 HS384 H

Peter vR 31 Dec 25, 2022
A fully functional social media app built with Kotlin (Android Studio) with multiple features

A social media app built with Kotlin (Android Studio) with multiple features ?? If you like this repo, give it a star ✨ and share ????‍?? it to your f

ThanhPhong 9 Dec 13, 2022
A fully functional Android app built entirely with Kotlin and Jetpack Compose

Now in Android App [Work in progress ?? ] Learn how this app was designed and built in the design case study, architecture learning journey and modula

Android 9.1k Dec 30, 2022
A Java API for generating .java source files.

JavaPoet JavaPoet is a Java API for generating .java source files. Source file generation can be useful when doing things such as annotation processin

Square 10k Jan 2, 2023