com.ntsync.android.sync.shared.SyncUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.ntsync.android.sync.shared.SyncUtils.java

Source

package com.ntsync.android.sync.shared;

/*
 * Copyright (C) 2014 Markus Grieder
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>. 
 */

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Semaphore;

import org.json.JSONException;
import org.json.JSONObject;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.PeriodicSync;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.text.TextUtils;
import android.util.Log;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ntsync.shared.Restrictions;

public final class SyncUtils {

    private static final Semaphore CHANGES_LOCK = new Semaphore(1);

    private static final String SYNC_RESTRICTIONS_PHOTOSUPPORT = "com.ntsync.android.sync.restrict.photosupport";
    private static final String SYNC_RESTRICTIONS_MAXCONTACTS = "com.ntsync.android.sync.restrict.maxcontacts";
    private static final String SYNC_RESTRICTIONS_MAXGROUPS = "com.ntsync.android.sync.restrict.maxgroups";
    private static final String SYNC_RESTRICTIONS_VALIDUNTIL = "com.ntsync.android.sync.restrict.validuntil";
    private static final String SYNC_STATE = "com.ntsync.android.sync.state";

    private static final String LAST_PAYMENT = "com.ntsync.android.sync.lastpayment";

    /** Default Interval in seconds */
    private static final int DEFAULT_SYNCINTERVAL = 1440;

    private final static String TAG = "SyncUtils";

    /** Prevents more than one Payment-Verification at the same time */
    private static final Semaphore VERIFICATION_LOCK = new Semaphore(1);

    private SyncUtils() {

    }

    /**
     * 
     * @param authority
     *            the provider to specify in the sync request
     * @param extras
     *            extra parameters to go along with the sync request
     * @param frequency
     *            in seconds
     * @param context
     */
    public static void addPeriodicSync(String authority, Bundle extras, long frequency, Context context) {

        AccountManager am = AccountManager.get(context);
        Account[] accounts = am.getAccountsByType(Constants.ACCOUNT_TYPE);
        for (Account ac : accounts) {
            ContentResolver.addPeriodicSync(ac, authority, extras, frequency);
        }
    }

    /**
     * Check if a Sync is active
     * 
     * @param accountName
     * @return true if a Sync is active with the current Account Name
     */
    public static boolean isSyncActive(String accountName) {
        Account account = new Account(accountName, Constants.ACCOUNT_TYPE);
        return ContentResolver.isSyncActive(account, ContactsContract.AUTHORITY);
    }

    public static void removePeriodicSync(String authority, Bundle extras, Context context) {
        AccountManager am = AccountManager.get(context);
        Account[] accounts = am.getAccountsByType(Constants.ACCOUNT_TYPE);
        for (Account ac : accounts) {
            ContentResolver.removePeriodicSync(ac, authority, extras);
        }
    }

    /**
     * Get the last saved Sync Result for an account.
     * 
     * @param accountManager
     * @param account
     * @return Sync Result or null if there was not saved Result.
     */
    public static AccountSyncResult getSyncResult(AccountManager accountManager, Account account) {
        String syncState = accountManager.getUserData(account, SYNC_STATE);

        AccountSyncResult result = null;
        if (syncState != null) {
            ObjectMapper mapper = new ObjectMapper();
            try {
                result = mapper.readValue(syncState, AccountSyncResult.class);
            } catch (IOException e) {
                LogHelper.logW(TAG,
                        "Could not parse AccountSyncResult for account: " + account + " Value:" + syncState, e);
            }
        }
        return result;
    }

    /**
     * Set Sync-Result for an account.
     * 
     * @param accountManager
     * @param account
     * @param syncResult
     */
    public static void setSyncResult(AccountManager accountManager, Account account, AccountSyncResult syncResult) {

        String strVal = null;
        if (syncResult != null) {
            ObjectMapper mapper = new ObjectMapper();
            try {
                strVal = mapper.writeValueAsString(syncResult);
            } catch (JsonProcessingException e) {
                LogHelper.logE(TAG, "Could not save AccountSyncResult for account: " + account, e);
            }
        }
        accountManager.setUserData(account, SYNC_STATE, strVal);
    }

    /**
        * Stop temporarily all DataChange processing, should be in a finally with
        * {@link #startProcessDataChanges());
        * @throws InterruptedException 
        */
    public static void stopProcessDataChanges() throws InterruptedException {
        CHANGES_LOCK.acquire();
    }

