net.niyonkuru.koodroid.ui.UsageFragment.java Source code

Java tutorial

Introduction

Here is the source code for net.niyonkuru.koodroid.ui.UsageFragment.java

Source

/*
 * Copyright 2012 Mike Niyonkuru
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.niyonkuru.koodroid.ui;

import android.app.Activity;
import android.app.DatePickerDialog;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.DatePicker;
import android.widget.TextView;

import java.sql.Date;
import java.sql.Timestamp;
import java.text.NumberFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;

import net.niyonkuru.koodroid.R;
import net.niyonkuru.koodroid.provider.AccountContract.Airtimes;
import net.niyonkuru.koodroid.provider.AccountContract.Subscribers;
import net.niyonkuru.koodroid.provider.AccountContract.Usages;
import net.niyonkuru.koodroid.provider.SettingsContract.Settings;
import net.niyonkuru.koodroid.service.SyncService;
import net.niyonkuru.koodroid.util.AnimUtils;
import net.niyonkuru.koodroid.util.IntentUtils;
import net.niyonkuru.koodroid.util.RelativeTime;

/**
 * Displays the billing cycle date, {@link Airtimes} and {@link Usages} information
 */
public class UsageFragment extends PageFragment {
    private static final int AIRTIME_TOKEN = 0x1;
    private static final int DATA_TOKEN = 0x2;
    private static final int TEXT_TOKEN = 0x3;

    private CallBack mCallBack;

    private TextView mPeriodFrom;

    private TextView mAirtimeUsed;
    private TextView mAirtimeRemaining;
    private TextView mAirtimeChargeable;
    private TextView mAirtimeIncluded;
    private TextView mAirtimeUpdateTime;

    private TextView mDataUsed;
    private TextView mDataCharged;
    private TextView mDataUpdatedTime;

    private TextView mTextUsed;
    private TextView mTextCharged;

    public interface CallBack extends BaseCallBack {

        public void onShowDataTransactions(String subscriber);

    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        try {
            mCallBack = (CallBack) activity;

        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement CallBack type");
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.usage_fragment, container, false);

        root.findViewById(R.id.billing_cycle_title).setOnClickListener(this);
        root.findViewById(R.id.billing_cycle_options).setOnClickListener(this);

        root.findViewById(R.id.airtime_title).setOnClickListener(this);
        root.findViewById(R.id.airtime_options).setOnClickListener(this);
        if (isAirtimeEnabled()) {
            root.findViewById(R.id.airtime_content).setVisibility(View.VISIBLE);
        }

        root.findViewById(R.id.data_title).setOnClickListener(this);
        root.findViewById(R.id.data_options).setOnClickListener(this);
        if (isUsageEnabled()) {
            root.findViewById(R.id.data_content).setVisibility(View.VISIBLE);
            root.findViewById(R.id.text_content).setVisibility(View.VISIBLE);
        }

        mPeriodFrom = (TextView) root.findViewById(R.id.billing_cycle_start);
        mPeriodFrom.setTag(mSettings.billingCycleDate());

        mAirtimeUsed = (TextView) root.findViewById(R.id.airtime_used);
        mAirtimeRemaining = (TextView) root.findViewById(R.id.airtime_remaining);
        mAirtimeChargeable = (TextView) root.findViewById(R.id.airtime_chargeable);
        mAirtimeIncluded = (TextView) root.findViewById(R.id.airtime_included);
        mAirtimeUpdateTime = (TextView) root.findViewById(R.id.airtime_usage_update_time);

        mDataUsed = (TextView) root.findViewById(R.id.data_used);
        mDataCharged = (TextView) root.findViewById(R.id.data_charged);
        mDataUpdatedTime = (TextView) root.findViewById(R.id.data_usage_update_time);

        mTextUsed = (TextView) root.findViewById(R.id.text_used);
        mTextCharged = (TextView) root.findViewById(R.id.text_charged);

