Java tutorial
/* * 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); } } } }