    public static void startProcessDataChanges() {
        CHANGES_LOCK.release();
    }

    public static boolean processDataChanges() {
        return CHANGES_LOCK.availablePermits() > 0;
    }

    /**
     * 
     * @return false if Verification is already running
     * @throws InterruptedException
     */
    public static boolean startPaymentVerification() {
        return VERIFICATION_LOCK.tryAcquire();
    }

    public static void stopPaymentVerification() {
        if (VERIFICATION_LOCK.availablePermits() == 0) {
            VERIFICATION_LOCK.release();
        }
    }

    public static boolean isPaymentVerificationStarted() {
        return VERIFICATION_LOCK.availablePermits() == 0;
    }

    /**
     * Get Restrictions
     * 
     * @param account
     *            the account we're syncing
     * @return null if restrictions were never saved before.
     */
    public static Restrictions getRestrictions(Account account, AccountManager accountManager) {
        boolean photoSupport = false;
        int maxContacts = Integer.MAX_VALUE;
        int maxGroups = Integer.MAX_VALUE;
        Date validUntil = null;

        boolean foundRestr = false;
        String photoSupportStr = accountManager.getUserData(account, SYNC_RESTRICTIONS_PHOTOSUPPORT);
        if (!TextUtils.isEmpty(photoSupportStr)) {
            photoSupport = Boolean.parseBoolean(photoSupportStr);
            foundRestr = true;
        }
        String maxContactStr = accountManager.getUserData(account, SYNC_RESTRICTIONS_MAXCONTACTS);
        if (!TextUtils.isEmpty(maxContactStr)) {
            maxContacts = Integer.parseInt(maxContactStr);
            foundRestr = true;
        }
        String maxGroupStr = accountManager.getUserData(account, SYNC_RESTRICTIONS_MAXGROUPS);
        if (!TextUtils.isEmpty(maxGroupStr)) {
            maxGroups = Integer.parseInt(maxGroupStr);
            foundRestr = true;
        }
        String validUntilStr = accountManager.getUserData(account, SYNC_RESTRICTIONS_VALIDUNTIL);
        if (!TextUtils.isEmpty(validUntilStr)) {
            validUntil = new Date(Long.parseLong(validUntilStr));
        }

        Restrictions restr = null;
        if (foundRestr) {
            restr = new Restrictions(maxContacts, maxGroups, photoSupport, validUntil);
        }

        return restr;
    }

    /**
     * Save Restrictions
     * 
     * @param account
     * @param accountManager
     * @param restr
     *            could be null, then Restriction-Info will be deleted
     */
    public static void saveRestrictions(Account account, AccountManager accountManager, Restrictions restr) {
        String photoSupportStr = restr != null ? Boolean.toString(restr.isPhotoSyncSupported()) : null;
        accountManager.setUserData(account, SYNC_RESTRICTIONS_PHOTOSUPPORT, photoSupportStr);

        String maxContactsStr = restr != null ? String.valueOf(restr.getMaxContactCount()) : null;
        accountManager.setUserData(account, SYNC_RESTRICTIONS_MAXCONTACTS, maxContactsStr);

        String maxGroupStr = restr != null ? String.valueOf(restr.getMaxGroupCount()) : null;
        accountManager.setUserData(account, SYNC_RESTRICTIONS_MAXGROUPS, maxGroupStr);

        Date validDate = restr != null ? restr.getValidUntil() : null;
        accountManager.setUserData(account, SYNC_RESTRICTIONS_VALIDUNTIL,
                validDate != null ? String.valueOf(validDate.getTime()) : null);
    }

    /**
     * Stores a Payment to make sure that the Payment will be verified and
     * processed.
     * 
     * @param account
     * @param accountManager
     * @param paymentConf
     *            null: clear any stored information.
     * @param priceId
     *            null: clear any stored information
     */
    public static void savePayment(Account account, AccountManager accountManager, JSONObject paymentConf,
            UUID priceId) {
        String storeValue = priceId == null || paymentConf == null ? null
                : priceId.toString() + ";" + paymentConf.toString() + ";" + System.currentTimeMillis();
        accountManager.setUserData(account, LAST_PAYMENT, storeValue);
    }