        return root;
    }

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

        LoaderManager loaderManager = getLoaderManager();
        if (isAirtimeEnabled()) {
            loaderManager.initLoader(AIRTIME_TOKEN, null, this);
        }
        if (isUsageEnabled()) {
            loaderManager.initLoader(DATA_TOKEN, null, this);
            loaderManager.initLoader(TEXT_TOKEN, null, this);
        }
    }

    @Override
    public void onResume() {
        super.onResume();

        final ContentResolver cr = mContext.getContentResolver();
        final Uri billingCycleUri = Uri.withAppendedPath(Settings.CONTENT_URI, Settings.BILLING_CYCLE);
        cr.registerContentObserver(billingCycleUri, false, mBillingCycleChangeObserver);
    }

    @Override
    public void onPause() {
        super.onPause();

        final ContentResolver cr = mContext.getContentResolver();
        cr.unregisterContentObserver(mBillingCycleChangeObserver);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();

        LoaderManager loaderManager = getLoaderManager();
        if (isAirtimeEnabled()) {
            loaderManager.destroyLoader(AIRTIME_TOKEN);
        }
        if (isUsageEnabled()) {
            loaderManager.destroyLoader(DATA_TOKEN);
            loaderManager.destroyLoader(TEXT_TOKEN);
        }
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();

        switch (id) {
        case R.id.billing_cycle_options: {
            PopupMenuBuilder builder = new PopupMenuBuilder(mContext, v);
            builder.addMenuResource(R.menu.billing_cycle_overflow_menu).show(this);

            break;
        }
        case R.id.airtime_options: {
            PopupMenuBuilder builder = new PopupMenuBuilder(mContext, v);

            if (isAirtimeEnabled()) {
                builder.addMenuResource(R.menu.airtime_overflow_menu);
                builder.addMenuItemResource(R.id.disable_airtime, R.string.disable);
            } else {
                builder.addMenuItemResource(R.id.enable_airtime, R.string.enable);
            }
            builder.show(this);

            break;
        }
        case R.id.data_options: {
            PopupMenuBuilder builder = new PopupMenuBuilder(mContext, v);

            if (isUsageEnabled()) {
                builder.addMenuResource(R.menu.data_overflow_menu);
                builder.addMenuItemResource(R.id.disable_data, R.string.disable);
            } else {
                builder.addMenuItemResource(R.id.enable_data, R.string.enable);
            }
            builder.show(this);

            break;
        }
        default:
            super.onClick(v);
        }

    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        int id = item.getItemId();

        switch (id) {
        case R.id.edit_billing_cycle:
            Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
            cal.setTime((Date) mPeriodFrom.getTag());

            DatePickerDialog dialog = new DatePickerDialog(getActivity(), new DatePickerDialog.OnDateSetListener() {
                @Override
                public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
                    ContentValues values = new ContentValues(1);
                    values.put(Settings.BILLING_CYCLE, dayOfMonth);
                    mContext.getContentResolver().insert(Settings.CONTENT_URI, values);
                }
            }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DATE));

            dialog.setMessage(getResources().getString(R.string.billing_cycle_dialog));

            //                DatePicker datePicker = dialog.getDatePicker();
            //                datePicker.setMaxDate(System.currentTimeMillis());

            dialog.show();

            return true;
        case R.id.enable_airtime: {
            getLoaderManager().restartLoader(AIRTIME_TOKEN, null, this);
        }
        case R.id.disable_airtime:
            View airtimeContent = getView().findViewById(R.id.airtime_content);
            AnimUtils.show(airtimeContent);

            toggleService(Settings.AIRTIME_SERVICE, id == R.id.enable_airtime);

            /* refresh the widget to show or hide data */
            IntentUtils.updateWidget(mContext);

            return true;
        case R.id.refresh_airtime:
            sync(SyncService.AIRTIME);
            return true;
        case R.id.enable_data: {
            LoaderManager loaderManager = getLoaderManager();

            loaderManager.restartLoader(DATA_TOKEN, null, this);
            loaderManager.restartLoader(TEXT_TOKEN, null, this);
        }
        case R.id.disable_data:
            View dataContent = getView().findViewById(R.id.data_content);
            AnimUtils.show(dataContent);

            View textContent = getView().findViewById(R.id.text_content);
            AnimUtils.show(textContent);

            toggleService(Settings.USAGE_SERVICE, id == R.id.enable_data);

            /* refresh the widget to show or hide data */
            IntentUtils.updateWidget(mContext);

            return true;
        case R.id.refresh_data:
            sync(SyncService.USAGE);
            return true;
        case R.id.detail_data:
            mCallBack.onShowDataTransactions(getSubscriber());
            return true;
        }

        return false;
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        switch (id) {
        case AIRTIME_TOKEN: {
            final Uri uri = Airtimes.buildAirtimeUri(getSubscriber());
            return new CursorLoader(mContext, uri, AirtimesQuery.PROJECTION, null, null, null);
        }
        case TEXT_TOKEN: {
            final Resources resources = mContext.getResources();

            final String textUsage = resources.getString(R.string.parser_usage_service_text);
            final String intlTextUsage = resources.getString(R.string.parser_usage_service_intl);

            final String selection = Usages.USAGE_SERVICE + "='" + textUsage + "'" + " OR " + Usages.USAGE_SERVICE
                    + "='" + intlTextUsage + "'";

            CursorLoader loader = new CursorLoader(mContext, Subscribers.buildUsagesUri(getSubscriber()),
                    UsagesQuery.PROJECTION, selection, null, null);
            loader.setUpdateThrottle(DateUtils.SECOND_IN_MILLIS);

            return loader;
        }
        case DATA_TOKEN: {
            final String dataUsage = getResources().getString(R.string.parser_usage_service_data);

            final String selection = Usages.USAGE_SERVICE + "='" + dataUsage + "'";

            CursorLoader loader = new CursorLoader(mContext, Subscribers.buildUsagesUri(getSubscriber()),
                    UsagesQuery.PROJECTION, selection, null, null);
            loader.setUpdateThrottle(DateUtils.SECOND_IN_MILLIS);

            return loader;
        }
        }

        return null;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        int id = loader.getId();

        switch (id) {
        case AIRTIME_TOKEN: {
            String used = null;
            String included = null;
            String chargeable = null;
            String remaining = null;
            Object timestamp = null;

            if (data.moveToFirst()) {
                used = data.getString(AirtimesQuery.USED);
                included = data.getString(AirtimesQuery.INCLUDED);
                chargeable = data.getString(AirtimesQuery.CHARGEABLE);
                remaining = data.getString(AirtimesQuery.REMAINING);
                timestamp = Timestamp.valueOf(data.getString(AirtimesQuery.UPDATED));
            }
            mAirtimeUsed.setText(used);
            mAirtimeIncluded.setText(included);
            mAirtimeChargeable.setText(chargeable);
            mAirtimeRemaining.setText(remaining);
            mAirtimeUpdateTime.setTag(timestamp);
            break;
        }
        case TEXT_TOKEN: {
            data.moveToFirst();

            mTextUsed.setText(data.getString(UsagesQuery.COUNT));

            mTextCharged.setText(formatMoney(data.getString(UsagesQuery.AMOUNT)));

            saveUsageTimestamps(data);
            break;
        }
        case DATA_TOKEN: {
            data.moveToFirst();

            mDataUsed.setText(UsageUtils.format(mContext, data.getDouble(UsagesQuery.COUNT), getNumberFormat()));

            mDataCharged.setText(formatMoney(data.getString(UsagesQuery.AMOUNT)));

            saveUsageTimestamps(data);
            break;
        }
        }

        updateTimestamps();
    }

    @Override
    protected void updateTimestamps() {
        RelativeTime t = new RelativeTime(mContext.getResources());
        t.format(mPeriodFrom);
        t.format(mAirtimeUpdateTime);
        t.format(mDataUpdatedTime);
    }

    private void saveUsageTimestamps(Cursor cursor) {
        if (!cursor.isNull(UsagesQuery.UPDATED)) {
            Timestamp new_timestamp = Timestamp.valueOf(cursor.getString(UsagesQuery.UPDATED));
            Object old_timestamp = mDataUpdatedTime.getTag();

            if (old_timestamp == null || new_timestamp.after((Timestamp) old_timestamp)) {
                mDataUpdatedTime.setTag(new_timestamp);
            }
        }
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        int id = loader.getId();

        switch (id) {
        case AIRTIME_TOKEN:
            mAirtimeUpdateTime.setText(null);
            break;
        case DATA_TOKEN:
        case TEXT_TOKEN:
            mDataUpdatedTime.setText(null);
            break;
        }
    }

    @Override
    protected void sync() {
        if (isAirtimeEnabled()) {
            sync(SyncService.AIRTIME);
        }

        if (isUsageEnabled()) {
            sync(SyncService.USAGE);
        }
    }

    private boolean isAirtimeEnabled() {
        return mSettings.serviceAllowed(getSubscriber(), Settings.AIRTIME_SERVICE);
    }

    private boolean isUsageEnabled() {
        return mSettings.serviceAllowed(getSubscriber(), Settings.USAGE_SERVICE);
    }

    /**
     * {@link Airtimes} query parameters
     */
    public interface AirtimesQuery {
        String[] PROJECTION = { Airtimes.UPDATED_LOCAL, Airtimes.AIRTIME_USED, Airtimes.AIRTIME_REMAINING,
                Airtimes.AIRTIME_CHARGEABLE, Airtimes.AIRTIME_INCLUDED };

        int UPDATED = 0;
        int USED = 1;
        int REMAINING = 2;
        int CHARGEABLE = 3;
        int INCLUDED = 4;
    }

    /**
     * {@link Usages} query parameters
     */
    public interface UsagesQuery {
        String[] PROJECTION = { Usages.USAGE_SUMMARY_UPDATED, Usages.USAGE_SUMMARY_COUNT,
                Usages.USAGE_SUMMARY_AMOUNT };

        int UPDATED = 0;
        int COUNT = 1;
        int AMOUNT = 2;
    }

    /*
      * When the billing cycle changes, reload data usage summaries.
      */
    private final ContentObserver mBillingCycleChangeObserver = new ContentObserver(sHandler) {
        @Override
        public void onChange(boolean selfChange) {
            Date billingCycle = mSettings.billingCycleDate();

            /* make sure the new billing cycle is different */
            if (billingCycle.equals(mPeriodFrom.getTag()))
                return;

            mPeriodFrom.setTag(billingCycle);

            LoaderManager loaderManager = getLoaderManager();

            loaderManager.restartLoader(AIRTIME_TOKEN, null, UsageFragment.this);
            loaderManager.restartLoader(DATA_TOKEN, null, UsageFragment.this);
            loaderManager.restartLoader(TEXT_TOKEN, null, UsageFragment.this);

            IntentUtils.updateWidget(mContext);
        }
    };

    public static class UsageUtils {
        public static final double BASE = 1024, KB = BASE, MB = KB * BASE;

        private UsageUtils() {
        }

        public static String format(Context context, double count, NumberFormat number) {
            if (count >= MB) {
                number.setMaximumFractionDigits(2);

                return number.format(count / MB) + " " + context.getString(R.string.gigabyte);
            } else if (count >= KB) {
                number.setMaximumFractionDigits(1);

                return number.format(count / KB) + " " + context.getString(R.string.megabyte);
            } else {
                number.setMaximumFractionDigits(0);

                return number.format(count) + " " + context.getString(R.string.kilobyte);
            }
        }
    }
}