public class ProductSyncService extends IntentService {
    public static final String TAG = ProductSyncService.class.getSimpleName();
    public static final String KEY_RESULT_RECEIVER = "result_receiver";
    private App app;

    public ProductSyncService() {
        super(TAG);
        Log.i(TAG, "ProductSyncService()");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.i(TAG, "ProductSyncService START");
        app = (App) getApplicationContext();
        if (app.inNetwork()) {
            try {
                ProductProduct products = new ProductProduct(getApplicationContext());
                products.quickSyncRecords(null, true); // FIXME: What if there are more than 500 products ??
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
            }
        }
        this.stopSelf();
        Log.i(TAG, "ProductSyncService END");
    }
} public class GoogleLoginActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener, View.OnClickListener {

    private GoogleApiClient google;
    private boolean mIntentInProgress = false;
    private static final int RC_SIGN_IN = 143;
    private boolean mClearingAccounts = false;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.shop_google_login);
        setResult(RESULT_CANCELED);
        google = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(Plus.API)
                .addScope(Plus.SCOPE_PLUS_LOGIN)
                .build();
        findViewById(R.id.btnLoginWithGoogle).setOnClickListener(this);
        if (google != null) {
            google.connect();
            mClearingAccounts = true;
        }
    }

    @Override
    public void onConnected(Bundle bundle) {
        if (!mClearingAccounts) {
            String email = Plus.AccountApi.getAccountName(google);
            Person person = Plus.PeopleApi.getCurrentPerson(google);
            if (person != null) {
                String personName = person.getDisplayName();
                Person.Image avatar = person.getImage();
                String photoURL = "false";
                if (person.hasImage() && avatar.hasUrl()) {
                    photoURL = avatar.getUrl().split("\\?")[0];
                }
                OUser user = new OUser();
                user.setUsername(email);
                user.setPassword("odoo_mobile_shop_google_user");
                user.setName(personName);
                user.setAvatar(photoURL);
                user.setDatabase(OConstants.publicUser().getDatabase());
                new AccountCreator().execute(user);
            }
        } else {
            Plus.AccountApi.clearDefaultAccount(google);
            mClearingAccounts = false;
        }
    }

    private class AccountCreator extends AsyncTask<OUser, Void, Boolean> {

        private ProgressDialog progressDialog;
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            progressDialog = new ProgressDialog(GoogleLoginActivity.this);
            progressDialog.setCancelable(false);
            progressDialog.setMessage(OResource.string(GoogleLoginActivity.this, R.string.status_logging_in));
            progressDialog.show();
        }

        @Override
        protected Boolean doInBackground(OUser... params) {
            try {
                OUser user = params[0];
                String host = OConstants.publicUser().getHost();
                Odoo odoo = Odoo.createInstance(GoogleLoginActivity.this, host);
                String url = host + "/web/signup";
                HashMap<String, String> data = new HashMap<>();
                data.put("login", user.getUsername());
                data.put("password", user.getPassword());
                data.put("name", user.getName());
                data.put("confirm_password", user.getPassword());
                odoo.requestHTTPController(url, data);

                odoo.helper.OUser odooUser = odoo.authenticate(user.getUsername(),
                        user.getPassword(), user.getDatabase());
                user.fillFromBundle(odooUser.getAsBundle());
                return user.createNewAccount(GoogleLoginActivity.this);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }

        @Override
        protected void onPostExecute(Boolean result) {
            super.onPostExecute(result);
            progressDialog.dismiss();
            reLaunch(result);
        }
    }

    private void reLaunch(Boolean result) {
        setResult(result ? RESULT_OK : RESULT_CANCELED);
        finish();
    }

    @Override
    protected void onActivityResult(int requestCode, int responseCode, Intent intent) {
        if (!mClearingAccounts && requestCode == RC_SIGN_IN) {
            mIntentInProgress = false;
            if (responseCode == Activity.RESULT_OK) {
                if (!google.isConnected()) {
                    google.reconnect();
                }
            } else {
                hideProgress();
            }
        }
    }

    @Override
    public void onConnectionSuspended(int i) {
        google.connect();
        showProgress();
    }

    @Override
    protected void onStop() {
        super.onStop();
        google.disconnect();
    }

    @Override
    public void onConnectionFailed(ConnectionResult result) {
        if (!mClearingAccounts && !mIntentInProgress && result.hasResolution()) {
            try {
                mIntentInProgress = true;
                result.startResolutionForResult(this, RC_SIGN_IN);
            } catch (Exception e) {
                mIntentInProgress = false;
                google.connect();
                e.printStackTrace();
            }
        }
        if (!result.hasResolution()) {
            GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), this,
                    0).show();
        }
    }

    private void showProgress() {
        findViewById(R.id.progressBar).setVisibility(View.VISIBLE);
    }

    private void hideProgress() {
        findViewById(R.id.progressBar).setVisibility(View.INVISIBLE);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnLoginWithGoogle:
                mClearingAccounts = false;
                if (!google.isConnecting()) {
                    if (google.isConnected()) {
                        google.reconnect();
                    } else {
                        google.connect();
                    }
                    showProgress();
                }
                break;
        }
    }
} public class HomeScreen extends BaseFragment {

    private EasyRecyclerView layoutContainer = null;
    public static final int ITEM_CATEGORY_LIST = 1;
    public static final int ITEM_PRODUCTS_RECENT = 2;
    private ProductTemplate templates = null;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        setHasOptionsMenu(true);
        return inflater.inflate(R.layout.shop_home_screen, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar);
        toolbar.setNavigationIcon(R.drawable.ic_action_navigation_menu_dark);
        OAppBarUtils.setChildAppBar(parent(), toolbar, true);
        setTitle(_s(R.string.label_welcome));
        init(view);
    }

    private List<Object> getContainerItems() {
        List<Object> items = new ArrayList<>();
        items.add(ITEM_CATEGORY_LIST);
        items.add(ITEM_PRODUCTS_RECENT);
        return items;
    }

    private void init(View view) {
        templates = new ProductTemplate(getContext());
        final List<Object> rows = getContainerItems();
        layoutContainer = (EasyRecyclerView) view.findViewById(R.id.layoutContainer);
        layoutContainer.setLayout(R.layout.shop_recycler_container);
        layoutContainer.changeCursor(rows);
        layoutContainer.setOnViewBindListener(new EasyRecyclerViewAdapter.OnViewBindListener() {
            @Override
            public void onViewBind(int position, View view, Object data) {
                int item = (int) data;
                ViewGroup container = (ViewGroup) view;
                switch (item) {
                    case ITEM_CATEGORY_LIST:
                        container.addView(getCategoryItemView(container));
                        break;
                    case ITEM_PRODUCTS_RECENT:
                        container.addView(getRecentProductItemView(container));
                }
            }
        });

        view.findViewById(R.id.search_view).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MenuItem item = new ActionMenuItem(getContext(), 0, R.id.menu_search_product, 0, 0, null);
                OAppBarUtils.onShopMenuItemClick(parent(), HomeScreen.this, item);
            }
        });
    }

    private View getRecentProductItemView(ViewGroup container) {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.shop_home_recent_products, container, false);
        final List<Object> items = new ArrayList<>();
        final List<ODataRow> productTemplates = templates.select(new String[]{"name", "image_medium"}, null, null, "id DESC");
        int limit = (productTemplates.size() > 10) ? 10 : productTemplates.size();
        items.addAll(productTemplates.subList(0, limit));
        view.findViewById(R.id.allNewProducts).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startFragment(new Products(), true, null);
            }
        });
        ExpandableListControl listControl = (ExpandableListControl) view.findViewById(R.id.recentProductList);
        listControl.setExpandableListItemClickListener(new ExpandableListControl.ExpandableListItemClickListener() {
            @Override
            public void onItemViewClick(int position, View view) {
                ODataRow row = (ODataRow) items.get(position);
                startFragment(new ProductDetail(), true, row.getPrimaryBundleData());
            }
        });
        final ExpandableListControl.ExpandableListAdapter adapter = listControl.getAdapter(R.layout.shop_home_recent_products_item, items,
                new ExpandableListControl.ExpandableListAdapterGetViewListener() {
                    @Override
                    public View getView(int position, View view, ViewGroup parent) {
                        ODataRow row = (ODataRow) items.get(position);
                        if (!row.getString("image_medium").equals("false")) {
                            OControls.setImage(view, R.id.productImage,
                                    BitmapUtils.getBitmapImage(getActivity(), row.getString("image_medium")));
                        } else {
                            OControls.setImage(view, R.id.productImage, R.drawable.no_image);
                        }
                        OControls.setText(view, R.id.productName, row.getString("name"));
                        return view;
                    }
                });
        adapter.notifyDataSetChanged(items);
        templates.setDataObserver(new OModel.OnDataChangeListener() {
            @Override
            public void onModelDataChanged() {
                if(productTemplates!=null) {
                    productTemplates.clear();
                    productTemplates.addAll(templates.select(new String[]{"name", "image_medium"}, null, null, "id DESC"));
                    items.clear();
                    int limit = (productTemplates.size() > 10) ? 10 : productTemplates.size();
                    items.addAll(productTemplates.subList(0, limit));
                    adapter.notifyDataSetChanged(items);
                }
            }
        });
        return view;
    }

    private View getCategoryItemView(ViewGroup container) {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.shop_home_category_list_view, container, false);
        final List<Object> items = new ArrayList<>();
        items.addAll(db().select(null, "parent_id is NULL", new String[]{}, "sequence"));

        ExpandableListControl listControl = (ExpandableListControl) view.findViewById(R.id.categoriesList);
        listControl.setExpandableListItemClickListener(new ExpandableListControl.ExpandableListItemClickListener() {
            @Override
            public void onItemViewClick(int position, View view) {
                ODataRow row = (ODataRow) items.get(position);
                startFragment(new ProductSubCategories(), true, row.getPrimaryBundleData());
            }
        });
        ExpandableListControl.ExpandableListAdapter adapter = listControl.getAdapter(R.layout.shop_home_category_item, items, new ExpandableListControl.ExpandableListAdapterGetViewListener() {
            @Override
            public View getView(int position, View view, ViewGroup parent) {
                ODataRow row = (ODataRow) items.get(position);
                if (!row.getString("image_medium").equals("false")) {
                    OControls.setImage(view, R.id.categoryIcon,
                            BitmapUtils.getBitmapImage(getActivity(), row.getString("image_medium")));
                } else {
                    OControls.setImage(view, R.id.categoryIcon, R.drawable.ic_action_arrow_right);
                }
                OControls.setText(view, R.id.title, row.getString("name"));
                return view;
            }
        });
        adapter.notifyDataSetChanged(items);
        return view;
    }

    @Override
    public List<ODrawerItem> drawerMenus(Context context) {
        return null;
    }

    @Override
    public Class<ProductPublicCategory> database() {
        return ProductPublicCategory.class;
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.menu_home, menu);
        OAppBarUtils.bindShopMenu(parent(), new int[]{R.id.menu_home, R.id.menu_search_product}, menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        OAppBarUtils.onShopMenuItemClick(parent(), this, item);
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onPause() {
        super.onPause();
        templates.setDataObserver(null);
    }
} public class ProductCategoryLoader extends AsyncTask<Void, Void, Void> {
    private Context mContext;
    private ProductPublicCategory category;
    private OnCategoryLoadListener mOnCategoryLoadListener;
    private App mApp;

    public ProductCategoryLoader(Context context, OnCategoryLoadListener callback) {
        mContext = context;
        mApp = (App) mContext.getApplicationContext();
        category = new ProductPublicCategory(context);
        mOnCategoryLoadListener = callback;
    }

    @Override
    protected Void doInBackground(Void... params) {
        try {
            if (mApp.inNetwork()) {
                category.quickSyncRecords(null, true);
            }
            Thread.sleep(1000);
        } catch (Exception e) {

        }
        return null;
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        if (mOnCategoryLoadListener != null) {
            mOnCategoryLoadListener.categoryLoaded();
        }
    }

    public interface OnCategoryLoadListener {
        void categoryLoaded();
    }
} public class ProductSubCategories extends BaseFragment implements EasyRecyclerViewAdapter.OnViewBindListener,
        EasyRecyclerViewAdapter.OnItemViewClickListener {

    private Bundle data;
    private ODataRow category;
    private EasyRecyclerView subCategories;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        setHasOptionsMenu(true);
        return inflater.inflate(R.layout.shop_home_sub_category, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar);
        toolbar.setNavigationIcon(R.drawable.ic_action_navigation_arrow_back);
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getFragmentManager().popBackStack();
            }
        });
        toolbar.inflateMenu(R.menu.menu_home);
        OAppBarUtils.bindShopMenu(parent(), new int[]{}, toolbar.getMenu());
        toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                OAppBarUtils.onShopMenuItemClick(parent(), ProductSubCategories.this, item);
                return true;
            }
        });
        data = getArguments();
        category = db().browse(data.getInt(OColumn.ROW_ID));
        setTitle(toolbar);
        init();
    }

    private void init() {
        List<Object> items = new ArrayList<>();
        subCategories = (EasyRecyclerView) findViewById(R.id.subCategoriesList);
        subCategories.setLayout(R.layout.shop_home_sub_category_item);
        subCategories.changeCursor(items);
        subCategories.setOnViewBindListener(this);
        subCategories.setOnItemViewClickListener(this);
        items.addAll(findAllChild(category.getInt(OColumn.ROW_ID)));
        subCategories.changeCursor(items);
    }

    private List<Object> findAllChild(int parent_id) {
        List<Object> items = new ArrayList<>();
        List<ODataRow> rows = db().select(new String[]{"name", "parent_id", "image_medium"}, "parent_id = ?",
                new String[]{parent_id + ""}, "sequence");
        for (ODataRow row : rows) {
            boolean hasChild = db().count("parent_id = ?", new String[]{row.getInt(OColumn.ROW_ID) + ""}) > 0;
            row.put("has_child", hasChild);
            items.add(row);
            if (hasChild) {
                items.addAll(findAllChild(row.getInt(OColumn.ROW_ID)));
            }
        }
        return items;
    }

    private void setTitle(Toolbar toolbar) {
        CollapsingToolbarLayout collapsing_toolbar = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
        collapsing_toolbar.setTitle(category.getString("name"));
        toolbar.setTitle(category.getString("name"));
        Bitmap cover;
        if (category.getString("image_medium").equals("false")) {
            cover = BitmapUtils.getAlphabetImage(getContext(), category.getString("name"));
        } else {
            cover = BitmapUtils.getBitmapImage(getContext(), category.getString("image_medium"));
        }

        Palette.Builder paletteBuilder = new Palette.Builder(cover);
        Palette palette = paletteBuilder.generate();
        ImageView imageView = (ImageView) findViewById(R.id.image);
        imageView.setImageBitmap(cover);
        if (palette != null) {
            int color = palette.getDarkVibrantColor(_c(R.color.theme_secondary_dark));
            imageView.setBackgroundColor(color);
        }

    }

    @Override
    public List<ODrawerItem> drawerMenus(Context context) {
        return null;
    }

    @Override
    public Class<ProductPublicCategory> database() {
        return ProductPublicCategory.class;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                getChildFragmentManager().popBackStack();
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onViewBind(int position, View view, Object data) {
        ODataRow row = (ODataRow) data;
        String[] display_names = row.getString("name").split(" / ");
        OControls.setText(view, android.R.id.text1, display_names[display_names.length - 1]);
        view.findViewById(R.id.subCategoryPadding).setVisibility(
                row.getInt("parent_id") != category.getInt(OColumn.ROW_ID) ? View.VISIBLE : View.GONE);
        BezelImageView icon = (BezelImageView) view.findViewById(R.id.categoryIcon);
        if (row.getString("image_medium").equals("false")) {
            icon.setImageResource(R.drawable.ic_action_image_lens);
        } else {
            icon.setImageBitmap(BitmapUtils.getBitmapImage(getContext(), row.getString("image_medium")));
        }
        icon.setVisibility(row.getInt("parent_id") == category.getInt(OColumn.ROW_ID) ? View.VISIBLE : View.GONE);
    }

    @Override
    public void onItemViewClick(int position, View view, Object data) {
        ODataRow row = (ODataRow) data;
        Bundle extra = new Bundle();
        extra.putBoolean(Products.KEY_FILTER_BY_CATEGORY, true);
        extra.putBundle(Products.KEY_FILTER_DATA, row.getPrimaryBundleData());
        startFragment(new Products(), true, extra);
    }
} public class ProductPublicCategory extends OModel {

    OColumn name = new OColumn("Name", OVarchar.class);
    OColumn complete_name = new OColumn("Complete Name", OVarchar.class);
    OColumn parent_id = new OColumn("Parent category", ProductPublicCategory.class, OColumn.RelationType.ManyToOne);
    OColumn sequence = new OColumn("Sequence", OInteger.class).setDefaultValue(0);
    OColumn image_medium = new OColumn("Image", OBlob.class).setDefaultValue("false");

    public ProductPublicCategory(Context context) {
        super(context, "product.public.category");
    }

    public static boolean hasCategories(Context context) {
        ProductPublicCategory category = new ProductPublicCategory(context);
        return category.count(null, null) > 0;
    }

} public class OdooLogin extends AppCompatActivity implements View.OnClickListener,
        View.OnFocusChangeListener, OdooInstancesSelectorDialog.OnInstanceSelectListener,
        OdooUserLoginSelectorDialog.IUserLoginSelectListener, IOdooConnectionListener, IOdooLoginCallback {

    private EditText edtUsername, edtPassword, edtSelfHosted;
    private Boolean mCreateAccountRequest = false;
    private Boolean mSelfHostedURL = false;
    private Boolean mConnectedToServer = false;
    private Boolean mAutoLogin = false;
    private Boolean mRequestedForAccount = false;
    private AccountCreater accountCreator = null;
    private Spinner databaseSpinner = null;
    private List<String> 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) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.base_login);
        mApp = (App) getApplicationContext();
        Bundle extras = getIntent().getExtras();
        if (extras != null) {
            if (extras.containsKey(OdooAuthenticator.KEY_NEW_ACCOUNT_REQUEST))
                mCreateAccountRequest = true;
            if (extras.containsKey(OdooActivity.KEY_ACCOUNT_REQUEST)) {
                mRequestedForAccount = true;
                setResult(RESULT_CANCELED);
            }
        }
        if (!mCreateAccountRequest) {
            if (OdooAccountManager.anyActiveUser(this)) {
                startOdooActivity();
                return;
            } else if (OdooAccountManager.hasAnyAccount(this)) {
                onRequestAccountSelect();
            }
        }
        init();
    }

    private void init() {
        mLoginProcessStatus = (TextView) findViewById(R.id.login_process_status);
        mTermsCondition = (TextView) findViewById(R.id.termsCondition);
        mTermsCondition.setMovementMethod(LinkMovementMethod.getInstance());
        findViewById(R.id.btnLogin).setOnClickListener(this);
        findViewById(R.id.forgot_password).setOnClickListener(this);
        findViewById(R.id.create_account).setOnClickListener(this);
        findViewById(R.id.txvAddSelfHosted).setOnClickListener(this);
        edtSelfHosted = (EditText) findViewById(R.id.edtSelfHostedURL);
    }

    private void startOdooActivity() {
        startActivity(new Intent(this, OdooActivity.class));
        finish();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_base_login, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.txvAddSelfHosted:
                toggleSelfHostedURL();
                break;
            case R.id.btnLogin:
                loginUser();
                break;
            case R.id.forgot_password:
                IntentUtils.openURLInBrowser(this, OConstants.URL_ODOO_RESET_PASSWORD);
                break;
            case R.id.create_account:
                IntentUtils.openURLInBrowser(this, OConstants.URL_ODOO_SIGN_UP);
                break;
        }
    }

    private void toggleSelfHostedURL() {
        TextView txvAddSelfHosted = (TextView) findViewById(R.id.txvAddSelfHosted);
        if (!mSelfHostedURL) {
            mSelfHostedURL = true;
            findViewById(R.id.layoutSelfHosted).setVisibility(View.VISIBLE);
            edtSelfHosted.setOnFocusChangeListener(this);
            edtSelfHosted.requestFocus();
            txvAddSelfHosted.setText(R.string.label_login_with_odoo);
        } else {
            findViewById(R.id.layoutBorderDB).setVisibility(View.GONE);
            findViewById(R.id.layoutDatabase).setVisibility(View.GONE);
            findViewById(R.id.layoutSelfHosted).setVisibility(View.GONE);
            mSelfHostedURL = false;
            txvAddSelfHosted.setText(R.string.label_add_self_hosted_url);
            edtSelfHosted.setText("");
        }
    }

    @Override
    public void onFocusChange(final View v, final boolean hasFocus) {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                if (mSelfHostedURL && v.getId() == R.id.edtSelfHostedURL && !hasFocus) {
                    if (!TextUtils.isEmpty(edtSelfHosted.getText())
                            && validateURL(edtSelfHosted.getText().toString())) {
                        edtSelfHosted.setError(null);
                        if (mAutoLogin) {
                            findViewById(R.id.controls).setVisibility(View.GONE);
                            findViewById(R.id.login_progress).setVisibility(View.VISIBLE);
                            mLoginProcessStatus.setText(OResource.string(OdooLogin.this,
                                    R.string.status_connecting_to_server));
                        }
                        findViewById(R.id.imgValidURL).setVisibility(View.GONE);
                        findViewById(R.id.serverURLCheckProgress).setVisibility(View.VISIBLE);
                        findViewById(R.id.layoutBorderDB).setVisibility(View.GONE);
                        findViewById(R.id.layoutDatabase).setVisibility(View.GONE);
                        String test_url = createServerURL(edtSelfHosted.getText().toString());
                        Log.v("", "Testing URL :" + test_url);
                        Odoo.createInstance(OdooLogin.this, test_url).setOnConnect(OdooLogin.this);
                    }
                }
            }
        }, 500);
    }

    private boolean validateURL(String url) {
        return (url.contains("."));
    }

    private String createServerURL(String server_url) {
        StringBuilder serverURL = new StringBuilder();
        if (!server_url.contains("http://") && !server_url.contains("https://")) {
            serverURL.append("http://");
        }
        serverURL.append(server_url);
        return serverURL.toString();
    }

    // User Login
    private void loginUser() {
        Log.v("", "LoginUser()");
        String serverURL = createServerURL((mSelfHostedURL) ? edtSelfHosted.getText().toString() :
                OConstants.URL_ODOO);
        String databaseName;
        edtUsername = (EditText) findViewById(R.id.edtUserName);
        edtPassword = (EditText) findViewById(R.id.edtPassword);

        if (mSelfHostedURL) {
            edtSelfHosted.setError(null);
            if (TextUtils.isEmpty(edtSelfHosted.getText())) {
                edtSelfHosted.setError(OResource.string(this, R.string.error_provide_server_url));
                edtSelfHosted.requestFocus();
                return;
            }
            if (databaseSpinner != null && databases.size() > 1 && databaseSpinner.getSelectedItemPosition() == 0) {
                Toast.makeText(this, OResource.string(this, R.string.label_select_database), Toast.LENGTH_LONG).show();
                return;
            }

        }
        edtUsername.setError(null);
        edtPassword.setError(null);
        if (TextUtils.isEmpty(edtUsername.getText())) {
            edtUsername.setError(OResource.string(this, R.string.error_provide_username));
            edtUsername.requestFocus();
            return;
        }
        if (TextUtils.isEmpty(edtPassword.getText())) {
            edtPassword.setError(OResource.string(this, R.string.error_provide_password));
            edtPassword.requestFocus();
            return;
        }
        findViewById(R.id.controls).setVisibility(View.GONE);
        findViewById(R.id.login_progress).setVisibility(View.VISIBLE);
        mLoginProcessStatus.setText(OResource.string(OdooLogin.this,
                R.string.status_connecting_to_server));
        if (mConnectedToServer) {
            databaseName = databases.get(0);
            if (databaseSpinner != null) {
                databaseName = databases.get(databaseSpinner.getSelectedItemPosition());
            }
            mAutoLogin = false;
            loginProcess(null, serverURL, databaseName);
        } else {
            mAutoLogin = true;
            Log.v("", "Testing URL: " + serverURL);
            Odoo.createInstance(OdooLogin.this, serverURL).setOnConnect(OdooLogin.this);
        }
    }

    private void showDatabases() {
        if (databases.size() > 1) {
            findViewById(R.id.layoutBorderDB).setVisibility(View.VISIBLE);
            findViewById(R.id.layoutDatabase).setVisibility(View.VISIBLE);
            databaseSpinner = (Spinner) findViewById(R.id.spinnerDatabaseList);
            databases.add(0, OResource.string(this, R.string.label_select_database));
            ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, databases);
            databaseSpinner.setAdapter(adapter);
        } else {
            databaseSpinner = null;
            findViewById(R.id.layoutBorderDB).setVisibility(View.GONE);
            findViewById(R.id.layoutDatabase).setVisibility(View.GONE);
        }
    }

    @Override
    public void onUserSelected(OUser user) {
        OdooAccountManager.login(this, user.getAndroidName());
        startOdooActivity();
    }

    @Override
    public void onRequestAccountSelect() {
        OdooUserLoginSelectorDialog dialog = new OdooUserLoginSelectorDialog(this);
        dialog.setUserLoginSelectListener(this);
        dialog.show();
    }

    @Override
    public void onNewAccountRequest() {
        init();
    }


    @Override
    public void onConnect(Odoo odoo) {
        Log.v("Odoo", "Connected to server.");
        mOdoo = odoo;
        databases.clear();
        findViewById(R.id.serverURLCheckProgress).setVisibility(View.GONE);
        edtSelfHosted.setError(null);
        mLoginProcessStatus.setText(OResource.string(OdooLogin.this, R.string.status_connected_to_server));
        mOdoo.getDatabaseList(new IDatabaseListListener() {
            @Override
            public void onDatabasesLoad(List<String> strings) {
                databases.addAll(strings);
                showDatabases();
                mConnectedToServer = true;
                findViewById(R.id.imgValidURL).setVisibility(View.VISIBLE);
                if (mAutoLogin) {
                    loginUser();
                }
            }
        });
    }

    @Override
    public void onError(OdooError error) {
        // Some error occurred
        if (error.getResponseCode() == Odoo.ErrorCode.InvalidURL.get() ||
                error.getResponseCode() == -1) {
            edtSelfHosted.setError(OResource.string(OdooLogin.this, R.string.error_invalid_odoo_url));
            edtSelfHosted.requestFocus();
        }
        canceledInstanceSelect();
    }

    @Override
    public void onCancelSelect() {
    }

    @Override
    public void canceledInstanceSelect() {
        findViewById(R.id.controls).setVisibility(View.VISIBLE);
        findViewById(R.id.login_progress).setVisibility(View.GONE);
        findViewById(R.id.serverURLCheckProgress).setVisibility(View.VISIBLE);
    }

    @Override
    public void instanceSelected(OdooInstance instance) {
        // Logging in to instance
        loginProcess(instance, null, null);
    }

    private void loginProcess(final OdooInstance instance, String url, final String database) {
        Log.v("", "LoginProcess");
        final String username = edtUsername.getText().toString();
        final String password = edtPassword.getText().toString();
        if (instance == null && url.equals(OConstants.URL_ODOO)) {
            // OAuth Login or Odoo.com Login
            mLoginProcessStatus.setText(OResource.string(OdooLogin.this, R.string.status_getting_instances));
            mOdoo.authenticate(username, password, database, new IOdooLoginCallback() {
                @Override
                public void onLoginSuccess(Odoo odoo, odoo.helper.OUser oUser) {
                    mOdoo = odoo;
                    mUser = oUser;
                    mOdoo.getSaasInstances(new IOdooInstanceListener() {
                        @Override
                        public void onInstancesLoad(List<OdooInstance> odooInstances) {
                            OdooInstance oInstance = new OdooInstance();
                            oInstance.setCompanyName(OConstants.ODOO_COMPANY_NAME);
                            oInstance.setUrl(OConstants.URL_ODOO);
                            oInstance.setDbName(database);
                            odooInstances.add(0, oInstance);
                            if (odooInstances.size() > 1) {
                                OdooInstancesSelectorDialog instancesSelectorDialog =
                                        new OdooInstancesSelectorDialog(OdooLogin.this);
                                instancesSelectorDialog.setInstances(odooInstances);
                                instancesSelectorDialog.setOnInstanceSelectListener(OdooLogin.this);
                                instancesSelectorDialog.showDialog();
                            } else {
                                //Loggin in to odoo.com (default instance)
                                loginProcess(oInstance, oInstance.getUrl(), database);
                            }
                        }
                    });
                }

                @Override
                public void onLoginFail(OdooError error) {
                    loginFail(error);
                }
            });
        } else if (instance == null) {
            Log.v("", "Processing Self Hosted Server Login");
            mLoginProcessStatus.setText(OResource.string(OdooLogin.this, R.string.status_logging_in));
            mOdoo.authenticate(username, password, database, this);
        } else {
            // Instance login
            Log.v("", "Processing Odoo Instance Login");
            mLoginProcessStatus.setText(OResource.string(OdooLogin.this,
                    R.string.status_logging_in_with_instance));
            new AsyncTask<Void, Void, odoo.helper.OUser>() {

                @Override
                protected odoo.helper.OUser doInBackground(Void... params) {
                    // Need to execute in background task.
                    return mOdoo.oAuthLogin(instance, username, password);
                }

                @Override
                protected void onPostExecute(odoo.helper.OUser oUser) {
                    super.onPostExecute(oUser);
                    onLoginSuccess(mOdoo, oUser);
                }
            }.execute();
        }
    }

    @Override
    public void onLoginSuccess(Odoo odoo, odoo.helper.OUser oUser) {
        mApp.setOdoo(odoo, oUser);
        mLoginProcessStatus.setText(OResource.string(OdooLogin.this, R.string.status_login_success));
        mOdoo = odoo;
        if (accountCreator != null) {
            accountCreator.cancel(true);
        }
        accountCreator = new AccountCreater();
        OUser user = new OUser();
        user.setFromBundle(oUser.getAsBundle());
        accountCreator.execute(user);
    }

    @Override
    public void onLoginFail(OdooError error) {
        loginFail(error);
    }

    private void loginFail(OdooError error) {
        findViewById(R.id.controls).setVisibility(View.VISIBLE);
        findViewById(R.id.login_progress).setVisibility(View.GONE);
        edtUsername.setError(OResource.string(this, R.string.error_invalid_username_or_password));
    }

    private class AccountCreater extends AsyncTask<OUser, Void, Boolean> {

        private OUser mUser;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mLoginProcessStatus.setText(OResource.string(OdooLogin.this, R.string.status_creating_account));
        }

        @Override
        protected Boolean doInBackground(OUser... params) {
            mUser = params[0];
            if (OdooAccountManager.createAccount(OdooLogin.this, mUser)) {
                mUser = OdooAccountManager.getDetails(OdooLogin.this, mUser.getAndroidName());
                OdooAccountManager.login(OdooLogin.this, mUser.getAndroidName());
                FirstLaunchConfig.onFirstLaunch(OdooLogin.this, mUser);
                try {
                    // Syncing company details
                    ODataRow company_details = new ODataRow();
                    company_details.put("id", mUser.getCompanyId());
                    ResCompany company = new ResCompany(OdooLogin.this);
                    company.quickCreateRecord(company_details);
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return true;
            }
            return false;
        }

        @Override
        protected void onPostExecute(Boolean success) {
            super.onPostExecute(success);
            mLoginProcessStatus.setText(OResource.string(OdooLogin.this, R.string.status_redirecting));
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (!mRequestedForAccount)
                        startOdooActivity();
                    else {
                        Intent intent = new Intent();
                        intent.putExtra(OdooActivity.KEY_NEW_USER_NAME, mUser.getAndroidName());
                        setResult(RESULT_OK, intent);
                        finish();
                    }
                }
            }, 1500);
        }
    }
} public class OdooAccountManager {

    public static final String TAG = OdooAccountManager.class.getSimpleName();
    public static final String KEY_ACCOUNT_TYPE = "com.odoo.auth";
    public static final String KEY_USER_ACCOUNT_VERSION = "key_user_account_version";

    /**
     * Gets all the account related Odoo Auth
     *
     * @param context
     * @return List of OUser instances if any
     */
    public static List<OUser> getAllAccounts(Context context) {
        List<OUser> users = new ArrayList<OUser>();
        AccountManager aManager = AccountManager.get(context);
        for (Account account : aManager.getAccountsByType(KEY_ACCOUNT_TYPE)) {
            OUser user = new OUser();
            user.fillFromAccount(aManager, account);
            user.setAccount(account);
            users.add(user);
        }
        return users;
    }

    /**
     * Check for any account availability
     *
     * @param context
     * @return true, if any account found
     */
    public static boolean hasAnyAccount(Context context) {
        if (getAllAccounts(context).size() > 0)
            return true;
        return false;
    }

    public static boolean isValidUserObj(Context context, OUser user) {
        return true;
    }

    /**
     * Creates Odoo account for app
     *
     * @param context
     * @param user    user instance (OUser)
     * @return true, if account created successfully
     */

    public static boolean createAccount(Context context, OUser user) {
        AccountManager accountManager = AccountManager.get(context);
        Account account = new Account(user.getAndroidName(), KEY_ACCOUNT_TYPE);
        if (accountManager.addAccountExplicitly(account, String.valueOf(user.getPassword()),
                user.getAsBundle())) {
            OPreferenceManager pref = new OPreferenceManager(context);
            if (pref.getInt(userObjectKEY(user), 0) != OUser.USER_ACCOUNT_VERSION) {
                pref.putInt(userObjectKEY(user), OUser.USER_ACCOUNT_VERSION);
            }
            return true;
        }
        return false;
    }

    public static String userObjectKEY(OUser user) {
        return KEY_USER_ACCOUNT_VERSION + "_" + user.getAndroidName();
    }

    /**
     * Remove account from device
     *
     * @param context
     * @param username
     * @return true, if account removed successfully
     */
    public static boolean removeAccount(Context context, String username) {
        OUser user = getDetails(context, username);
        if (user != null) {
            AccountManager accountManager = AccountManager.get(context);
            accountManager.removeAccount(user.getAccount(), null, null);
            return true;
        }
        return false;
    }

    public static OUser updateUserData(Context context, OUser user, OUser newData) {
        if (user != null) {
            AccountManager accountManager = AccountManager.get(context);
            for (String key : newData.getAsBundle().keySet()) {
                accountManager.setUserData(user.getAccount(), key, newData.getAsBundle().get(key) + "");
            }
            OPreferenceManager pref = new OPreferenceManager(context);
            if (pref.getInt(userObjectKEY(user), 0) != OUser.USER_ACCOUNT_VERSION) {
                pref.putInt(userObjectKEY(user), OUser.USER_ACCOUNT_VERSION);
            }
        }
        return getDetails(context, newData.getAndroidName());
    }

    /**
     * Updates user bundle data in accounts
     *
     * @param context
     * @param newData instance of OUser class
     * @return new user object with updated values
     */
    public static OUser updateUserData(Context context, OUser newData) {
        OUser user = getDetails(context, newData.getAndroidName());
        return updateUserData(context, user, newData);
    }

    /**
     * Finds any active user for application
     *
     * @param context
     * @return true, if there is any active user for app
     */
    public static boolean anyActiveUser(Context context) {
        for (OUser user : getAllAccounts(context)) {
            if (user.isActive()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Gets active user object
     *
     * @param context
     * @return user object (Instance of OUser class)
     */
    public static OUser getActiveUser(Context context) {
        for (OUser user : getAllAccounts(context)) {
            if (user.isActive()) {
                return user;
            }
        }
        return null;
    }

    /**
     * Returns OUser object with username
     *
     * @param context
     * @param username
     * @return instance for OUser class or null
     */
    public static OUser getDetails(Context context, String username) {
        for (OUser user : getAllAccounts(context))
            if (user.getAndroidName().equals(username)) {
                return user;
            }
        return null;
    }

    /**
     * Login to user account. changes active state for user.
     * Other users will be automatically logged out
     *
     * @param context
     * @param username
     * @return new user object
     */
    public static OUser login(Context context, String username) {

        // Setting odoo instance to null
        App app = (App) context.getApplicationContext();
        app.setOdoo(null, null);
        // Clearing models registry
        OModelRegistry registry = new OModelRegistry();
        registry.clearAll();
        OUser activeUser = getActiveUser(context);
        // Logging out user if any
        if (activeUser != null) {
            logout(context, activeUser.getAndroidName());
        }

        OUser newUser = getDetails(context, username);
        if (newUser != null) {
            AccountManager accountManager = AccountManager.get(context);
            accountManager.setUserData(newUser.getAccount(), "isactive", "true");
            Log.i(TAG, newUser.getName() + " Logged in successfully");
            return newUser;
        }
        // Clearing old cache of the system
        OCacheUtils.clearSystemCache(context);
        return null;
    }

    /**
     * Logout user
     *
     * @param context
     * @param username
     * @return true, if successfully logged out
     */
    public static boolean logout(Context context, String username) {
        OUser user = getDetails(context, username);
        if (user != null) {
            if (cancelUserSync(user.getAccount())) {
                AccountManager accountManager = AccountManager.get(context);
                accountManager.setUserData(user.getAccount(), "isactive", "false");
                Log.i(TAG, user.getName() + " Logged out successfully");
                return true;
            }
        }
        return false;
    }

    private static boolean cancelUserSync(Account account) {
        //TODO: Cancel user's sync services. if any.
        return true;
    }
} 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;
    private OUser mUser;
    private String model_name = null;
    private List<OColumn> mColumns = new ArrayList<>();
    private List<OColumn> mRelationColumns = new ArrayList<>();
    private List<OColumn> mFunctionalColumns = new ArrayList<>();
    private HashMap<String, Field> mDeclaredFields = new HashMap<>();
    private OdooVersion mOdooVersion = null;
    private String default_name_column = "name";
    public static OModelRegistry modelRegistry = new OModelRegistry();
    private boolean hasMailChatter = false;

    // data observer
    private OnDataChangeListener onDataChangeListener;
    private DataObserver dataObserver;

    // 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
    @Odoo.api.v9alpha
    public OColumn create_date = new OColumn("Created On", ODateTime.class);

    @Odoo.api.v8
    @Odoo.api.v9alpha
    public OColumn write_date = new OColumn("Last Updated On", ODateTime.class);

    // Local Base columns
    OColumn _id = new OColumn("_ID", OInteger.class).setAutoIncrement().setLocalColumn();
    OColumn _write_date = new OColumn("Local Write Date", ODateTime.class).setLocalColumn();
    OColumn _is_dirty = new OColumn("Dirty record", OBoolean.class).setDefaultValue(false).setLocalColumn();
    OColumn _is_active = new OColumn("Active Record", OBoolean.class).setDefaultValue(true).setLocalColumn();

    public OModel(Context context, String model_name) {
        mContext = context;
        mUser = OUser.currentUser(context);
        this.model_name = model_name;
        if (mUser != null) {
            mOdooVersion = mUser.getOdooVersion();
            if (sqLite == null) {
                sqLite = new OSQLite(mContext, mUser);
            }
        }
    }

    public SQLiteDatabase getReadableDatabase() {
        return sqLite.getReadableDatabase();
    }

    public SQLiteDatabase getWritableDatabase() {
        return sqLite.getWritableDatabase();
    }

    public String getDatabaseName() {
        return sqLite.getDatabaseName();
    }

    public void close() {
        // Any operation when closing database
    }

    public void setDefaultNameColumn(String nameColumn) {
        default_name_column = nameColumn;
    }

    public String getDefaultNameColumn() {
        return default_name_column;
    }

    public OModel setModelName(String model_name) {
        this.model_name = model_name;
        return this;
    }

    public boolean hasMailChatter() {
        return hasMailChatter;
    }

    public void setHasMailChatter(boolean hasMailChatter) {
        this.hasMailChatter = hasMailChatter;
    }

    public OUser getUser() {
        return mUser;
    }

    public OdooVersion getOdooVersion() {
        return mOdooVersion;
    }

    public List<OColumn> getColumns() {
        if (mColumns.size() == 0) {
            prepareFields();
        }
        return mColumns;
    }

    public List<OColumn> getColumns(Boolean local) {
        if (local != null) {
            List<OColumn> cols = new ArrayList<>();
            for (OColumn column : getColumns())
                if (local == column.isLocal())
                    cols.add(column);
            return cols;
        } else {
            return mColumns;
        }
    }

    public List<OColumn> getRelationColumns() {
        if (mColumns.size() <= 0)
            prepareFields();
        return mRelationColumns;
    }

    public OColumn getColumn(String column_name) {
        if (mDeclaredFields.size() <= 0)
            prepareFields();
        Field filed = mDeclaredFields.get(column_name);
        return getColumn(filed);
    }

    private OColumn getColumn(Field field) {
        OColumn column;
        if (field != null) {
            try {
                field.setAccessible(true);
                column = (OColumn) field.get(this);
                if (column.getName() == null)
                    column.setName(field.getName());
                Boolean validField = compatibleField(field);
                if (validField) {
                    // Functional Method
                    Method method = checkForFunctionalColumn(field);
                    if (method != null) {
                        column.setIsFunctionalColumn(true);
                        column.setFunctionalMethod(method);
                        column.setFunctionalStore(checkForFunctionalStore(field));
                        column.setFunctionalStoreDepends(getFunctionalDepends(field));
                        if (!column.canFunctionalStore()) {
                            column.setLocalColumn();
                        }
                    }

                    // Onchange method for column
                    Method onChangeMethod = checkForOnChangeMethod(field);
                    if (onChangeMethod != null) {
                        column.setOnChangeMethod(onChangeMethod);
                        column.setOnChangeBGProcess(checkForOnChangeBGProcess(field));
                    }

                    // Adding sync column name (if local column name is different)
                    String syncColumnName = checkForSyncColumnName(field);
                    column.setSyncColumnName(syncColumnName);

                    // domain filter on column
                    column.setHasDomainFilterColumn(isDomainFilterColumn(field));
                    return column;
                }
            } catch (Exception e) {
                e.printStackTrace();
                Log.e(TAG, e.getMessage());
            }
        }
        return null;
    }

    private boolean isDomainFilterColumn(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.hasDomainFilter.class);
        if (annotation != null) {
            Odoo.hasDomainFilter domainFilter = (Odoo.hasDomainFilter) annotation;
            return domainFilter.checkDomainRuntime();
        }
        return false;
    }

    private Boolean checkForOnChangeBGProcess(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.onChange.class);
        if (annotation != null) {
            Odoo.onChange onChange = (Odoo.onChange) annotation;
            return onChange.bg_process();
        }
        return false;
    }

    private String checkForSyncColumnName(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.SyncColumnName.class);
        if (annotation != null) {
            Odoo.SyncColumnName syncColumnName = (Odoo.SyncColumnName) annotation;
            if (syncColumnName.value().length() != 0) {
                return syncColumnName.value();
            }
        }
        return null;
    }

    private Method checkForOnChangeMethod(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.onChange.class);
        if (annotation != null) {
            Odoo.onChange onChange = (Odoo.onChange) annotation;
            String method_name = onChange.method();
            try {
                return getClass().getMethod(method_name, ODataRow.class);
            } catch (NoSuchMethodException e) {
                Log.e(TAG, "No Such Method: " + e.getMessage());
            }
        }
        return null;
    }

    /**
     * Check for functional store.
     *
     * @param field the field
     * @return the boolean
     */
    public Boolean checkForFunctionalStore(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.Functional.class);
        if (annotation != null) {
            Odoo.Functional functional = (Odoo.Functional) annotation;
            return functional.store();
        }
        return false;
    }


    /**
     * Gets the functional depends.
     *
     * @param field the field
     * @return the functional depends
     */
    public String[] getFunctionalDepends(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.Functional.class);
        if (annotation != null) {
            Odoo.Functional functional = (Odoo.Functional) annotation;
            return functional.depends();
        }
        return null;
    }

    private Method checkForFunctionalColumn(Field field) {
        Annotation annotation = field.getAnnotation(Odoo.Functional.class);
        if (annotation != null) {
            Odoo.Functional functional = (Odoo.Functional) annotation;
            String method_name = functional.method();
            try {
                if (functional.store())
                    return getClass().getMethod(method_name, OValues.class);
                else
                    return getClass().getMethod(method_name, ODataRow.class);
            } catch (NoSuchMethodException e) {
                Log.e(TAG, "No Such Method: " + e.getMessage());
            }
        }
        return null;
    }

    private boolean compatibleField(Field field) {
        if (mOdooVersion != null) {
            Annotation[] annotations = field.getDeclaredAnnotations();
            if (annotations.length > 0) {
                int version = 0;
                for (Annotation annotation : annotations) {
                    // Check for odoo api annotation
                    Class<? extends Annotation> type = annotation.annotationType();
                    if (type.getDeclaringClass().isAssignableFrom(Odoo.api.class)) {
                        switch (mOdooVersion.getVersionNumber()) {
                            case 9:
                                if (type.isAssignableFrom(Odoo.api.v9alpha.class)) {
                                    version++;
                                }
                                break;
                            case 8:
                                if (type.isAssignableFrom(Odoo.api.v8.class)) {
                                    version++;
                                }
                                break;
                            case 7:
                                if (type.isAssignableFrom(Odoo.api.v7.class)) {
                                    version++;
                                }
                                break;
                        }
                    }
                    // Check for functional annotation
                    if (type.isAssignableFrom(Odoo.Functional.class)
                            || type.isAssignableFrom(Odoo.onChange.class)
                            || type.isAssignableFrom(Odoo.hasDomainFilter.class)
                            || type.isAssignableFrom(Odoo.SyncColumnName.class)) {
                        version++;
                    }
                }
                return (version > 0) ? true : false;
            }
            return true;
        }
        return false;
    }

    private void prepareFields() {
        mColumns.clear();
        mRelationColumns.clear();
        List<Field> fields = new ArrayList<>();
        fields.addAll(Arrays.asList(getClass().getSuperclass().getDeclaredFields()));
        fields.addAll(Arrays.asList(getClass().getDeclaredFields()));
        mDeclaredFields.clear();
        for (Field field : fields) {
            if (field.getType().isAssignableFrom(OColumn.class)) {
                String name = field.getName();
                try {
                    OColumn column = getColumn(field);
                    if (column != null) {
                        name = column.getName();
                        if (column.getRelationType() != null) {
                            mRelationColumns.add(column);
                        }
                        if (column.isFunctionalColumn()) {
                            if (column.canFunctionalStore()) {
                                mColumns.add(column);
                            }
                            mFunctionalColumns.add(column);
                        } else {
                            mColumns.add(column);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                mDeclaredFields.put(name, field);
            }
        }
    }

    public List<OColumn> getFunctionalColumns() {
        if (mColumns.size() <= 0)
            prepareFields();
        return mFunctionalColumns;
    }

    public String getModelName() {
        return model_name;
    }

    public List<OColumn> getManyToManyColumns(OModel relation_model) {
        List<OColumn> cols = new ArrayList<OColumn>();
        _write_date.setName("_write_date");
        cols.add(_write_date);
        _is_dirty.setName("_is_dirty");
        cols.add(_is_dirty);
        _is_active.setName("_is_active");
        cols.add(_is_active);

        OColumn base_id = new OColumn("Base Id", OInteger.class);
        base_id.setName(getTableName() + "_id");
        cols.add(base_id);
        OColumn relation_id = new OColumn("Relation Id", OInteger.class);
        relation_id.setName(relation_model.getTableName() + "_id");
        cols.add(relation_id);
        return cols;
    }

    public OModel createInstance(Class<?> type) {
        try {
            Constructor<?> constructor = type.getConstructor(Context.class);
            return (OModel) constructor.newInstance(mContext);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getTableName() {
        return getModelName().replaceAll("\\.", "_");
    }

    public String toString() {
        return getModelName();
    }

    public static OModel get(Context context, String model_name, String username) {
        OModel model = modelRegistry.getModel(model_name, username);
        OUser user = OdooAccountManager.getDetails(context, username);
        if (model == null) {
            try {
                OPreferenceManager pfManager = new OPreferenceManager(context);
                Class<?> model_class = Class.forName(pfManager.getString(model_name, null));
                if (model_class != null) {
                    model = new OModel(context, model_name).createInstance(model_class);
                    if (model != null) {
                        modelRegistry.register(model);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return model;
    }

    public String authority() {
        return BASE_AUTHORITY;
    }

    public Uri buildURI(String authority) {
        BASE_AUTHORITY = authority;
        String path = getModelName().toLowerCase(Locale.getDefault());
        return BaseModelProvider.buildURI(BASE_AUTHORITY, path, mUser.getAndroidName());
    }

    public Uri uri() {
        String path = getModelName().toLowerCase(Locale.getDefault());
        return BaseModelProvider.buildURI(BASE_AUTHORITY, path, mUser.getAndroidName());
    }

    public HashMap<String, Object> requestContext(HashMap<String, Object> context) {
        if (context != null) {
            return context;
        }
        return new HashMap<>();
    }

    public ODomain defaultDomain() {
        return new ODomain();
    }

    private String[] updateProjection(String[] projection) {
        HashSet<String> names = new HashSet<>();
        String[] allProjection = projection;
        if (allProjection == null) {
            allProjection = projection();
        } else {
            for (String col : projection) {
                OColumn column = getColumn(col);
                if (column.isFunctionalColumn() && column.canFunctionalStore()) {
                    names.add(column.getName());
                }
            }
        }
        names.addAll(Arrays.asList(allProjection));
        names.addAll(Arrays.asList(new String[]{OColumn.ROW_ID, "id", "_write_date", "_is_dirty", "_is_active"}));
        return names.toArray(new String[names.size()]);
    }

    public String[] projection(Boolean onlyServerColumns) {
        List<String> names = new ArrayList<>();
        for (OColumn column : getColumns(false)) {
            if (column.getRelationType() == null || column.canFunctionalStore()) {
                names.add(column.getName());
            } else if (column.getRelationType() == OColumn.RelationType.ManyToOne) {
                names.add(column.getName());
            }
        }
        return names.toArray(new String[names.size()]);
    }

    public String[] projection() {
        List<String> names = new ArrayList<>();
        for (OColumn column : getColumns()) {
            if (column.getRelationType() == null || column.canFunctionalStore()) {
                names.add(column.getName());
            } else if (column.getRelationType() == OColumn.RelationType.ManyToOne) {
                names.add(column.getName());
            }
        }
        return names.toArray(new String[names.size()]);
    }

    // Sync default methods
    public boolean checkForCreateDate() {
        return true;
    }

    public boolean checkForWriteDate() {
        return true;
    }

    public boolean allowUpdateRecordOnServer() {
        return true;
    }

    public boolean allowCreateRecordOnServer() {
        return true;
    }

    public boolean allowDeleteRecordOnServer() {
        return true;
    }

    public boolean allowDeleteRecordInLocal() {
        return true;
    }

    // Database Operations

    public String getLabel(String column, String key) {
        OColumn col = getColumn(column);
        if (col.getType().isAssignableFrom(OSelection.class)) {
            return col.getSelectionMap().get(key);
        }
        return "false";
    }

    public ODataRow browse(int row_id) {
        return browse(null, row_id);
    }

    public ODataRow browse(String[] projection, int row_id) {
        List<ODataRow> rows = select(projection, OColumn.ROW_ID + " = ?", new String[]{row_id + ""});
        if (rows.size() > 0) {
            return rows.get(0);
        }
        return null;
    }

    public ODataRow browse(String[] projection, String selection, String[] args) {
        List<ODataRow> rows = select(updateProjection(projection), selection, args);
        if (rows.size() > 0) {
            return rows.get(0);
        }
        return null;
    }

    public List<Integer> getServerIds() {
        List<Integer> ids = new ArrayList<>();
        for (ODataRow row : select(new String[]{"id"})) {
            if (row.getInt("id") != 0) {
                ids.add(row.getInt("id"));
            }
        }
        return ids;
    }

    public boolean isEmptyTable() {
        return (count(null, null) <= 0);
    }

    public String getLastSyncDateTime() {
        IrModel model = new IrModel(mContext);
        List<ODataRow> records = model.select(null, "model = ?", new String[]{getModelName()});
        if (records.size() > 0) {
            String date = records.get(0).getString("last_synced");
            Date write_date = ODateUtils.createDateObject(date, ODateUtils.DEFAULT_FORMAT, true);
            Calendar cal = Calendar.getInstance();
            cal.setTime(write_date);
            /*
                Fixed for Postgres SQL
                It stores milliseconds so comparing date wrong. 
             */
            cal.set(Calendar.SECOND, cal.get(Calendar.SECOND) + 2);
            write_date = cal.getTime();
            return ODateUtils.getDate(write_date, ODateUtils.DEFAULT_FORMAT);
        }
        return null;
    }

    public List<ODataRow> select() {
        return select(null, null, null, null);
    }

    public List<ODataRow> select(String[] projection) {
        return select(projection, null, null, null);
    }

    public List<ODataRow> select(String[] projection, String where, String[] args) {
        return select(projection, where, args, null);
    }

    public List<ODataRow> select(String[] projection, String where, String[] args, String sortOrder) {
        Cursor cr = mContext.getContentResolver().query(uri(),
                updateProjection(projection), where, args, sortOrder);
        List<ODataRow> rows = new ArrayList<>();
        try {
            if (cr != null && cr.moveToFirst()) {
                do {
                    ODataRow row = OCursorUtils.toDatarow(cr);
                    for (OColumn column : getRelationColumns(projection)) {
                        if (!row.getString(column.getName()).equals("false")
                                || column.getRelationType() == OColumn.RelationType.OneToMany
                                || column.getRelationType() == OColumn.RelationType.ManyToMany) {
                            switch (column.getRelationType()) {
                                case ManyToMany:
                                    OM2MRecord m2mRecords = new OM2MRecord(this, column, row.getInt(OColumn.ROW_ID));
                                    row.put(column.getName(), m2mRecords);
                                    break;
                                case ManyToOne:
                                    OM2ORecord m2ORecord = new OM2ORecord(this, column, row.getInt(column.getName()));
                                    row.put(column.getName(), m2ORecord);
                                    break;
                                case OneToMany:
                                    OO2MRecord o2MRecord = new OO2MRecord(this, column, row.getInt(OColumn.ROW_ID));
                                    row.put(column.getName(), o2MRecord);
                                    break;
                            }
                        }
                    }
                    for (OColumn column : getFunctionalColumns(projection)) {
                        List<String> depends = column.getFunctionalStoreDepends();
                        if (depends != null && depends.size() > 0) {
                            ODataRow values = new ODataRow();
                            for (String depend : depends) {
                                if (row.contains(depend)) {
                                    values.put(depend, row.get(depend));
                                }
                            }
                            if (values.size() == depends.size()) {
                                Object value = getFunctionalMethodValue(column, values);
                                row.put(column.getName(), value);
                            }
                        }
                    }
                    rows.add(row);
                } while (cr.moveToNext());
            }
        } finally {
            cr.close();
        }
        return rows;
    }

    public Object getFunctionalMethodValue(OColumn column, Object record) {
        if (column.isFunctionalColumn()) {
            Method method = column.getFunctionalMethod();
            OModel model = this;
            try {
                return method.invoke(model, new Object[]{record});
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    public Object getOnChangeMethodValue(OColumn column, Object record) {
        Method method = column.getOnChangeMethod();
        OModel model = this;
        try {
            return method.invoke(model, new Object[]{record});
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    private List<OColumn> getFunctionalColumns(String[] projection) {
        List<OColumn> cols = new ArrayList<>();
        if (projection != null) {
            for (String key : projection) {
                OColumn column = getColumn(key);
                if (column.isFunctionalColumn() && !column.canFunctionalStore()) {
                    cols.add(column);
                }
            }
        } else {
            for (OColumn column : getFunctionalColumns()) {
                if (!column.canFunctionalStore())
                    cols.add(column);
            }
        }
        return cols;
    }

    private List<OColumn> getRelationColumns(String[] projection) {
        List<OColumn> cols = new ArrayList<>();
        if (projection != null) {
            for (String key : projection) {
                OColumn column = getColumn(key);
                if (column.getRelationType() != null) {
                    cols.add(column);
                }
            }
        } else {
            cols.addAll(getRelationColumns());
        }
        return cols;
    }

    public int insertOrUpdate(int serverId, OValues values) {
        if (hasServerRecord(serverId)) {
            int row_id = selectRowId(serverId);
            update(row_id, values);
            return row_id;
        } else {
            return insert(values);
        }
    }

    public int insertOrUpdate(String selection, String[] args, OValues values) {
        int count = update(selection, args, values);
        if (count <= 0) {
            return insert(values);
        } else {
            return selectRowId(selection, args);
        }
    }

    public int selectRowId(String selection, String[] args) {
        int row_id = INVALID_ROW_ID;
        SQLiteDatabase db = getReadableDatabase();
        Cursor cr = db.query(getTableName(), new String[]{OColumn.ROW_ID}, selection, args, null, null, null);
        try {
            if (cr.moveToFirst()) {
                row_id = cr.getInt(0);
            }
        } finally {
            cr.close();
        }
        return row_id;
    }

    public int selectServerId(int row_id) {
        return browse(row_id).getInt("id");
    }

    public int selectRowId(int server_id) {
        List<ODataRow> rows = select(new String[]{OColumn.ROW_ID}, "id = ?", new String[]{server_id + ""});
        if (rows.size() > 0) {
            return rows.get(0).getInt(OColumn.ROW_ID);
        }
        return INVALID_ROW_ID;
    }

    public int insert(OValues values) {
        Uri uri = mContext.getContentResolver().insert(uri(), values.toContentValues());
        if (uri != null) {
            return Integer.parseInt(uri.getLastPathSegment());
        }
        return INVALID_ROW_ID;
    }


    public boolean hasServerRecord(int server_id) {
        int count = count("id = ? ", new String[]{server_id + ""});
        return (count > 0);
    }

    public boolean isServerRecordDirty(int server_id) {
        int count = count("id = ? and _is_dirty = ?", new String[]{server_id + "", "true"});
        return (count > 0);
    }

    public boolean hasRecord(int row_id) {
        int count = count(OColumn.ROW_ID + " = ? ", new String[]{row_id + ""});
        return (count > 0);
    }


    public int deleteRecords(List<Integer> serverIds, boolean permanently) {
        String selection = "id IN (" + StringUtils.repeat("?, ", serverIds.size() - 1) + " ?)";
        String[] args = OListUtils.toStringList(serverIds).toArray(new String[serverIds.size()]);
        if (permanently) {
            return delete(selection, args, true);
        } else {
            OValues values = new OValues();
            values.put("_is_active", "false");
            return update(selection, args, values);
        }
    }

    public int delete(String selection, String[] args) {
        return delete(selection, args, false);
    }

    public int delete(String selection, String[] args, boolean permanently) {
        int count = 0;
        if (permanently) {
            count = mContext.getContentResolver().delete(uri(), selection, args);
        } else {
            List<ODataRow> records = select(new String[]{"_is_active"}, selection, args);
            for (ODataRow row : records) {
                if (row.getBoolean("_is_active")) {
                    OValues values = new OValues();
                    values.put("_is_active", "false");
                    update(row.getInt(OColumn.ROW_ID), values);
                }
                count++;
            }
        }
        return count;
    }

    public boolean delete(int row_id) {
        return delete(row_id, false);
    }

    public boolean delete(int row_id, boolean permanently) {
        int count = 0;
        if (permanently)
            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;
    }

    public int update(String selection, String[] args, OValues values) {
        return mContext.getContentResolver().update(uri(), values.toContentValues(), selection, args);
    }

    public boolean update(int row_id, OValues values) {
        int count = mContext.getContentResolver().update(uri().withAppendedPath(uri(), row_id + ""),
                values.toContentValues(), null, null);
        return (count > 0) ? true : false;
    }


    public List<ODataRow> query(String query) {
        return query(query, null);
    }

    public List<ODataRow> query(String query, String[] args) {
        List<ODataRow> rows = new ArrayList<>();
        SQLiteDatabase db = getReadableDatabase();
        Cursor cr = db.rawQuery(query, args);
        try {
            if (cr.moveToFirst()) {
                do {
                    rows.add(OCursorUtils.toDatarow(cr));
                } while (cr.moveToNext());
            }
        } finally {
            cr.close();
        }
        return rows;

    }

    public int count(String selection, String[] args) {
        int count = 0;
        SQLiteDatabase db = getReadableDatabase();
        Cursor cr = db.query(getTableName(), new String[]{"count(*)"}, selection, args, null, null, null);
        try {
            cr.moveToFirst();
            count = cr.getInt(0);
        } finally {
            cr.close();
        }
        return count;
    }


    public void storeManyToManyRecord(String column_name, int row_id, List<Integer> 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";

            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();
            }
        } else {
            throw new InvalidObjectException("Column [" + column_name + "] not found in " + getModelName() + " model.");

        }
    }

    public List<ODataRow> selectManyToManyRecords(String[] projection, String column_name, int row_id) {
        OColumn column = getColumn(column_name);
        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";

        // Getting relation table ids
        List<String> ids = new ArrayList<>();
        SQLiteDatabase db = getReadableDatabase();
        Cursor cr = null;
        try {
            cr = db.query(table, new String[]{rel_column}, base_column + "=?",
                    new String[]{row_id + ""}, null, null, null);
            if (cr.moveToFirst()) {
                do {
                    ids.add(cr.getInt(0) + "");
                } while (cr.moveToNext());
            }
        } finally {
            if (cr != null) {
                cr.close();
            }
        }
        List<ODataRow> data = rel_model.select(projection, OColumn.ROW_ID + " IN (" + StringUtils.repeat(" ?, ", ids.size() - 1) + " ?)",
                ids.toArray(new String[ids.size()]));
        rel_model.close();
        return data;
    }

    public ServerDataHelper getServerDataHelper() {
        return new ServerDataHelper(mContext, this, getUser());
    }

    public String getName(int row_id) {
        ODataRow row = browse(row_id);
        if (row != null) {
            return row.getString("name");
        }
        return "false";
    }

    public void quickSyncRecords(ODomain domain) {
        quickSyncRecords(domain, false);
    }

    public void quickSyncRecords(ODomain domain, boolean checkForCreateWriteDate) {
        quickSyncRecords(domain, checkForCreateWriteDate, 0);
    }

    public void quickSyncRecords(ODomain domain, boolean checkForCreateWriteDate, int dataLimit) {
        OSyncAdapter syncAdapter = new OSyncAdapter(mContext, getClass(), null, true);
        syncAdapter.setModel(this);
        syncAdapter.syncDataLimit(dataLimit);
        syncAdapter.setDomain(domain);
        syncAdapter.checkForWriteCreateDate(checkForCreateWriteDate);
        syncAdapter.onPerformSync(getUser().getAccount(), null, authority(), null, new SyncResult());
    }

    public ODataRow quickCreateRecord(ODataRow record) {
        OSyncAdapter syncAdapter = new OSyncAdapter(mContext, getClass(), null, true);
        syncAdapter.setModel(this);
        ODomain domain = new ODomain();
        domain.add("id", "=", record.getInt("id"));
        syncAdapter.setDomain(domain);
        syncAdapter.checkForWriteCreateDate(false);
        syncAdapter.onPerformSync(getUser().getAccount(), null, authority(), null, new SyncResult());
        return browse(null, "id = ?", new String[]{record.getString("id")});
    }

    public ODataRow countGroupBy(String column, String group_by, String having, String[] args) {
        String sql = "select count(*) as total, " + column;
        sql += " from " + getTableName() + " group by " + group_by + " having " + having;
        List<ODataRow> data = query(sql, args);
        if (data.size() > 0) {
            return data.get(0);
        } else {
            ODataRow row = new ODataRow();
            row.put("total", 0);
            return row;
        }
    }

    public void isInstalledOnServer(final String module_name, IModuleInstallListener callback) {
        App app = (App) mContext.getApplicationContext();
        app.getOdoo(getUser()).installedOnServer(module_name, new IModuleInstallListener() {
            @Override
            public void installedOnServer(boolean isInstalled) {
                IrModel model = new IrModel(mContext);
                OValues values = new OValues();
                values.put("id", 0);
                values.put("name", module_name);
                values.put("state", "installed");
                model.insertOrUpdate("name = ?", new String[]{module_name}, values);
            }
        });
    }

    public String getDatabaseLocalPath() {
        return sqLite.databaseLocalPath();
    }

    public void exportDB() {
        FileChannel source;
        FileChannel destination;
        String currentDBPath = getDatabaseLocalPath();
        String backupDBPath = OStorageUtils.getDirectoryPath("file")
                + "/" + getDatabaseName();
        File currentDB = new File(currentDBPath);
        File backupDB = new File(backupDBPath);
        try {
            source = new FileInputStream(currentDB).getChannel();
            destination = new FileOutputStream(backupDB).getChannel();
            destination.transferFrom(source, 0, source.size());
            source.close();
            destination.close();
            String subject = "Database Export: " + getDatabaseName();
            Uri uri = Uri.fromFile(backupDB);
            Intent intent = new Intent(Intent.ACTION_SEND);
            intent.putExtra(Intent.EXTRA_STREAM, uri);
            intent.putExtra(Intent.EXTRA_SUBJECT, subject);
            intent.setType("message/rfc822");
            mContext.startActivity(intent);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onSyncStarted() {
        // Will be over ride by extending model
    }

    @Override
    public void onSyncFinished() {
        // Will be over ride by extending model
    }

    // Data observer
    class DataObserver extends ContentObserver {

        public DataObserver(Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            if (onDataChangeListener != null) {
                onDataChangeListener.onModelDataChanged();
            }
        }

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            super.onChange(selfChange, uri);
            if (onDataChangeListener != null) {
                onDataChangeListener.onModelDataChanged();
            }
        }
    }

    public OModel setDataObserver(OnDataChangeListener callback) {
        onDataChangeListener = callback;
        if (model_name != null) {
            dataObserver = new DataObserver(new Handler());
            mContext.getContentResolver().registerContentObserver(uri(), true, dataObserver);
        }
        return this;
    }

    public interface OnDataChangeListener {
        void onModelDataChanged();
    }

    @Override
    protected void finalize() throws Throwable {
        if (model_name != null && dataObserver != null) {
            mContext.getContentResolver().unregisterContentObserver(dataObserver);
        }
        super.finalize();
    }
} public class OUser extends odoo.helper.OUser {

    public static final int USER_ACCOUNT_VERSION = 2;
    private Account account;
    private OdooVersion odooVersion;
    private Boolean dummyUser = false;
    private OPreferenceManager pref = null;

    public Boolean isDummyUser() {
        return dummyUser;
    }

    public void setDummyUser(Boolean dummyUser) {
        this.dummyUser = dummyUser;
    }

    public static OUser currentUser(Context context) {
        return OConstants.publicUser();
    }

    public static OUser getUser(Context context) {
        OPreferenceManager pref = new OPreferenceManager(context);
        return pref.getUserValues();
    }

    public boolean createNewAccount(Context context) {
        pref = new OPreferenceManager(context);
        pref.setUserValues(this);
        return true;
    }

    @Override
    public Bundle getAsBundle() {
        Bundle data = super.getAsBundle();
        // Converting each value to string. Account supports only string values
        for (String key : data.keySet()) {
            data.putString(key, data.get(key) + "");
        }
        return data;
    }

    public void setFromBundle(Bundle data) {
        if (data != null) {
            data.putInt("version_type_number", Integer.parseInt(data.getString("version_type_number")));
            data.putInt("version_number", Integer.parseInt(data.getString("version_number")));
            data.putBoolean("oauth_login", Boolean.parseBoolean(data.getString("oauth_login")));
            data.putInt("user_id", Integer.parseInt(data.getString("user_id")));
            data.putInt("partner_id", Integer.parseInt(data.getString("partner_id")));
            data.putInt("company_id", Integer.parseInt(data.getString("company_id")));
            data.putBoolean("allow_self_signed_ssl", false);
            data.putBoolean("is_active",true);
            data.putBoolean("allow_force_connect", Boolean.parseBoolean(data.getString("allow_force_connect")));
        }
        fillFromBundle(data);
    }

    public void fillFromAccount(AccountManager accMgr, Account account) {

        setName(accMgr.getUserData(account, "name"));
        setUsername(accMgr.getUserData(account, "username"));
        setUserId(Integer.parseInt(accMgr.getUserData(account, "user_id")));
        setPartnerId(Integer.parseInt(accMgr.getUserData(account, "partner_id")));
        setTimezone(accMgr.getUserData(account, "timezone"));
        setIsActive(Boolean.parseBoolean(accMgr.getUserData(account, "isactive")));
        setAvatar(accMgr.getUserData(account, "avatar"));
        setDatabase(accMgr.getUserData(account, "database"));
        setHost(accMgr.getUserData(account, "host"));
        setAndroidName(accMgr.getUserData(account, "android_name"));
        setPassword(accMgr.getUserData(account, "password"));
        setCompanyId(Integer.parseInt(accMgr.getUserData(account, "company_id")));
        setAllowForceConnect(Boolean.parseBoolean(accMgr.getUserData(account, "allow_self_signed_ssl")));
        try {
            OdooVersion version = new OdooVersion();
            version.setServerSerie(accMgr.getUserData(account, "server_serie"));
            version.setVersionType(accMgr.getUserData(account, "version_type"));
            version.setVersionRelease(accMgr.getUserData(account, "version_release"));
            version.setVersionNumber(Integer.parseInt(accMgr.getUserData(account, "version_number")));
            version.setVersionTypeNumber(Integer.parseInt(accMgr.getUserData(account, "version_type_number")));
            version.setServerVersion(accMgr.getUserData(account, "server_version"));
            setOdooVersion(version);
        } catch (Exception e) {
            e.printStackTrace();
            Log.w(TAG, e.getMessage());
        }
        // If oAuth login
        setOAuthLogin(Boolean.parseBoolean(accMgr.getUserData(account, "oauth_login")));
        setInstanceDatabase(accMgr.getUserData(account, "instance_database"));
        setInstanceURL(accMgr.getUserData(account, "instance_url"));
        setClientId(accMgr.getUserData(account, "client_id"));
    }

    public String getDBName() {
        String db_name = "OdooSQLite";
        db_name += "_" + getUsername();
        db_name += "_" + getDatabase();
        return db_name + ".db";
    }

    @Override
    public String toString() {
        return getAndroidName();
    }

    public Account getAccount() {
        return account;
    }

    public void setAccount(Account account) {
        this.account = account;
    }

    @Override
    public OdooVersion getOdooVersion() {
        return odooVersion;
    }

    @Override
    public void setOdooVersion(OdooVersion odooVersion) {
        this.odooVersion = odooVersion;
    }
} public class SyncUtils {
    public static final String TAG = SyncUtils.class.getSimpleName();

    private OUser mUser;

    public SyncUtils(Context context, OUser user) {
        mUser = (user != null) ? user : OUser.currentUser(context);
    }

    public static SyncUtils get(Context context) {
        return new SyncUtils(context, null);
    }

    public static SyncUtils get(Context context, OUser user) {
        return new SyncUtils(context, user);
    }

    public void setAutoSync(String authority, boolean autoSync) {
        try {
            Account account = mUser.getAccount();
            if (!ContentResolver.isSyncActive(account, authority)) {
                ContentResolver.setSyncAutomatically(account, authority, autoSync);
            }
        } catch (NullPointerException e) {
            Log.e(TAG, e.getMessage());
            e.printStackTrace();
        }
    }

    public void requestSync(String authority) {
        requestSync(authority, null);
    }

    public void requestSync(String authority, Bundle bundle) {
        Account account = mUser.getAccount();
        Bundle settingsBundle = new Bundle();
        settingsBundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
        settingsBundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
        if (bundle != null) {
            settingsBundle.putAll(bundle);
        }
        ContentResolver.requestSync(account, authority, settingsBundle);
    }

    public void setSyncPeriodic(String authority, long interval_in_minute,
                                long seconds_per_minute, long milliseconds_per_second) {
        Account account = mUser.getAccount();
        Bundle extras = new Bundle();
        this.setAutoSync(authority, true);
        ContentResolver.setIsSyncable(account, authority, 1);
        final long sync_interval = interval_in_minute * seconds_per_minute
                * milliseconds_per_second;
        ContentResolver.addPeriodicSync(account, authority, extras,
                sync_interval);

    }

    public void cancelSync(String authority) {
        Account account = mUser.getAccount();
        ContentResolver.cancelSync(account, authority);
    }
} public class OControls {

    public static void setText(View parent_view, int textview_id, Object value) {
        TextView textView = (TextView) parent_view.findViewById(textview_id);
        if (value instanceof String || value instanceof CharSequence)
            textView.setText(value.toString());
        if (value instanceof Integer)
            textView.setText(Integer.parseInt(value.toString()));
    }

    public static String getText(View parent_view, int textview_id) {
        TextView textView = (TextView) parent_view.findViewById(textview_id);
        return textView.getText().toString();
    }

    public static void toggleViewVisibility(View parent_view, int view_id,
                                            Boolean visible) {
        int view_visibility = (visible) ? View.VISIBLE : View.GONE;
        parent_view.findViewById(view_id).setVisibility(view_visibility);

    }

    public static void setImage(View parent_view, int imageview_id,
                                Bitmap bitmap) {
        ImageView imgView = (ImageView) parent_view.findViewById(imageview_id);
        imgView.setImageBitmap(bitmap);
    }

    public static void setImage(View parent_view, int imageview_id,
                                int drawable_id) {
        ImageView imgView = (ImageView) parent_view.findViewById(imageview_id);
        imgView.setImageResource(drawable_id);
    }

    public static void setVisible(View parent_view, int resource_id) {
        View view = parent_view.findViewById(resource_id);
        view.setVisibility(View.VISIBLE);
    }


    public static void setInvisible(View parent_view, int resource_id) {
        parent_view.findViewById(resource_id).setVisibility(View.INVISIBLE);
    }

    public static void setGone(View parent_view, int resource_id) {
        View view = parent_view.findViewById(resource_id);
        view.setVisibility(View.GONE);
    }

    public static void setTextViewStrikeThrough(View parent, int res_id) {
        TextView tv = (TextView) parent.findViewById(res_id);
        tv.setPaintFlags(tv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
    }


    public static void setTextColor(View parent, int txv_id, int color) {
        TextView tv = (TextView) parent.findViewById(txv_id);
        tv.setTextColor(color);
    }

    public static void makeSetOdooFont(View textView) {
        TextView tv = (TextView) textView;
        Context context = tv.getContext();
        Typeface tf = Typeface.createFromAsset(context.getAssets(), "fonts/Arista.ttf");
        tv.setTypeface(tf,Typeface.NORMAL);
    }

} public class OFragmentUtils {
    public static final String TAG = OFragmentUtils.class.getSimpleName();

    private AppCompatActivity mActivity;
    private Context mContext;
    private Bundle savedInstance = null;
    private FragmentManager fragmentManager;

    public OFragmentUtils(AppCompatActivity activity, Bundle savedInstance) {
        mActivity = activity;
        mContext = activity;
        fragmentManager = mActivity.getSupportFragmentManager();
    }

    public static OFragmentUtils get(AppCompatActivity activity, Bundle savedInstance) {
        return new OFragmentUtils(activity, savedInstance);
    }

    public void startFragment(Fragment fragment, boolean addToBackState, Bundle extra) {
        Bundle extra_data = fragment.getArguments();
        if (extra_data == null)
            extra_data = new Bundle();
        if (extra != null)
            extra_data.putAll(extra);
        fragment.setArguments(extra_data);
        loadFragment(fragment, addToBackState);
    }

    private void loadFragment(Fragment fragment, Boolean addToBackState) {
        String tag = fragment.getClass().getCanonicalName();
        if (fragmentManager.findFragmentByTag(tag) != null && savedInstance != null) {
            fragmentManager.popBackStack(tag, FragmentManager.POP_BACK_STACK_INCLUSIVE);
            fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        }
        if (savedInstance == null) {
            Log.i(TAG, "Fragment Loaded (" + tag + ")");
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                Slide slideIn = new Slide();
                slideIn.setSlideEdge(Gravity.RIGHT);
                fragment.setEnterTransition(slideIn);
                Slide slideOut = new Slide();
                slideOut.setSlideEdge(Gravity.LEFT);
                fragment.setExitTransition(slideOut);
            }
            FragmentTransaction tran = fragmentManager.beginTransaction()
                    .replace(R.id.fragment_container, fragment, tag);

            if (addToBackState)
                tran.addToBackStack(tag);
            tran.commitAllowingStateLoss();
        }
    }

} public class OPreferenceManager {
    public static final String TAG = OPreferenceManager.class.getSimpleName();
    private SharedPreferences mPref = null;
    private Context mContext;

    public OPreferenceManager(Context context) {
        mPref = android.preference.PreferenceManager
                .getDefaultSharedPreferences(context);
        mContext = context;
    }

    public void putString(String key, String value) {
        SharedPreferences.Editor editor = mPref.edit();
        editor.putString(key, value);
        editor.commit();
    }

    public boolean putStringSet(String key, List<String> values) {
        SharedPreferences.Editor editor = mPref.edit();
        editor.putStringSet(key, new HashSet<>(values));
        return editor.commit();
    }

    public List<String> getStringSet(String key) {
        List<String> list = new ArrayList<>();
        Set<String> vals = mPref.getStringSet(key, null);
        if (vals != null)
            list.addAll(vals);
        return list;
    }

    public String getString(String key, String default_value) {
        return mPref.getString(key, default_value);
    }

    public boolean putInt(String key, int value) {
        SharedPreferences.Editor editor = mPref.edit();
        editor.putInt(key, value);
        return editor.commit();
    }

    public int getInt(String key, int default_value) {
        try {
            return Integer.parseInt(mPref.getString(key, default_value + ""));
        } catch (Exception e) {
            return mPref.getInt(key, default_value);
        }
    }

    public void setBoolean(String key, Boolean value) {
        SharedPreferences.Editor editor = mPref.edit();
        editor.putBoolean(key, value);
        editor.commit();
    }

    public boolean getBoolean(String key, boolean defValue) {
        return mPref.getBoolean(key, defValue);
    }

    public void setUserValues(OUser user) {
        Bundle data = user.getAsBundle();
        SharedPreferences.Editor editor = mPref.edit();
        for (String key : data.keySet()) {
            Object val = data.get(key);
            editor.putString(key, val + "");
        }
        editor.putStringSet("user_object_keys", data.keySet());
        editor.commit();

    }

    public boolean hasKey(String key) {
        return mPref.contains(key);
    }

    public OUser getUserValues() {
        OUser user = null;
        if (hasKey("user_object_keys")) {
            user = new OUser();
            Bundle data = new Bundle();
            for (String key : getStringSet("user_object_keys")) {
                data.putString(key, mPref.getString(key, null));
            }
            user.setFromBundle(data);
        }
        return user;
    }

} public class DrawerUtils {

    public static List<ODrawerItem> getDrawerItems(Context context) {
        List<ODrawerItem> items = new ArrayList<>();
        for (OAddon addon : new Addons().getAddons()) {
            IBaseFragment frag = (IBaseFragment) addon.get();
            if (frag != null) {
                List<ODrawerItem> menus = frag.drawerMenus(context);
                if (menus != null) {
                    items.addAll(menus);
                }
            }
        }
        items.addAll(DrawerUtils.baseSettingsItems(context));
        return items;
    }

    public static List<ODrawerItem> baseSettingsItems(Context context) {
        String key = "base.settings";
        OPreferenceManager pref = new OPreferenceManager(context);
        List<ODrawerItem> settings = new ArrayList<>();
        settings.add(new ODrawerItem(key).setTitle(OResource.string(context, R.string.label_settings))
                .setGroupTitle());
        settings.add(new ODrawerItem(key).setTitle(OResource.string(context, R.string.title_profile))
                .setInstance(Profile.class).setIcon(R.drawable.ic_action_user));
        settings.add(new ODrawerItem(key).setTitle(OResource.string(context, R.string.label_settings))
                .setIcon(R.drawable.ic_action_settings)
                .setInstance(SettingsActivity.class));
//        if (pref.getBoolean(Profile.CONNECT_WITH_ODOO, false))
//            settings.add(new ODrawerItem(key).setTitle(OResource.string(context, R.string.label_access_odoo_mobile))
//                    .setInstance(OdooMobileQRReader.class).setIcon(R.drawable.ic_action_qrcode)
//                    .setExtra(OUser.current(context).getAsBundle()));
//        OdooNews news = new OdooNews(context, null);
//        if (!news.isEmptyTable()) {
//            settings.add(new ODrawerItem(key).setTitle("Odoo News")
//                            .setInstance(new News()).setIcon(R.drawable.ic_odoo_o)
//            );
//        }
        return settings;
    }

    public static View fillDrawerItemValue(View view, ODrawerItem item) {
        if (item.isGroupTitle()) {
            OControls.setText(view, R.id.group_title, item.getTitle());
        } else {
            if (item.getIcon() > 0)
                OControls.setImage(view, R.id.icon, item.getIcon());
            else
                view.findViewById(R.id.icon).setVisibility(View.GONE);
            OControls.setText(view, R.id.title, item.getTitle());
            if (item.getCounter() > 0) {
                OControls.setText(view, R.id.counter, item.getCounter() + "");
            }
        }
        return view;
    }

    public static IBaseFragment getDefaultDrawerFragment() {
        OAddon addon = new Addons().getDefaultAddon();
        if (addon != null) {
            return (IBaseFragment) addon.get();
        }
        return null;
    }

    public static ODrawerItem getStartableObject(Context context, IBaseFragment fragment) {
        List<ODrawerItem> items = fragment.drawerMenus(context);
        if (items != null) {
            for (ODrawerItem item : items) {
                if (!item.isGroupTitle() && item.getInstance() != null) {
                    return item;
                }
            }
        }
        return null;
    }

    public static void focusOnView(Context context, View view, boolean focused) {
        ODrawerItem item = (ODrawerItem) view.getTag();
        if (!item.isGroupTitle()) {
            ImageView icon = (ImageView) view.findViewById(R.id.icon);
            TextView title = (TextView) view.findViewById(R.id.title);
            TextView counter = (TextView) view.findViewById(R.id.counter);
            if (focused) {
                icon.setColorFilter(context.getResources().getColor(R.color.drawer_icon_tint_selected));
                title.setTextColor(context.getResources().getColor(R.color.drawer_text_color_selected));
                title.setTypeface(title.getTypeface(), Typeface.BOLD);
                counter.setTextColor(context.getResources().getColor(R.color.drawer_text_color_selected));
                counter.setTypeface(title.getTypeface(), Typeface.BOLD);
            } else {
                icon.setColorFilter(context.getResources().getColor(R.color.drawer_icon_tint));
                title.setTextColor(context.getResources().getColor(R.color.drawer_text_color));
                title.setTypeface(null, Typeface.NORMAL);
                counter.setTextColor(context.getResources().getColor(R.color.drawer_text_color));
                counter.setTypeface(null, Typeface.NORMAL);
            }
        }
    }

    public static Integer findItemIndex(ODrawerItem item, LinearLayout itemContainer) {
        for (int i = 0; i < itemContainer.getChildCount(); i++) {
            ODrawerItem dItem = (ODrawerItem) itemContainer.getChildAt(i).getTag();
            if (dItem != null && dItem.getKey().equals(item.getKey())) {
                return i;
            }
        }
        return -1;

    }
}
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.odoo.addons.products.services.ProductSyncService;
import com.odoo.addons.website_sale.GoogleLoginActivity;
import com.odoo.addons.website_sale.HomeScreen;
import com.odoo.addons.website_sale.ProductCategoryLoader;
import com.odoo.addons.website_sale.ProductSubCategories;
import com.odoo.addons.website_sale.models.ProductPublicCategory;
import com.odoo.core.account.AppIntro;
import com.odoo.core.account.ManageAccounts;
import com.odoo.core.account.OdooLogin;
import com.odoo.core.account.OdooUserAskPassword;
import com.odoo.core.auth.OdooAccountManager;
import com.odoo.core.auth.OdooAuthenticator;
import com.odoo.core.orm.ODataRow;
import com.odoo.core.orm.OModel;
import com.odoo.core.support.OUser;
import com.odoo.core.support.addons.fragment.IBaseFragment;
import com.odoo.core.support.sync.SyncUtils;
import com.odoo.core.utils.BitmapUtils;
import com.odoo.core.utils.OAlert;
import com.odoo.core.utils.OControls;
import com.odoo.core.utils.OFragmentUtils;
import com.odoo.core.utils.OPreferenceManager;
import com.odoo.core.utils.OResource;
import com.odoo.core.utils.drawer.DrawerUtils;
import com.odoo.core.utils.sys.IOnActivityResultListener;
import com.odoo.core.utils.sys.IOnBackPressListener;
import java.util.List;
        }

        // Setting Accounts
        List<OUser> accounts = OdooAccountManager.getAllAccounts(this);
        if (accounts.size() > 0) {
            chosenAccountView.setEnabled(true);
            ImageView boxIndicator = (ImageView) findViewById(R.id.expand_account_box_indicator);
            boxIndicator.setVisibility(View.VISIBLE);
            chosenAccountView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mAccountBoxExpanded = !mAccountBoxExpanded;
                    accountBoxToggle();
                }
            });
            populateAccountList(currentUser, accounts);
        }
        chosenAccountView.setOnClickListener(null);
        if (currentUser.isDummyUser()) {
            chosenAccountView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    startActivityForResult(new Intent(OdooActivity.this, GoogleLoginActivity.class), REQUEST_GOOGLE_ACCOUNT);
                }
            });
        }
    }

    private void accountBoxToggle() {
        ImageView boxIndicator = (ImageView) findViewById(R.id.expand_account_box_indicator);
        boxIndicator.setImageResource(mAccountBoxExpanded ? R.drawable.ic_drawer_accounts_collapse
                : R.drawable.ic_drawer_accounts_expand);
        int hideTranslateY = -mDrawerAccountContainer.getHeight() / 4;
        if (mAccountBoxExpanded && mDrawerAccountContainer.getTranslationY() == 0) {
            // initial setup
            mDrawerAccountContainer.setAlpha(0);
            mDrawerAccountContainer.setTranslationY(hideTranslateY);
        }

        AnimatorSet set = new AnimatorSet();
        set.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                mDrawerItemContainer
                        .setVisibility(mAccountBoxExpanded ? View.INVISIBLE
                                : View.VISIBLE);
                mDrawerAccountContainer
                        .setVisibility(mAccountBoxExpanded ? View.VISIBLE
                                : View.INVISIBLE);
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                onAnimationEnd(animation);
            }
        });

        if (mAccountBoxExpanded) {
            mDrawerAccountContainer.setVisibility(View.VISIBLE);
            AnimatorSet subSet = new AnimatorSet();
            subSet.playTogether(
                    ObjectAnimator
                            .ofFloat(mDrawerAccountContainer, View.ALPHA, 1)
                            .setDuration(DRAWER_ACCOUNT_BOX_ANIMATION_DURATION),
                    ObjectAnimator.ofFloat(mDrawerAccountContainer,
                            View.TRANSLATION_Y, 0).setDuration(
                            DRAWER_ACCOUNT_BOX_ANIMATION_DURATION));
            set.playSequentially(
                    ObjectAnimator.ofFloat(mDrawerItemContainer,
                            View.ALPHA, 0).setDuration(
                            DRAWER_ACCOUNT_BOX_ANIMATION_DURATION), subSet);
            set.start();
        } else {
            mDrawerItemContainer.setVisibility(View.VISIBLE);
            AnimatorSet subSet = new AnimatorSet();
            subSet.playTogether(
                    ObjectAnimator
                            .ofFloat(mDrawerAccountContainer, View.ALPHA, 0)
                            .setDuration(DRAWER_ACCOUNT_BOX_ANIMATION_DURATION),
                    ObjectAnimator.ofFloat(mDrawerAccountContainer,
                            View.TRANSLATION_Y, hideTranslateY).setDuration(
                            DRAWER_ACCOUNT_BOX_ANIMATION_DURATION));
            set.playSequentially(
                    subSet,
                    ObjectAnimator.ofFloat(mDrawerItemContainer,
                            View.ALPHA, 1).setDuration(
                            DRAWER_ACCOUNT_BOX_ANIMATION_DURATION));
            set.start();
        }

        set.start();

    }

    private void populateAccountList(OUser me, List<OUser> accounts) {
        mDrawerAccountContainer.removeAllViews();
        for (final OUser user : accounts) {
            if (!user.getAndroidName().equals(me.getAndroidName())) {
                View view = LayoutInflater.from(this).inflate(R.layout.base_drawer_account_item, mDrawerAccountContainer, false);
                ImageView avatar = (ImageView) view.findViewById(R.id.profile_image);
                if (!user.getAvatar().equals("false")) {
                    Bitmap img = BitmapUtils.getBitmapImage(this, user.getAvatar());
                    if (img != null)
                        avatar.setImageBitmap(img);
                }
                OControls.setText(view, R.id.profile_name_text, user.getName());
                OControls.setText(view, R.id.profile_url_text, (user.isOAuthLogin()) ? user.getInstanceURL() : user.getHost());
                // Setting login event for other account
                view.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                        OdooUserAskPassword.get(OdooActivity.this, user)
                                .setOnUserPasswordValidateListener(
                                        new OdooUserAskPassword.OnUserPasswordValidateListener() {
                                            @Override
                                            public void onSuccess() {
                                                // Logging in to other account
                                                OdooAccountManager.login(OdooActivity.this,
                                                        user.getAndroidName());
                                                OModel.sqLite = null;