    /**
     * Get a stored PaymentConfirmation (has to be verified on the server).
     * 
     * @param account
     * @param accountManager
     * @return null if none is available.
     */
    public static PaymentData getPayment(Account account, AccountManager accountManager) {
        PaymentData payment = null;
        String paymentData = accountManager.getUserData(account, LAST_PAYMENT);
        if (paymentData != null) {
            int pos1 = paymentData.indexOf(';');
            int pos2 = paymentData.lastIndexOf(';');
            int startTimePos = pos2 + 1;
            if (pos1 > 0 && pos2 > 0 && pos2 > pos1 && startTimePos < paymentData.length()) {
                try {
                    long paymentSaveDate = Long.parseLong(paymentData.substring(startTimePos));
                    UUID priceId = UUID.fromString(paymentData.substring(0, pos1));
                    JSONObject obj = new JSONObject(paymentData.substring(pos1 + 1, pos2));
                    payment = new PaymentData(priceId, paymentSaveDate, obj);
                } catch (JSONException ex) {
                    Log.w(TAG, "Invalid PaymentConfirmation data. Data Ignored", ex);
                } catch (IllegalArgumentException ex) {
                    Log.w(TAG, "Invalid PaymentConfirmation data. Data Ignored", ex);
                }
            }
            if (payment == null) {
                // Remove invalid PaymentData
                accountManager.setUserData(account, LAST_PAYMENT, null);
            }
        }

        return payment;
    }

    /**
     * Checks if some PaymentData is not yet verified
     * 
     * @param context
     * @return true if some PaymentData is open to verify
     */
    public static boolean hasPaymentData(Context context) {
        boolean foundPaymentData = false;
        AccountManager acm = AccountManager.get(context);
        Account[] accounts = acm.getAccountsByType(Constants.ACCOUNT_TYPE);
        for (Account account : accounts) {
            PaymentData paymentData = SyncUtils.getPayment(account, acm);
            if (paymentData != null) {
                foundPaymentData = true;
                break;
            }
        }
        return foundPaymentData;
    }

    /**
     * Check if a network is active and connected.
     * 
     * @param context
     * @return true if an active network is available.
     */
    public static boolean isNetworkConnected(Context context) {
        ConnectivityManager cMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetwork = cMgr.getActiveNetworkInfo();
        return activeNetwork != null && activeNetwork.isConnected();
    }

    /**
     * Create a new Account and activate the automatic sync
     * 
     * @param account
     *            null is not allowed
     * @param accountManager
     *            null is not allowed
     * @param password
     *            null is not allowed
     */
    public static boolean createAccount(Context context, final Account account, AccountManager accountManager,
            String password) {
        boolean added = accountManager.addAccountExplicitly(account, password, null);
        if (added) {
            List<PeriodicSync> syncs = ContentResolver.getPeriodicSyncs(account, ContactsContract.AUTHORITY);
            if (syncs != null) {
                // Remove default syncs.
                for (PeriodicSync periodicSync : syncs) {
                    ContentResolver.removePeriodicSync(account, ContactsContract.AUTHORITY, periodicSync.extras);
                }
            }
            SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
            int synctime;
            try {
                synctime = settings.getInt("pref_synctime", DEFAULT_SYNCINTERVAL);
            } catch (ClassCastException e) {
                LogHelper.logI(TAG, "Invalid SyncTime-Settingvalue", e);
                synctime = DEFAULT_SYNCINTERVAL;
            }
            if (synctime != 0) {
                addPeriodicSync(ContactsContract.AUTHORITY, Bundle.EMPTY, synctime, context);
            }

            // Set contacts sync for this account.
            ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
        } else {
            LogHelper.logI(TAG, "Account " + account.name + " is already available.");
        }
        return added;
    }

    /**
     * Struct-Class for transporting temporary saved PaymentConfiguration-Data
     */
    public static class PaymentData {

        public final UUID priceId;
        public final long paymentSaveDate;

        /** JSON-Object of PaymentConfirmation */
        public final JSONObject paymentConfirmation;

        public PaymentData(UUID priceId, long paymentSaveDate, JSONObject paymentConfirmation) {
            super();
            this.priceId = priceId;
            this.paymentSaveDate = paymentSaveDate;
            this.paymentConfirmation = paymentConfirmation;
        }
    }
}