/** * Odoo, Open Source Management Solution * Copyright (C) 2012-today Odoo SA (<http:www.odoo.com>) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero 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 Affero General Public License for more details * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http:www.gnu.org/licenses/> * * Created on 31/12/14 6:54 PM */ package com.odoo.core.orm.provider; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import com.odoo.core.auth.OdooAccountManager; import com.odoo.core.orm.OModel; import com.odoo.core.orm.OValues; import com.odoo.core.orm.fields.OColumn; import com.odoo.core.support.OUser; import com.odoo.core.utils.JSONUtils; import com.odoo.core.utils.ODateUtils; import java.io.InvalidObjectException; import java.util.Arrays; import java.util.HashSet; public class BaseModelProvider extends ContentProvider { public static final String TAG = BaseModelProvider.class.getSimpleName(); public final static String KEY_MODEL = "key_model"; public final static String KEY_USERNAME = "key_username"; private final int COLLECTION = 1; private final int SINGLE_ROW = 2; protected OModel mModel = null; public UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); public static Uri buildURI(String authority, String model, String username) { Uri.Builder uriBuilder = new Uri.Builder(); uriBuilder.authority(authority); uriBuilder.appendPath(model); uriBuilder.appendQueryParameter(KEY_MODEL, model); uriBuilder.appendQueryParameter(KEY_USERNAME, username); uriBuilder.scheme("content"); return uriBuilder.build(); } @Override public boolean onCreate() { return true; } public String authority() { return null; } public OUser getUser(Uri uri) { String username = uri.getQueryParameter(KEY_USERNAME); return OdooAccountManager.getDetails(getContext(), username); } public void setModel(Uri uri) { String path = uri.getQueryParameter(KEY_MODEL); String username = uri.getQueryParameter(KEY_USERNAME); mModel = OModel.get(getContext(), path, username); assert mModel != null; } private void setMatcher(Uri uri) { String authority = (authority() != null) ? authority() : uri.getAuthority(); matcher.addURI(authority, mModel.getModelName(), COLLECTION); matcher.addURI(authority, mModel.getModelName() + "/#", SINGLE_ROW); } @Override public Cursor query(Uri uri, String[] base_projection, String selection, String[] selectionArgs, String sortOrder) { setModel(uri); setMatcher(uri); if (mModel == null) return null; String[] projection = removeRelationColumns(base_projection); int match = matcher.match(uri); SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); builder.setTables(mModel.getTableName()); // If selection not null and does not contain _is_active if ((selection != null && !selection.contains("_is_active")) || selection == null) { builder.appendWhere("_is_active = 'true'"); } Cursor cr = null; switch (match) { case COLLECTION: cr = builder.query(mModel.getReadableDatabase(), projection, selection, selectionArgs, null, null, sortOrder); break; case SINGLE_ROW: int row_id = Integer.parseInt(uri.getLastPathSegment()); cr = builder.query(mModel.getReadableDatabase(), projection, OColumn.ROW_ID + " = ? ", new String[]{row_id + ""}, null, null, null); case UriMatcher.NO_MATCH: break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); } Context ctx = getContext(); assert ctx != null; if (cr != null) cr.setNotificationUri(ctx.getContentResolver(), uri); return cr; } private String[] removeRelationColumns(String[] projection) { HashSet<String> columns = new HashSet<>(); if (projection != null && projection.length > 0 && mModel != null) { for (String key : projection) { OColumn column = mModel.getColumn(key); if (column != null && column.getRelationType() == null) { columns.add(key); } else if (column != null && column.getRelationType() == OColumn.RelationType.ManyToOne) { columns.add(key); } } columns.addAll(Arrays.asList(new String[]{OColumn.ROW_ID, "id", "_is_active", "_write_date"})); return columns.toArray(new String[columns.size()]); } return null; } @Override public String getType(Uri uri) { return uri.toString(); } @Override public Uri insert(Uri uri, ContentValues all_values) { setModel(uri); setMatcher(uri); ContentValues[] values = generateValues(all_values); ContentValues value_to_insert = values[0]; value_to_insert.put("_write_date", ODateUtils.getUTCDate()); if (!value_to_insert.containsKey("_is_active")) value_to_insert.put("_is_active", "true"); if (!value_to_insert.containsKey("_is_dirty")) value_to_insert.put("_is_dirty", "false"); int match = matcher.match(uri); switch (match) { case COLLECTION: SQLiteDatabase db = mModel.getWritableDatabase(); long new_id = 0; new_id = db.insert(mModel.getTableName(), null, value_to_insert); // Updating relation columns for record if (values[1].size() > 0) { storeUpdateRelationRecords(values[1], OColumn.ROW_ID + " = ?", new String[]{new_id + ""}); } return uri.withAppendedPath(uri, new_id + ""); case SINGLE_ROW: throw new UnsupportedOperationException( "Insert not supported on URI: " + uri); case UriMatcher.NO_MATCH: break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); } notifyDataChange(uri); return null; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int count = 0; setModel(uri); setMatcher(uri); int match = matcher.match(uri); switch (match) { case COLLECTION: SQLiteDatabase db = mModel.getWritableDatabase(); count = db.delete(mModel.getTableName(), selection, selectionArgs); break; case SINGLE_ROW: db = mModel.getWritableDatabase(); String row_id = uri.getLastPathSegment(); count = db.delete(mModel.getTableName(), OColumn.ROW_ID + " = ?", new String[]{row_id}); break; case UriMatcher.NO_MATCH: break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); } notifyDataChange(uri); return count; } @Override public int update(Uri uri, ContentValues all_values, String selection, String[] selectionArgs) { setModel(uri); setMatcher(uri); ContentValues[] values = generateValues(all_values); ContentValues value_to_update = values[0]; if (!value_to_update.containsKey("_write_date")) { value_to_update.put("_write_date", ODateUtils.getUTCDate()); } if (!value_to_update.containsKey("_is_dirty")) { value_to_update.put("_is_dirty", "true"); } int count = 0; int match = matcher.match(uri); switch (match) { case COLLECTION: SQLiteDatabase db = mModel.getWritableDatabase(); count = db.update(mModel.getTableName(), value_to_update, selection, selectionArgs); // Updating relation columns if (values[1].size() > 0) { storeUpdateRelationRecords(values[1], selection, selectionArgs); } break; case SINGLE_ROW: String row_id = uri.getLastPathSegment(); db = mModel.getWritableDatabase(); count = db.update(mModel.getTableName(), value_to_update, OColumn.ROW_ID + " = ?", new String[]{row_id}); // Updating relation columns for record if (values[1].size() > 0) { storeUpdateRelationRecords(values[1], OColumn.ROW_ID + " = ?", new String[]{row_id}); } break; case UriMatcher.NO_MATCH: break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); } notifyDataChange(uri); return count; } private void storeUpdateRelationRecords(ContentValues values, String selection, String[] args) { int row_id = mModel.selectRowId(selection, args); for (String key : values.keySet()) { try { mModel.storeManyToManyRecord(key, row_id, JSONUtils.<Integer>toList(values.getAsString(key)), OModel.Command.Replace); } catch (InvalidObjectException e) { e.printStackTrace(); } } } private ContentValues[] generateValues(ContentValues values) { OValues data_value = new OValues(); OValues rel_value = new OValues(); for (String key : values.keySet()) { OColumn column = mModel.getColumn(key); if (column != null) { if (column.getRelationType() == null) { data_value.put(key, values.get(key)); } else { if (column.getRelationType() == OColumn.RelationType.ManyToOne) { data_value.put(key, values.get(key)); } else { rel_value.put(key, values.get(key).toString()); } } } } return new ContentValues[]{data_value.toContentValues(), rel_value.toContentValues()}; } private void notifyDataChange(Uri uri) { // Send broadcast to registered ContentObservers, to refresh UI. Context ctx = getContext(); assert ctx != null; ctx.getContentResolver().notifyChange(uri, null); } }