diff --git a/README.md b/README.md index e989dec1..51130939 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ -Odoo Mobile -
v2.1.0alpha (Build 5) +Odoo Mobile +
v2.2.0 (Build 6) - -Odoo Mobile v2.1.0 (Framework) +Odoo Mobile v2.2.0 (Framework) ============================== Odoo Mobile is open-source framework allows you to integrate Odoo into your Android app. @@ -19,10 +18,21 @@ It has pre-developed services,providers, controls and more other features to mak **Odoo Mobile** is a part of The Odoo (India) +CHANGELOG +========= + +- June 2016 + - Sync performance improved + - Easy relation record creation (Fix for: https://github.com/Odoo-mobile/framework/issues/138) + - ManyToOne, OneToMany and ManyToMany + - Minor bug fixes + SUPPORTED ODOO VERSIONS ======================= -Odoo 7.0, 8.0, 9.0 +- [x] Odoo 7.0 +- [x] Odoo 8.0 +- [x] Odoo 9.0 HOW TO START ============ @@ -41,3 +51,5 @@ GIVE FEEDBACK - Please report bug or issues [https://github.com/Odoo-mobile/framework/issues](https://github.com/Odoo-mobile/framework/issues) - You can also write us on **android@odoo.co.in** + +Follow us on Twitter: @odoomobile diff --git a/app/build.gradle b/app/build.gradle index 04be87e1..a00f2f6e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,17 +1,21 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + compileSdkVersion 24 + buildToolsVersion "24.0.0" defaultConfig { //FIXME: Change application name as your requirement manifestPlaceholders = [applicationName: "Odoo"] //FIXME: Please change the application id as your requirement applicationId "com.odoo" minSdkVersion 14 - targetSdkVersion 23 - versionCode 5 - versionName "2.1.0alpha" + targetSdkVersion 24 + versionCode 6 + versionName "2.2.0" + multiDexEnabled true + } + dexOptions { + javaMaxHeapSize "2g" } buildTypes { release { @@ -24,10 +28,10 @@ android { dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') - compile 'com.android.support:appcompat-v7:23.3.0' - compile 'com.android.support:cardview-v7:23.3.0' - compile 'com.google.android.gms:play-services:8.4.0' - compile 'com.android.support:design:23.3.0' + compile 'com.android.support:appcompat-v7:24.0.0' + compile 'com.android.support:cardview-v7:24.0.0' + compile 'com.google.android.gms:play-services:9.0.2' + compile 'com.android.support:design:24.0.0' compile project(':intro-slider-lib') compile project(':odoo-rpc-v3') } diff --git a/app/src/main/java/com/odoo/addons/customers/Customers.java b/app/src/main/java/com/odoo/addons/customers/Customers.java index 086dcc2d..a75759ca 100644 --- a/app/src/main/java/com/odoo/addons/customers/Customers.java +++ b/app/src/main/java/com/odoo/addons/customers/Customers.java @@ -63,7 +63,6 @@ public class Customers extends BaseFragment implements ISyncStatusObserverListen public static final String EXTRA_KEY_TYPE = "extra_key_type"; private View mView; private String mCurFilter = null; - private ListView mPartnersList = null; private OCursorListAdapter mAdapter = null; private boolean syncRequested = false; @@ -87,7 +86,7 @@ public void onViewCreated(View view, Bundle savedInstanceState) { setHasSwipeRefreshView(view, R.id.swipe_container, this); mView = view; mType = Type.valueOf(getArguments().getString(EXTRA_KEY_TYPE)); - mPartnersList = (ListView) view.findViewById(R.id.listview); + ListView mPartnersList = (ListView) view.findViewById(R.id.listview); mAdapter = new OCursorListAdapter(getActivity(), null, R.layout.customer_row_item); mAdapter.setOnViewBindListener(this); mAdapter.setHasSectionIndexers(true, "name"); @@ -96,6 +95,7 @@ public void onViewCreated(View view, Bundle savedInstanceState) { mPartnersList.setOnItemClickListener(this); setHasFloatingButton(view, R.id.fabButton, mPartnersList, this); getLoaderManager().initLoader(0, null, this); + } @Override diff --git a/app/src/main/java/com/odoo/base/addons/res/ResPartner.java b/app/src/main/java/com/odoo/base/addons/res/ResPartner.java index af47e4ce..a579d2f1 100644 --- a/app/src/main/java/com/odoo/base/addons/res/ResPartner.java +++ b/app/src/main/java/com/odoo/base/addons/res/ResPartner.java @@ -64,6 +64,12 @@ public class ResPartner extends OModel { .setLocalColumn(); OColumn large_image = new OColumn("Image", OBlob.class).setDefaultValue("false").setLocalColumn(); + OColumn category_id = new OColumn("Tags", ResPartnerCategory.class, + OColumn.RelationType.ManyToMany); + + OColumn child_ids = new OColumn("Contacts", ResPartner.class, OColumn.RelationType.OneToMany) + .setRelatedColumn("parent_id"); + public ResPartner(Context context, OUser user) { super(context, "res.partner", user); setHasMailChatter(true); diff --git a/app/src/main/java/com/odoo/base/addons/res/ResPartnerCategory.java b/app/src/main/java/com/odoo/base/addons/res/ResPartnerCategory.java new file mode 100644 index 00000000..f9463e0c --- /dev/null +++ b/app/src/main/java/com/odoo/base/addons/res/ResPartnerCategory.java @@ -0,0 +1,17 @@ +package com.odoo.base.addons.res; + +import android.content.Context; + +import com.odoo.core.orm.OModel; +import com.odoo.core.orm.fields.OColumn; +import com.odoo.core.orm.fields.types.OVarchar; +import com.odoo.core.support.OUser; + +public class ResPartnerCategory extends OModel { + + OColumn name = new OColumn("Name", OVarchar.class); + + public ResPartnerCategory(Context context, OUser user) { + super(context, "res.partner.category", user); + } +} diff --git a/app/src/main/java/com/odoo/core/account/OdooAccountQuickManage.java b/app/src/main/java/com/odoo/core/account/OdooAccountQuickManage.java index 7f2e1584..f76b7194 100644 --- a/app/src/main/java/com/odoo/core/account/OdooAccountQuickManage.java +++ b/app/src/main/java/com/odoo/core/account/OdooAccountQuickManage.java @@ -1,20 +1,20 @@ /** * Odoo, Open Source Management Solution * Copyright (C) 2012-today Odoo SA () - * + *

* 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 - * + *

* Created on 16/2/15 12:52 PM */ package com.odoo.core.account; @@ -27,7 +27,6 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; -import android.support.v7.app.ActionBarActivity; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.view.View; @@ -43,7 +42,6 @@ import com.odoo.core.service.OSyncAdapter; import com.odoo.core.utils.BitmapUtils; import com.odoo.core.utils.OResource; -import com.odoo.core.utils.notification.ONotificationBuilder; import odoo.Odoo; import odoo.helper.OUser; @@ -66,8 +64,6 @@ protected void onCreate(Bundle savedInstanceState) { getSupportActionBar().hide(); action = getIntent().getAction(); mApp = (App) getApplicationContext(); - // Removing notification - ONotificationBuilder.cancelNotification(this, OSyncAdapter.REQUEST_SIGN_IN_ERROR); user = OdooAccountManager.getDetails(this, getIntent().getStringExtra("android_name")); if (action.equals("remove_account")) { findViewById(R.id.layoutSavePassword).setVisibility(View.GONE); @@ -131,7 +127,6 @@ public void onClick(DialogInterface dialog, int which) { @Override public void onClick(View v) { - ONotificationBuilder.cancelNotification(this, OSyncAdapter.REQUEST_SIGN_IN_ERROR); switch (v.getId()) { case R.id.cancel: finish(); diff --git a/app/src/main/java/com/odoo/core/account/OdooLogin.java b/app/src/main/java/com/odoo/core/account/OdooLogin.java index 2b564a19..5dd37c52 100644 --- a/app/src/main/java/com/odoo/core/account/OdooLogin.java +++ b/app/src/main/java/com/odoo/core/account/OdooLogin.java @@ -54,14 +54,12 @@ public class OdooLogin extends AppCompatActivity implements View.OnClickListener private Boolean mConnectedToServer = false; private Boolean mAutoLogin = false; private Boolean mRequestedForAccount = false; - private AccountCreater accountCreator = null; + private AccountCreator accountCreator = null; private Spinner databaseSpinner = null; private List databases = new ArrayList<>(); private TextView mLoginProcessStatus = null; - private TextView mTermsCondition; private App mApp; private Odoo mOdoo; - private odoo.helper.OUser mUser; @Override protected void onCreate(Bundle savedInstanceState) { @@ -90,7 +88,7 @@ protected void onCreate(Bundle savedInstanceState) { private void init() { mLoginProcessStatus = (TextView) findViewById(R.id.login_process_status); - mTermsCondition = (TextView) findViewById(R.id.termsCondition); + TextView mTermsCondition = (TextView) findViewById(R.id.termsCondition); mTermsCondition.setMovementMethod(LinkMovementMethod.getInstance()); findViewById(R.id.btnLogin).setOnClickListener(this); findViewById(R.id.forgot_password).setOnClickListener(this); @@ -214,6 +212,8 @@ private void loginUser() { } if (databaseSpinner != null && databases.size() > 1 && databaseSpinner.getSelectedItemPosition() == 0) { Toast.makeText(this, OResource.string(this, R.string.label_select_database), Toast.LENGTH_LONG).show(); + findViewById(R.id.controls).setVisibility(View.VISIBLE); + findViewById(R.id.login_progress).setVisibility(View.GONE); return; } @@ -243,7 +243,6 @@ private void loginUser() { loginProcess(null, serverURL, databaseName); } else { mAutoLogin = true; - Log.v("", "Testing URL: " + serverURL); try { Odoo.createInstance(OdooLogin.this, serverURL).setOnConnect(OdooLogin.this); } catch (OdooVersionException e) { @@ -347,7 +346,6 @@ private void loginProcess(final OdooInstance instance, String url, final String @Override public void onLoginSuccess(Odoo odoo, odoo.helper.OUser oUser) { mOdoo = odoo; - mUser = oUser; mOdoo.getSaasInstances(new IOdooInstanceListener() { @Override public void onInstancesLoad(List odooInstances) { @@ -409,7 +407,7 @@ public void onLoginSuccess(Odoo odoo, odoo.helper.OUser oUser) { if (accountCreator != null) { accountCreator.cancel(true); } - accountCreator = new AccountCreater(); + accountCreator = new AccountCreator(); OUser user = new OUser(); user.setFromBundle(oUser.getAsBundle()); accountCreator.execute(user); @@ -426,7 +424,7 @@ private void loginFail(OdooError error) { edtUsername.setError(OResource.string(this, R.string.error_invalid_username_or_password)); } - private class AccountCreater extends AsyncTask { + private class AccountCreator extends AsyncTask { private OUser mUser; diff --git a/app/src/main/java/com/odoo/core/auth/OdooAccountManager.java b/app/src/main/java/com/odoo/core/auth/OdooAccountManager.java index f96814b9..1831de36 100644 --- a/app/src/main/java/com/odoo/core/auth/OdooAccountManager.java +++ b/app/src/main/java/com/odoo/core/auth/OdooAccountManager.java @@ -1,20 +1,20 @@ /** * Odoo, Open Source Management Solution * Copyright (C) 2012-today Odoo SA () - *

+ *

* 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 - *

+ *

* Created on 17/12/14 6:21 PM */ package com.odoo.core.auth; @@ -46,7 +46,7 @@ public class OdooAccountManager { * @return List of OUser instances if any */ public static List getAllAccounts(Context context) { - List users = new ArrayList(); + List users = new ArrayList<>(); AccountManager aManager = AccountManager.get(context); for (Account account : aManager.getAccountsByType(KEY_ACCOUNT_TYPE)) { OUser user = new OUser(); diff --git a/app/src/main/java/com/odoo/core/auth/OdooAuthenticator.java b/app/src/main/java/com/odoo/core/auth/OdooAuthenticator.java index b5ce7860..a83408b6 100644 --- a/app/src/main/java/com/odoo/core/auth/OdooAuthenticator.java +++ b/app/src/main/java/com/odoo/core/auth/OdooAuthenticator.java @@ -1,20 +1,20 @@ /** * Odoo, Open Source Management Solution * Copyright (C) 2012-today Odoo SA () - * + *

* 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 - * + *

* Created on 17/12/14 6:21 PM */ package com.odoo.core.auth; @@ -29,6 +29,7 @@ import android.os.Bundle; import android.support.annotation.NonNull; +import com.odoo.App; import com.odoo.core.account.OdooLogin; import com.odoo.core.orm.OSQLite; import com.odoo.core.support.OUser; @@ -66,11 +67,12 @@ public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response, Ac && !result.containsKey(AccountManager.KEY_INTENT)) { final boolean removalAllowed = result .getBoolean(AccountManager.KEY_BOOLEAN_RESULT); - if (removalAllowed) { OUser user = OdooAccountManager.getDetails(mContext, account.name); OSQLite sqLite = new OSQLite(mContext, user); sqLite.dropDatabase(); + App app = (App) mContext.getApplicationContext(); + app.setOdoo(null, user); } } return result; diff --git a/app/src/main/java/com/odoo/core/orm/OModel.java b/app/src/main/java/com/odoo/core/orm/OModel.java index 5c15c8f5..86d9a70f 100644 --- a/app/src/main/java/com/odoo/core/orm/OModel.java +++ b/app/src/main/java/com/odoo/core/orm/OModel.java @@ -19,13 +19,13 @@ */ package com.odoo.core.orm; -import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.SyncResult; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; +import android.text.TextUtils; import android.util.Log; import com.odoo.App; @@ -53,7 +53,6 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.io.InvalidObjectException; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -77,8 +76,6 @@ public class OModel implements ISyncServiceListener { public static final String TAG = OModel.class.getSimpleName(); public String BASE_AUTHORITY = App.APPLICATION_ID + ".core.provider.content"; - public static final String KEY_UPDATE_IDS = "key_update_ids"; - public static final String KEY_INSERT_IDS = "key_insert_ids"; public static final int INVALID_ROW_ID = -1; public static OSQLite sqLite = null; private Context mContext; @@ -93,21 +90,6 @@ public class OModel implements ISyncServiceListener { public static OModelRegistry modelRegistry = new OModelRegistry(); private boolean hasMailChatter = false; - // Relation record command - public enum Command { - Add(0), Update(1), Delete(2), Replace(6); - - int type; - - Command(int type) { - this.type = type; - } - - public int getValue() { - return type; - } - } - // Base Columns OColumn id = new OColumn("ID", OInteger.class).setDefaultValue(0); @Odoo.api.v8 @@ -447,7 +429,7 @@ public String getModelName() { } public List getManyToManyColumns(OColumn column, OModel relation_model) { - List cols = new ArrayList(); + List cols = new ArrayList<>(); _write_date.setName("_write_date"); cols.add(_write_date); _is_dirty.setName("_is_dirty"); @@ -712,7 +694,7 @@ public List select(String[] projection, String where, String[] args, S } while (cr.moveToNext()); } } finally { - cr.close(); + if (cr != null) cr.close(); } return rows; } @@ -722,7 +704,7 @@ public Object getFunctionalMethodValue(OColumn column, Object record) { Method method = column.getFunctionalMethod(); OModel model = this; try { - return method.invoke(model, new Object[]{record}); + return method.invoke(model, record); } catch (Exception e) { e.printStackTrace(); } @@ -734,7 +716,7 @@ public Object getOnChangeMethodValue(OColumn column, Object record) { Method method = column.getOnChangeMethod(); OModel model = this; try { - return method.invoke(model, new Object[]{record}); + return method.invoke(model, record); } catch (Exception e) { e.printStackTrace(); } @@ -885,14 +867,14 @@ public boolean delete(int row_id) { public boolean delete(int row_id, boolean permanently) { int count = 0; if (permanently) - count = mContext.getContentResolver().delete(uri().withAppendedPath(uri(), row_id + ""), null, null); + count = mContext.getContentResolver().delete(Uri.withAppendedPath(uri(), row_id + ""), null, null); else { OValues values = new OValues(); values.put("_is_active", "false"); update(row_id, values); count++; } - return (count > 0) ? true : false; + return count > 0; } public int update(String selection, String[] args, OValues values) { @@ -900,9 +882,9 @@ public int update(String selection, String[] args, OValues values) { } public boolean update(int row_id, OValues values) { - int count = mContext.getContentResolver().update(uri().withAppendedPath(uri(), row_id + ""), + int count = mContext.getContentResolver().update(Uri.withAppendedPath(uri(), row_id + ""), values.toContentValues(), null, null); - return (count > 0) ? true : false; + return count > 0; } @@ -941,56 +923,141 @@ public int count(String selection, String[] args) { } - public void storeManyToManyRecord(String column_name, int row_id, List relationIds, - Command command) - throws InvalidObjectException { - OColumn column = getColumn(column_name); - if (column != null) { - OModel rel_model = createInstance(column.getType()); - String table = getTableName() + "_" + rel_model.getTableName() + "_rel"; - String base_column = getTableName() + "_id"; - String rel_column = rel_model.getTableName() + "_id"; + /** + * Handle record values for insert, update, delete operation with relation columns + * Each record have different behaviour, appending, deleting, unlink reference and + * replacing with new list + * + * @param record_id Main record id on which relation values affected + * @param column column object of the record (for relation column only) + * @param values values list with commands (@see RelCommands) + */ + public void handleRelationValues(int record_id, OColumn column, RelValues values) { + OModel relModel = createInstance(column.getType()); + HashMap> columnValues = values.getColumnValues(); + for (RelCommands commands : columnValues.keySet()) { + switch (column.getRelationType()) { + case OneToMany: + handleOneToManyRecords(column, commands, relModel, record_id, columnValues); + break; + case ManyToMany: + handleManyToManyRecords(column, commands, relModel, record_id, columnValues); + break; + } + } + } - SQLiteDatabase db = getWritableDatabase(); - try { - switch (command) { - case Add: - if (relationIds.size() > 0) { - for (int id : relationIds) { - ContentValues values = new ContentValues(); - values.put(base_column, row_id); - values.put(rel_column, id); - values.put("_write_date", ODateUtils.getDate()); - db.insert(table, null, values); - } - } - break; - case Update: - break; - case Delete: - // Deleting records to relation model - if (relationIds.size() > 0) { - for (int id : relationIds) { - db.delete(table, base_column + " = ? AND " + rel_column - + " = ?", new String[]{row_id + "", id + ""}); - } - } - break; - case Replace: - // Removing old entries - db.delete(table, base_column + " = ?", new String[]{row_id + ""}); - // Creating new entries - storeManyToManyRecord(column_name, row_id, relationIds, Command.Add); - break; - } - } finally { - db.close(); - rel_model.close(); + private void handleOneToManyRecords(OColumn column, RelCommands commands, OModel relModel, + int record_id, HashMap> values) { + if (commands == RelCommands.Replace) { + // Force to unlink record even no any other record values available. + OValues old_values = new OValues(); + old_values.put(column.getRelatedColumn(), 0); + int count = relModel.update(column.getRelatedColumn() + " = ?", new String[]{record_id + ""}, + old_values); + Log.i(TAG, String.format("#%d references removed " + relModel.getModelName(), count)); + } + for (Object rowObj : values.get(commands)) { + switch (commands) { + case Append: + OValues value; + if (rowObj instanceof OValues) { + value = (OValues) rowObj; + value.put(column.getRelatedColumn(), record_id); + relModel.insert(value); + } else { + int rec_id = (int) rowObj; + value = new OValues(); + value.put(column.getRelatedColumn(), record_id); + relModel.update(rec_id, value); + } + break; + case Replace: + if (rowObj instanceof OValues) { + value = (OValues) rowObj; + value.put(column.getRelatedColumn(), record_id); + relModel.insert(value); + } else { + int rec_id = (int) rowObj; + value = new OValues(); + value.put(column.getRelatedColumn(), record_id); + relModel.update(rec_id, value); + } + break; + case Delete: + relModel.delete((int) rowObj); + break; + case Unlink: + // Removing all older references + OValues old_update = new OValues(); + old_update.put(column.getRelatedColumn(), 0); + relModel.update((int) rowObj, old_update); + break; } - } else { - throw new InvalidObjectException("Column [" + column_name + "] not found in " + getModelName() + " model."); + } + } + private void handleManyToManyRecords(OColumn column, RelCommands command, OModel relModel, + int record_id, HashMap> values) { + + String table = column.getRelTableName() != null ? column.getRelTableName() : + getTableName() + "_" + relModel.getTableName() + "_rel"; + String base_column = column.getRelBaseColumn() != null ? column.getRelBaseColumn() : + getTableName() + "_id"; + String rel_column = column.getRelRelationColumn() != null ? column.getRelRelationColumn() : + relModel.getTableName() + "_id"; + SQLiteDatabase db = getWritableDatabase(); + switch (command) { + case Append: + // Inserting each relation id with base record id to relation table + List append_items = values.get(command); + StringBuilder sql = new StringBuilder("INSERT INTO ").append(table) + .append(" (") + .append(base_column).append(", ") + .append(rel_column) + .append(", _write_date )").append(" VALUES "); + for (Object obj : append_items) { + int id; + if (obj instanceof OValues) { + Log.d(TAG, "creating quick record for many to many "); + id = relModel.insert((OValues) obj); + } else id = (int) obj; + sql.append(" (").append(record_id).append(",") + .append(id).append(", ") + .append("'").append(ODateUtils.getUTCDate()).append("'), "); + } + String statement = sql.substring(0, sql.length() - 2); + db.execSQL(statement); + break; + case Replace: + List ids = values.get(command); + // Unlink records + values.put(RelCommands.Unlink, ids); + handleManyToManyRecords(column, RelCommands.Unlink, relModel, record_id, values); + + // Appending record in relation with base record + values.put(RelCommands.Append, ids); + handleManyToManyRecords(column, RelCommands.Append, relModel, record_id, values); + break; + case Delete: + // Unlink relation with base record and removing relation records + values.put(RelCommands.Unlink, values.get(command)); + handleManyToManyRecords(column, RelCommands.Unlink, relModel, record_id, values); + + // Deleting master record from relation model with given ids + String deleteSql = "DELETE FROM " + relModel.getTableName() + " WHERE " + OColumn.ROW_ID + " IN (" + + TextUtils.join(",", values.get(command)) + ")"; + db.execSQL(deleteSql); + break; + case Unlink: + // Unlink relation with base record + String unlinkSQL = "DELETE FROM " + table + " WHERE " + base_column + " = " + record_id + " AND " + rel_column + " IN (" + + TextUtils.join(",", values.get(command)) + ")"; + db.execSQL(unlinkSQL); + break; } + values.remove(command); + db.close(); } public List selectManyToManyRecords(String[] projection, String column_name, int row_id) { diff --git a/app/src/main/java/com/odoo/core/orm/OValues.java b/app/src/main/java/com/odoo/core/orm/OValues.java index 780653f2..4e4b35f2 100644 --- a/app/src/main/java/com/odoo/core/orm/OValues.java +++ b/app/src/main/java/com/odoo/core/orm/OValues.java @@ -1,43 +1,56 @@ /** * Odoo, Open Source Management Solution * Copyright (C) 2012-today Odoo SA () - * + *

* 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 - * + *

* Created on 31/12/14 6:49 PM */ package com.odoo.core.orm; import android.content.ContentValues; +import com.odoo.core.utils.OObjectUtils; + +import java.io.IOException; +import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -public class OValues { +public class OValues implements Serializable { public static final String TAG = OValues.class.getSimpleName(); - private HashMap _values = new HashMap(); + private HashMap _values = new HashMap<>(); public OValues() { _values.clear(); - _values = new HashMap(); + _values = new HashMap<>(); } public void put(String key, Object value) { _values.put(key, value); } + /** + * Used for adding chained records (ids or OValues) for relation column M2M and O2M + * + * @param values Relation records + */ + public void put(String key, RelValues values) { + _values.put(key, values); + } + public Object get(String key) { return _values.get(key); } @@ -98,8 +111,26 @@ public ContentValues toContentValues() { ContentValues values = new ContentValues(); for (String key : _values.keySet()) { Object val = _values.get(key); - val = (val == null) ? "false" : val; - values.put(key, val.toString()); + if (val instanceof ArrayList || val instanceof List) { + // Contains all the ids list so replacing with all ids. + List ids = (List) val; + val = new RelValues().replace(ids.toArray(new Object[ids.size()])); + } + if (val instanceof OValues || val instanceof RelValues) { + // Converting values to byte so we can pass it to ContentValues + try { + // Possible values: record (M2O) or list of records (M2M, O2M). + val = OObjectUtils.objectToByte(val); + values.put(key, (byte[]) val); + } catch (IOException e) { + e.printStackTrace(); + values.put(key, "false"); + } + } else if (val instanceof byte[]) { + values.put(key, (byte[]) val); + } else { + values.put(key, val.toString()); + } } return values; } @@ -115,4 +146,5 @@ public static OValues from(ContentValues contentValues) { } return values; } + } diff --git a/app/src/main/java/com/odoo/core/orm/RelCommands.java b/app/src/main/java/com/odoo/core/orm/RelCommands.java new file mode 100644 index 00000000..fa9d8fd3 --- /dev/null +++ b/app/src/main/java/com/odoo/core/orm/RelCommands.java @@ -0,0 +1,8 @@ +package com.odoo.core.orm; + +/** + * Created by dpr on 5/4/16. + */ +public enum RelCommands { + Replace, Append, Delete, Unlink +} diff --git a/app/src/main/java/com/odoo/core/orm/RelValues.java b/app/src/main/java/com/odoo/core/orm/RelValues.java new file mode 100644 index 00000000..73f69d42 --- /dev/null +++ b/app/src/main/java/com/odoo/core/orm/RelValues.java @@ -0,0 +1,69 @@ +package com.odoo.core.orm; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +public class RelValues implements Serializable { + + private HashMap> columnValues = new HashMap<>(); + + public RelValues append(List values) { + return append(values.toArray(new Object[values.size()])); + } + + public RelValues append(Object... values) { + return manageRecord(RelCommands.Append, values); + } + + public RelValues replace(List values) { + return replace(values.toArray(new Object[values.size()])); + } + + public RelValues replace(Object... values) { + return manageRecord(RelCommands.Replace, values); + } + + public RelValues delete(List values) { + return delete(values.toArray(new Integer[values.size()])); + } + + public RelValues delete(Integer... values) { + List ids = new ArrayList<>(); + ids.addAll(Arrays.asList(values)); + return manageRecord(RelCommands.Delete, ids.toArray(new Object[ids.size()])); + } + + public RelValues unlink(List values) { + return unlink(values.toArray(new Integer[values.size()])); + } + + public RelValues unlink(Integer... values) { + List ids = new ArrayList<>(); + ids.addAll(Arrays.asList(values)); + return manageRecord(RelCommands.Unlink, ids.toArray(new Object[ids.size()])); + } + + private RelValues manageRecord(RelCommands command, Object... values) { + if (columnValues.containsKey(command)) { + List items = new ArrayList<>(); + items.addAll(columnValues.get(command)); + items.addAll(Arrays.asList(values)); + columnValues.put(command, items); + } else { + columnValues.put(command, Arrays.asList(values)); + } + return this; + } + + public HashMap> getColumnValues() { + return columnValues; + } + + @Override + public String toString() { + return columnValues + ""; + } +} diff --git a/app/src/main/java/com/odoo/core/orm/provider/BaseModelProvider.java b/app/src/main/java/com/odoo/core/orm/provider/BaseModelProvider.java index a1e9d74c..c1f5285f 100644 --- a/app/src/main/java/com/odoo/core/orm/provider/BaseModelProvider.java +++ b/app/src/main/java/com/odoo/core/orm/provider/BaseModelProvider.java @@ -1,20 +1,20 @@ /** * Odoo, Open Source Management Solution * Copyright (C) 2012-today Odoo SA () - * + *

* 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 - * + *

* Created on 31/12/14 6:54 PM */ package com.odoo.core.orm.provider; @@ -31,12 +31,13 @@ import com.odoo.core.auth.OdooAccountManager; import com.odoo.core.orm.OModel; import com.odoo.core.orm.OValues; +import com.odoo.core.orm.RelValues; import com.odoo.core.orm.fields.OColumn; import com.odoo.core.support.OUser; import com.odoo.core.utils.ODateUtils; -import com.odoo.core.utils.OdooRecordUtils; +import com.odoo.core.utils.OObjectUtils; -import java.io.InvalidObjectException; +import java.io.IOException; import java.util.Arrays; import java.util.HashSet; @@ -117,8 +118,8 @@ public Cursor query(Uri uri, String[] base_projection, String selection, String[ throw new UnsupportedOperationException("Unknown uri: " + uri); } Context ctx = getContext(); - assert ctx != null; - cr.setNotificationUri(ctx.getContentResolver(), uri); + if (cr != null && ctx != null) + cr.setNotificationUri(ctx.getContentResolver(), uri); return cr; } @@ -133,7 +134,7 @@ private String[] removeRelationColumns(String[] projection) { columns.add(key); } } - columns.addAll(Arrays.asList(new String[]{OColumn.ROW_ID, "id", "_is_active", "_write_date"})); + columns.addAll(Arrays.asList(OColumn.ROW_ID, "id", "_is_active", "_write_date")); return columns.toArray(new String[columns.size()]); } return null; @@ -159,14 +160,12 @@ public Uri insert(Uri uri, ContentValues all_values) { switch (match) { case COLLECTION: SQLiteDatabase db = mModel.getWritableDatabase(); - long new_id = 0; - new_id = db.insert(mModel.getTableName(), null, value_to_insert); + long 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 + ""); + return Uri.withAppendedPath(uri, new_id + ""); case SINGLE_ROW: throw new UnsupportedOperationException( "Insert not supported on URI: " + uri); @@ -250,10 +249,11 @@ private void storeUpdateRelationRecords(ContentValues values, String selection, int row_id = mModel.selectRowId(selection, args); for (String key : values.keySet()) { try { - mModel.storeManyToManyRecord(key, - row_id, OdooRecordUtils.toList(values.getAsString(key)), - OModel.Command.Replace); - } catch (InvalidObjectException e) { + OColumn column = mModel.getColumn(key); + RelValues relValues = (RelValues) OObjectUtils.byteToObject( + (byte[]) values.get(key)); + mModel.handleRelationValues(row_id, column, relValues); + } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } @@ -269,9 +269,22 @@ private ContentValues[] generateValues(ContentValues values) { data_value.put(key, values.get(key)); } else { if (column.getRelationType() == OColumn.RelationType.ManyToOne) { - data_value.put(key, values.get(key)); + if (!(values.get(key) instanceof byte[])) + data_value.put(key, values.get(key)); + else { + // Creating many to one record and assigning id to record + OModel m2oModel = mModel.createInstance(column.getType()); + try { + OValues m2oVal = (OValues) OObjectUtils.byteToObject( + (byte[]) values.get(key)); + int id = m2oModel.insert(m2oVal); + data_value.put(key, id); + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } + } } else { - rel_value.put(key, values.get(key).toString()); + rel_value.put(key, values.get(key)); } } } diff --git a/app/src/main/java/com/odoo/core/service/OSyncAdapter.java b/app/src/main/java/com/odoo/core/service/OSyncAdapter.java index 290db987..dda5f53a 100644 --- a/app/src/main/java/com/odoo/core/service/OSyncAdapter.java +++ b/app/src/main/java/com/odoo/core/service/OSyncAdapter.java @@ -32,7 +32,6 @@ import com.odoo.base.addons.ir.IrModel; import com.odoo.base.addons.res.ResCompany; import com.odoo.core.account.About; -import com.odoo.core.account.OdooAccountQuickManage; import com.odoo.core.auth.OdooAccountManager; import com.odoo.core.orm.ODataRow; import com.odoo.core.orm.OModel; @@ -44,7 +43,6 @@ import com.odoo.core.utils.OResource; import com.odoo.core.utils.OdooRecordUtils; import com.odoo.core.utils.logger.OLog; -import com.odoo.core.utils.notification.ONotificationBuilder; import java.util.ArrayList; import java.util.HashMap; @@ -60,20 +58,18 @@ public class OSyncAdapter extends AbstractThreadedSyncAdapter { public static final String TAG = OSyncAdapter.class.getSimpleName(); - public static final Integer REQUEST_SIGN_IN_ERROR = 11244; - public static final String KEY_AUTH_ERROR = "key_authentication_error"; private Context mContext; - private Class mModelClass; + private App app = null; + private Odoo mOdoo; private OModel mModel; private OSyncService mService; - private OUser mUser; - private Boolean checkForWriteCreateDate = true; + private OSyncDataUtils dataUtils; + private OPreferenceManager preferenceManager; + private Class mModelClass; private Integer mSyncDataLimit = 0; + private Boolean checkForWriteCreateDate = true; private HashMap mDomain = new HashMap<>(); - private OPreferenceManager preferenceManager; - private Odoo mOdoo; private HashMap mSyncFinishListeners = new HashMap<>(); - private App app = null; public OSyncAdapter(Context context, Class model, OSyncService service, boolean autoInitialize) { @@ -116,24 +112,29 @@ public void onPerformSync(Account account, Bundle extras, String authority, // Creating model Object mModel = new OModel(mContext, null, OdooAccountManager.getDetails(mContext, account.name)) .createInstance(mModelClass); - mUser = mModel.getUser(); + OUser mUser = mModel.getUser(); if (OdooAccountManager.isValidUserObj(mContext, mUser)) { // Creating Odoo instance mOdoo = createOdooInstance(mContext, mUser); - Log.i(TAG, "User : " + mModel.getUser().getAndroidName()); - Log.i(TAG, "Model : " + mModel.getModelName()); - Log.i(TAG, "Database : " + mModel.getDatabaseName()); - Log.i(TAG, "Odoo Version: " + mUser.getOdooVersion().getServerSerie()); - // Calling service callback - if (mService != null) - mService.performDataSync(this, extras, mUser); - - //Creating domain - ODomain domain = (mDomain.containsKey(mModel.getModelName())) ? - mDomain.get(mModel.getModelName()) : null; - - // Ready for sync data from server - syncData(mModel, mUser, domain, syncResult, true, true); + if (mOdoo != null) { + dataUtils = new OSyncDataUtils(mContext, mOdoo); + Log.i(TAG, "User : " + mModel.getUser().getAndroidName()); + Log.i(TAG, "Model : " + mModel.getModelName()); + Log.i(TAG, "Database : " + mModel.getDatabaseName()); + Log.i(TAG, "Odoo Version: " + mUser.getOdooVersion().getServerSerie()); + // Calling service callback + if (mService != null) + mService.performDataSync(this, extras, mUser); + + //Creating domain + ODomain domain = (mDomain.containsKey(mModel.getModelName())) ? + mDomain.get(mModel.getModelName()) : null; + + // Ready for sync data from server + syncData(mModel, mUser, domain, syncResult, true, true); + } else { + Log.e(TAG, "Unable to connect with Odoo Server."); + } } } @@ -178,6 +179,11 @@ private void syncData(OModel model, OUser user, ODomain domain_filter, // Getting data OdooResult response = mOdoo.searchRead(model.getModelName(), getFields(model) , domain, 0, mSyncDataLimit, "create_date DESC"); + if (response == null) { + // FIXME: Check in library. May be timeout issue with slow network. + Log.w(TAG, "Response null from server."); + return; + } if (response.containsKey("error")) { app.setOdoo(null, user); OPreferenceManager pref = new OPreferenceManager(mContext); @@ -189,8 +195,7 @@ private void syncData(OModel model, OUser user, ODomain domain_filter, } Log.v(TAG, "Processing " + response.getRecords().size() + " records"); - OSyncDataUtils dataUtils = new OSyncDataUtils(mContext, mOdoo, model, user, response, - result, createRelationRecords); + dataUtils.handleResult(model, user, result, response, createRelationRecords); // Updating records on server if local are latest updated. // if model allowed update record to server if (model.allowUpdateRecordOnServer()) { @@ -241,42 +246,6 @@ private void syncData(OModel model, OUser user, ODomain domain_filter, model.close(); } - private static void showSignInErrorNotification(Context context, OUser user) { - ONotificationBuilder builder = new ONotificationBuilder(context, - REQUEST_SIGN_IN_ERROR); - builder.setTitle("Odoo authentication problem"); - builder.setBigText("May be you have changed your account " + - "password or your account does not exists"); - builder.setIcon(R.drawable.ic_action_alert_warning); - builder.setText(user.getAndroidName()); - builder.allowVibrate(true); - builder.withRingTone(false); - builder.setOngoing(true); - builder.withLargeIcon(false); - builder.setColor(R.color.android_orange_dark); - Bundle extra = user.getAsBundle(); - // Actions - ONotificationBuilder.NotificationAction actionReset = new ONotificationBuilder.NotificationAction( - R.drawable.ic_action_refresh, - "Reset", - 110, - "reset_password", - OdooAccountQuickManage.class, - extra - ); - ONotificationBuilder.NotificationAction deleteAccount = new ONotificationBuilder.NotificationAction( - R.drawable.ic_action_navigation_close, - "Remove", - 111, - "remove_account", - OdooAccountQuickManage.class, - extra - ); - builder.addAction(actionReset); - builder.addAction(deleteAccount); - builder.build().show(); - } - private void handleRelationRecords(OUser user, HashMap relationRecords, SyncResult result) { @@ -285,9 +254,13 @@ private void handleRelationRecords(OUser user, OModel model = record.getBaseModel(); OModel rel_model = model.createInstance(record.getRelationModel()); model.close(); - ODomain domain = new ODomain(); - domain.add("id", "in", record.getUniqueIds()); - syncData(rel_model, user, domain, result, false, false); + + // Skipping blank sync request if there is no any ids to sync. + if (!record.getUniqueIds().isEmpty()) { + ODomain domain = new ODomain(); + domain.add("id", "in", record.getUniqueIds()); + syncData(rel_model, user, domain, result, true, false); + } // Updating manyToOne record with their relation record row_id switch (record.getRelationType()) { case ManyToOne: @@ -331,10 +304,7 @@ public static Odoo createOdooInstance(final Context context, final OUser user) { company.quickCreateRecord(company_details); } } else { - // FIXME: Need to check again. Not working properly - //showSignInErrorNotification(context, user); -// Toast.makeText(context, OResource.string(context, R.string.toast_something_gone_wrong), -// Toast.LENGTH_LONG).show(); + // FIXME: Unable to get user object or may be due session destroyed with Odoo Saas (single connection support only) Log.e(TAG, OResource.string(context, R.string.toast_something_gone_wrong)); } } @@ -406,7 +376,7 @@ public boolean validateRelationRecords(OModel model, ODataRow row) { case ManyToOne: if (!row.getString(column.getName()).equals("false")) { ODataRow m2oRec = row.getM2ORecord(column.getName()).browse(); - if (m2oRec.getInt("id") == 0) { + if (m2oRec != null && m2oRec.getInt("id") == 0) { int new_id = relModel.getServerDataHelper().createOnServer( OdooRecordUtils.createRecordValues(relModel, m2oRec)); updateRecordServerId(relModel, m2oRec.getInt(OColumn.ROW_ID), new_id); @@ -469,7 +439,6 @@ private int createOnServer(OModel model, ORecordValues values) { return id; } - /** * Removes record on server if local record is not active * diff --git a/app/src/main/java/com/odoo/core/service/OSyncDataUtils.java b/app/src/main/java/com/odoo/core/service/OSyncDataUtils.java index a61cd2c9..c000137e 100644 --- a/app/src/main/java/com/odoo/core/service/OSyncDataUtils.java +++ b/app/src/main/java/com/odoo/core/service/OSyncDataUtils.java @@ -52,20 +52,23 @@ public class OSyncDataUtils { private OUser mUser; private OdooResult response; private HashSet recordsId = new HashSet<>(); + private List recentSyncIds = new ArrayList<>(); private HashMap relationRecordsHashMap = new HashMap<>(); private Odoo mOdoo; private SyncResult mResult; private HashMap> updateToServerRecords = new HashMap<>(); private Boolean mCreateRelationRecords = true; - public OSyncDataUtils(Context context, Odoo odoo, OModel model, OUser user, OdooResult response, - SyncResult result, Boolean createRelRecord) { + public OSyncDataUtils(Context context, Odoo odoo) { mContext = context; mOdoo = odoo; + } + + public void handleResult(OModel model, OUser user, SyncResult result, OdooResult response, Boolean createRelRecord) { mModel = model; mUser = user; - this.response = response; mResult = result; + this.response = response; mCreateRelationRecords = createRelRecord; List updateInLocal = checkLocalUpdatedRecords(); handleResult(updateInLocal); @@ -92,11 +95,9 @@ private List checkLocalUpdatedRecords() { } // getting local dirty records if server records length = 0 - if (records.size() <= 0) { - for (ODataRow row : mModel.select(new String[]{}, "_is_dirty = ? and _is_active = ? and id != ?", - new String[]{"true", "true", "0"})) { - serverIds.add(row.getInt("id")); - } + for (ODataRow row : mModel.select(new String[]{}, "_is_dirty = ? and _is_active = ? and id != ?", + new String[]{"true", "true", "0"})) { + serverIds.add(row.getInt("id")); } // Comparing dirty (updated) record List updateToServerIds = new ArrayList<>(); @@ -167,120 +168,128 @@ private void handleResult(List records) { List columns = mModel.getColumns(false); columns.addAll(mModel.getFunctionalColumns()); for (OdooRecord record : records) { - OValues values = new OValues(); - recordsId.add(mModel.getModelName() + "_" + record.getInt("id")); - for (OColumn column : columns) { - String name = column.getSyncColumn(); - String lName = column.getName(); - if (column.getRelationType() == null) { - // checks for functional store fields - if (column.isFunctionalColumn() && column.canFunctionalStore()) { - List depends = column.getFunctionalStoreDepends(); - OValues dependValues = new OValues(); - if (!column.isLocal()) - dependValues.put(column.getName(), record.get(column.getName())); - for (String depend : depends) { - if (record.containsKey(depend)) { - dependValues.put(depend, record.get(depend)); + if (!recentSyncIds.contains(mModel.getModelName() + ":" + record.getInt("id"))) { + OValues values = new OValues(); + recordsId.add(mModel.getModelName() + "_" + record.getInt("id")); + for (OColumn column : columns) { + String name = column.getSyncColumn(); + String lName = column.getName(); + if (column.getRelationType() == null) { + // checks for functional store fields + if (column.isFunctionalColumn() && column.canFunctionalStore()) { + List depends = column.getFunctionalStoreDepends(); + OValues dependValues = new OValues(); + if (!column.isLocal()) + dependValues.put(column.getName(), record.get(column.getName())); + for (String depend : depends) { + if (record.containsKey(depend)) { + dependValues.put(depend, record.get(depend)); + } } + Object value = mModel.getFunctionalMethodValue(column, dependValues); + values.put(lName, value); + } else { + // Normal Columns + values.put(lName, record.get(name)); } - Object value = mModel.getFunctionalMethodValue(column, dependValues); - values.put(lName, value); } else { - // Normal Columns - values.put(lName, record.get(name)); - } - } else { - // Relation Columns - if (!record.getString(name).equals("false")) { - switch (column.getRelationType()) { - case ManyToOne: - OdooRecord m2oData = record.getM20(name); - OModel m2o_model = mModel.createInstance(column.getType()); - String recKey = m2o_model.getModelName() + "_" + m2oData.getInt("id"); - int m2oRowId; - if (!recordsId.contains(recKey)) { - OValues m2oValue = new OValues(); - m2oValue.put("id", m2oData.getInt("id")); - m2oValue.put(m2o_model.getDefaultNameColumn(), m2oData.getString("name")); - m2oValue.put("_is_dirty", "false"); - m2oRowId = m2o_model.insertOrUpdate(m2oData.getInt("id"), - m2oValue); - } else { - m2oRowId = m2o_model.selectRowId(m2oData.getInt("id")); - } - - values.put(lName, m2oRowId); - if (mCreateRelationRecords) { - // Add id to sync if model contains more than (id,name) columns - if (m2o_model.getColumns(false).size() > 2 - || (m2o_model.getColumns(false).size() > 4 - && mModel.getOdooVersion().getVersionNumber() > 7)) { - List m2oIds = new ArrayList<>(); - m2oIds.add(m2oData.getInt("id")); - addUpdateRelationRecord(mModel, m2o_model.getTableName(), - column.getType(), name, null, - column.getRelationType(), m2oIds); - } - } - m2o_model.close(); - break; - case ManyToMany: - OModel m2mModel = mModel.createInstance(column.getType()); - List m2mIds = OListUtils.doubleToIntList(record.getM2M(name)); - if (mCreateRelationRecords) { - addUpdateRelationRecord(mModel, m2mModel.getTableName(), column.getType(), - name, null, column.getRelationType(), - (column.getRecordSyncLimit() > 0) ? - m2mIds.subList(0, column.getRecordSyncLimit()) : m2mIds); - } - List m2mRowIds = new ArrayList<>(); - for (Integer id : m2mIds) { - recKey = m2mModel.getModelName() + "_" + id; - int r_id; + // Relation Columns + if (!record.getString(name).equals("false")) { + switch (column.getRelationType()) { + case ManyToOne: + OdooRecord m2oData = record.getM20(name); + OModel m2o_model = mModel.createInstance(column.getType()); + String recKey = m2o_model.getModelName() + "_" + m2oData.getInt("id"); + int m2oRowId; if (!recordsId.contains(recKey)) { - OValues m2mValues = new OValues(); - m2mValues.put("id", id); - m2mValues.put("_is_dirty", "false"); - r_id = m2mModel.insertOrUpdate(id, m2mValues); + OValues m2oValue = new OValues(); + m2oValue.put("id", m2oData.getInt("id")); + m2oValue.put(m2o_model.getDefaultNameColumn(), m2oData.getString("name")); + m2oValue.put("_is_dirty", "false"); + m2oRowId = m2o_model.insertOrUpdate(m2oData.getInt("id"), + m2oValue); } else { - r_id = m2mModel.selectRowId(id); + m2oRowId = m2o_model.selectRowId(m2oData.getInt("id")); } - m2mRowIds.add(r_id); - } - if (m2mRowIds.size() > 0) { - // Putting many to many related ids - // (generated _id for each of server ids) - values.put(lName, m2mRowIds); - } - m2mModel.close(); - break; - case OneToMany: - if (mCreateRelationRecords) { - OModel o2mModel = mModel.createInstance(column.getType()); - List o2mIds = OListUtils.doubleToIntList(record.getO2M(name)); - addUpdateRelationRecord(mModel, o2mModel.getTableName(), - column.getType(), name, column.getRelatedColumn(), - column.getRelationType(), - (column.getRecordSyncLimit() > 0) ? - o2mIds.subList(0, column.getRecordSyncLimit()) : o2mIds); - o2mModel.close(); - } - break; + + values.put(lName, m2oRowId); + if (mCreateRelationRecords) { + // Add id to sync if model contains more than (id,name) columns + if (m2o_model.getColumns(false).size() > 2 + || (m2o_model.getColumns(false).size() > 4 + && mModel.getOdooVersion().getVersionNumber() > 7)) { + List m2oIds = new ArrayList<>(); + m2oIds.add(m2oData.getInt("id")); + addUpdateRelationRecord(mModel, m2o_model.getTableName(), + column.getType(), name, null, + column.getRelationType(), m2oIds); + } + } + m2o_model.close(); + break; + case ManyToMany: + OModel m2mModel = mModel.createInstance(column.getType()); + List m2mIds = OListUtils.doubleToIntList(record.getM2M(name)); + if (mCreateRelationRecords) { + addUpdateRelationRecord(mModel, m2mModel.getTableName(), column.getType(), + name, null, column.getRelationType(), + (column.getRecordSyncLimit() > 0) ? + m2mIds.subList(0, column.getRecordSyncLimit()) : m2mIds); + } + List m2mRowIds = new ArrayList<>(); + for (Integer id : m2mIds) { + recKey = m2mModel.getModelName() + "_" + id; + int r_id; + if (!recordsId.contains(recKey)) { + OValues m2mValues = new OValues(); + m2mValues.put("id", id); + m2mValues.put("_is_dirty", "false"); + r_id = m2mModel.insertOrUpdate(id, m2mValues); + } else { + r_id = m2mModel.selectRowId(id); + } + m2mRowIds.add(r_id); + } + if (m2mRowIds.size() > 0) { + // Putting many to many related ids + // (generated _id for each of server ids) + values.put(lName, m2mRowIds); + } + m2mModel.close(); + break; + case OneToMany: + if (mCreateRelationRecords) { + OModel o2mModel = mModel.createInstance(column.getType()); + List o2mIds = OListUtils.doubleToIntList(record.getO2M(name)); + addUpdateRelationRecord(mModel, o2mModel.getTableName(), + column.getType(), name, column.getRelatedColumn(), + column.getRelationType(), + (column.getRecordSyncLimit() > 0) ? + o2mIds.subList(0, column.getRecordSyncLimit()) : o2mIds); + o2mModel.close(); + } + break; + } } } } + // Some default values + values.put("id", record.getInt("id")); + values.put("_write_date", ODateUtils.getUTCDate()); + values.put("_is_active", "true"); + values.put("_is_dirty", "false"); + mModel.insertOrUpdate(record.getInt("id"), values); + + // Fixed issue of multiple time sync same record. Performance improved + // Adding to recent sync list for avoiding duplicate process for record + recentSyncIds.add(mModel.getModelName() + ":" + record.getInt("id")); + mResult.stats.numEntries++; + counter++; } - // Some default values - values.put("id", record.getInt("id")); - values.put("_write_date", ODateUtils.getUTCDate()); - values.put("_is_active", "true"); - values.put("_is_dirty", "false"); - mModel.insertOrUpdate(record.getInt("id"), values); - counter++; } Log.i(TAG, counter + " records affected"); } catch (Exception e) { + mResult.stats.numParseExceptions++; e.printStackTrace(); } } diff --git a/app/src/main/java/com/odoo/core/support/addons/fragment/IBaseFragment.java b/app/src/main/java/com/odoo/core/support/addons/fragment/IBaseFragment.java index d29215a7..bd907ee4 100644 --- a/app/src/main/java/com/odoo/core/support/addons/fragment/IBaseFragment.java +++ b/app/src/main/java/com/odoo/core/support/addons/fragment/IBaseFragment.java @@ -1,20 +1,20 @@ /** * Odoo, Open Source Management Solution * Copyright (C) 2012-today Odoo SA () - * + *

* 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 - * + *

* Created on 30/12/14 3:30 PM */ package com.odoo.core.support.addons.fragment; diff --git a/app/src/main/java/com/odoo/core/utils/OObjectUtils.java b/app/src/main/java/com/odoo/core/utils/OObjectUtils.java new file mode 100644 index 00000000..76faff44 --- /dev/null +++ b/app/src/main/java/com/odoo/core/utils/OObjectUtils.java @@ -0,0 +1,23 @@ +package com.odoo.core.utils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public class OObjectUtils { + + public static byte[] objectToByte(Object obj) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutputStream os = new ObjectOutputStream(outputStream); + os.writeObject(obj); + return outputStream.toByteArray(); + } + + public static Object byteToObject(byte[] data) throws IOException, ClassNotFoundException { + ByteArrayInputStream inputStream = new ByteArrayInputStream(data); + ObjectInputStream is = new ObjectInputStream(inputStream); + return is.readObject(); + } +} diff --git a/app/src/main/java/com/odoo/core/utils/OdooRecordUtils.java b/app/src/main/java/com/odoo/core/utils/OdooRecordUtils.java index afa2dcb5..4e50920d 100644 --- a/app/src/main/java/com/odoo/core/utils/OdooRecordUtils.java +++ b/app/src/main/java/com/odoo/core/utils/OdooRecordUtils.java @@ -106,37 +106,38 @@ public static ORecordValues createRecordValues(OModel model, ODataRow row) { List o2mRecords = new ArrayList<>(); List o2mRecordList = row.getO2MRecord( col.getName()).browseEach(); + List rec_ids = new ArrayList<>(); if (o2mRecordList.size() > 0) { - List rec_ids = new ArrayList<>(); for (ODataRow o2mR : o2mRecordList) { if (o2mR.getInt("id") != 0) rec_ids.add(o2mR.getInt("id")); } - o2mRecords.add(6); - o2mRecords.add(false); - o2mRecords.add(rec_ids); - List oneToManyValue = new ArrayList<>(); - oneToManyValue.add(o2mRecords); - values.put(col.getSyncColumn(), oneToManyValue); } + o2mRecords.add(6); + o2mRecords.add(false); + o2mRecords.add(rec_ids); + List oneToManyValue = new ArrayList<>(); + oneToManyValue.add(o2mRecords); + values.put(col.getSyncColumn(), oneToManyValue); + break; case ManyToMany: List m2mRecords = new ArrayList<>(); List m2mRecordList = row.getM2MRecord( col.getName()).browseEach(); + rec_ids = new ArrayList<>(); if (!m2mRecordList.isEmpty()) { - List rec_ids = new ArrayList<>(); for (ODataRow o2mR : m2mRecordList) { if (o2mR.getInt("id") != 0) rec_ids.add(o2mR.getInt("id")); } - m2mRecords.add(6); - m2mRecords.add(false); - m2mRecords.add(rec_ids); - List manyToManyRecord = new ArrayList<>(); - manyToManyRecord.add(m2mRecords); - values.put(col.getSyncColumn(), manyToManyRecord); } + m2mRecords.add(6); + m2mRecords.add(false); + m2mRecords.add(rec_ids); + List manyToManyRecord = new ArrayList<>(); + manyToManyRecord.add(m2mRecords); + values.put(col.getSyncColumn(), manyToManyRecord); break; } } diff --git a/app/src/main/java/odoo/controls/OSelectionField.java b/app/src/main/java/odoo/controls/OSelectionField.java index 56736d34..eb10e01a 100644 --- a/app/src/main/java/odoo/controls/OSelectionField.java +++ b/app/src/main/java/odoo/controls/OSelectionField.java @@ -202,8 +202,12 @@ public void onClick(View v) { } catch (Exception e) { } - mContext.registerReceiver(valueReceiver, - new IntentFilter("searchable_value_select")); + try { + mContext.registerReceiver(valueReceiver, + new IntentFilter("searchable_value_select")); + } catch (Exception e) { + + } mContext.startActivity(intent); } }); diff --git a/build.gradle b/build.gradle index 1ef1f9e2..dff35ce7 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0' + classpath 'com.android.tools.build:gradle:2.1.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle.properties b/gradle.properties index 1f84c3be..ac6c93d2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,6 +17,7 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true #################################################################################################### +org.gradle.jvmargs=-Xmx2024M # Previnting developer to use same applicaiton id and application name checkForPackageName=true appId=com.odoo diff --git a/odoo_mobile.png b/odoo_mobile.png new file mode 100644 index 00000000..ab8506dd Binary files /dev/null and b/odoo_mobile.png differ