Java tutorial
/* * Copyright (C) 2015 Jackson Thuraisamy. * * 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/>. */ package com.github.jthuraisamy.yellowusage.ui; import android.annotation.SuppressLint; import android.app.DownloadManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.PorterDuff; import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.CardView; import android.support.v7.widget.Toolbar; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.Switch; import android.widget.TextView; import android.widget.Toast; import com.github.jthuraisamy.yellowusage.Constants; import com.github.jthuraisamy.yellowusage.R; import com.github.jthuraisamy.yellowusage.models.Balance; import com.github.jthuraisamy.yellowusage.models.usage.data.DataUsage; import com.github.jthuraisamy.yellowusage.models.usage.messaging.TextMessageUsage; import com.github.jthuraisamy.yellowusage.models.usage.voice.PeakTimeVoiceUsage; import com.github.jthuraisamy.yellowusage.parsers.BalanceParser; import com.github.jthuraisamy.yellowusage.parsers.DataParser; import com.github.jthuraisamy.yellowusage.parsers.MessageParser; import com.github.jthuraisamy.yellowusage.parsers.VoiceParser; import com.github.jthuraisamy.yellowusage.utils.OverageFeesHelper; import com.github.jthuraisamy.yellowusage.utils.PreferenceHelper; import com.sothree.slidinguppanel.SlidingUpPanelLayout; import com.sothree.slidinguppanel.SlidingUpPanelLayout.PanelSlideListener; import com.sothree.slidinguppanel.SlidingUpPanelLayout.PanelState; import java.io.IOException; import java.net.UnknownHostException; import java.text.DateFormat; import java.util.LinkedList; import java.util.Queue; import io.realm.Realm; import io.realm.RealmChangeListener; import static com.github.jthuraisamy.yellowusage.AccountInfoService.startActionRefreshUI; import static com.github.jthuraisamy.yellowusage.parsers.DataParser.humanReadableByteCount; import static com.github.jthuraisamy.yellowusage.utils.NetworkHelper.isAirplaneModeOn; import static com.github.jthuraisamy.yellowusage.utils.NetworkHelper.isConnectedNetworkOperatorValid; import static com.github.jthuraisamy.yellowusage.utils.NetworkHelper.isMobileDataConnected; import static com.github.jthuraisamy.yellowusage.utils.NetworkHelper.isMobileDataEnabled; import static com.github.jthuraisamy.yellowusage.utils.NetworkHelper.isWifiConnected; import static com.github.jthuraisamy.yellowusage.utils.NetworkHelper.setMobileDataEnabled; import static com.github.jthuraisamy.yellowusage.utils.UIHelper.refreshUiSection; import static java.text.DateFormat.getDateInstance; public class MainActivity extends ActionBarActivity { private static final String TAG = MainActivity.class.getSimpleName(); private Realm realm; private String networkBrand; private int ongoingTasks; private Queue<AsyncTask<Void, Void, Void>> taskQueue = new LinkedList<>(); // APIs for managing mobile connectivity. private ConnectivityManager connectivityManager; private WifiManager wifiManager; private TelephonyManager telephonyManager; private boolean wifiWasEnabled; // Menu elements. private View refreshItemActionView; // Data usage card UI elements. private CardView dataUsageCard; private TextView dataUsagePrimarySubheader; private TextView dataUsagePrimaryValue; private TextView dataUsageSecondaryValue; private ProgressBar dataUsageProgress; private Switch dataProtectionSwitch; // Voice usage card UI elements. private CardView voiceUsageCard; private TextView voiceUsagePrimarySubheader; private TextView voiceUsagePrimaryValue; private TextView voiceUsageSecondaryValue; private ProgressBar voiceUsageProgress; private Switch voiceProtectionSwitch; // Messaging usage card UI elements. private CardView messagingUsageCard; private TextView messagingUsagePrimarySubheader; private TextView messagingUsagePrimaryValue; private TextView messagingUsageSecondaryValue; private ProgressBar messagingUsageProgress; private Switch messagingProtectionSwitch; // Sliding panel UI elements. private SlidingUpPanelLayout feesPanel; private Toolbar bottomBar; private ImageView panelStateIcon; private TextView overageStatus; private TextView planNameHeader; private TextView totalAmountDueValue; private TextView paymentDueDateValue; private TextView nextBillDateValue; private TextView dataOverageFeesValue; private TextView weekdayMinutesOverageFeesValue; /** * Listener for phone data state. Run any and all queued tasks once data is connected. */ private PhoneStateListener phoneStateListener = new PhoneStateListener() { @Override public void onDataConnectionStateChanged(int state) { super.onDataConnectionStateChanged(state); Log.i(TAG, "phoneStateListener: state = " + String.valueOf(state) + "."); if (state == TelephonyManager.DATA_CONNECTED) { Log.i(TAG, "phoneStateListener: Data is connected."); if (!taskQueue.isEmpty()) { // Stop prepare refresh animation in the app bar via onPrepareOptionsMenu(). // supportInvalidateOptionsMenu(); runTasks(); } } } }; /** * Listener for AsyncTask requests. */ private final BroadcastReceiver asyncTaskReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // Don't add the task if mobile data is not enabled. if (!isMobileDataEnabled(connectivityManager)) return; String section = intent.getStringExtra(Constants.EXTRA_SECTION); switch (section) { case Constants.BALANCE: taskQueue.add(new BalanceTask()); break; case Constants.DATA: taskQueue.add(new DataUsageTask()); break; case Constants.VOICE_PEAKTIME: taskQueue.add(new VoiceUsageTask()); break; case Constants.MESSAGING_TEXT: taskQueue.add(new MessagingUsageTask()); break; default: break; } Log.d(TAG, "runTasks: asyncTaskReceiver - " + section); if (!taskQueue.isEmpty()) runTasks(); } }; /** * Listener for UI refreshes. Refresh UI after retrieving data for applicable models. */ private final BroadcastReceiver uiRefreshReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String used, included, secondaryValue; boolean isRemaining, isUnlimited, isOverage; String section = intent.getStringExtra(Constants.EXTRA_SECTION); Log.i(TAG, "Refreshing section: " + section + "."); switch (section) { case Constants.BALANCE: // Get Realm model. Balance balance = realm.where(Balance.class).findFirst(); // Populate values in the fees panel. totalAmountDueValue.setText(balance.getTotalAmountDue()); paymentDueDateValue.setText(getDateInstance(DateFormat.FULL).format(balance.getPaymentDueDate())); nextBillDateValue.setText(getDateInstance(DateFormat.FULL).format(balance.getNextBillDate())); break; case Constants.DATA: // Get Realm models. balance = realm.where(Balance.class).findFirst(); DataUsage dataUsage = realm.where(DataUsage.class).findFirst(); // Format and populate secondary values. used = humanReadableByteCount(dataUsage.getUsed()); included = (dataUsage.getIncluded() < 0) ? getString(R.string.unlimited) : humanReadableByteCount(dataUsage.getIncluded()); secondaryValue = String.format(getString(R.string.subheader_secondary_value), used, included); dataUsageSecondaryValue.setText(secondaryValue); // Set plan name. planNameHeader.setText(balance.getPlanName()); // Set boolean values for usage states. isRemaining = dataUsage.getRemaining() > 0; isUnlimited = dataUsage.getRemaining() < 0; isOverage = dataUsage.getOverage() > 0; // If there is usage remaining: if (isRemaining) { // Format and set primary values. dataUsagePrimarySubheader.setText(R.string.subheader_remaining); dataUsagePrimaryValue.setText(humanReadableByteCount(dataUsage.getRemaining())); // Set progress bar to proportional values. int progress = (int) (dataUsage.getUsed() * 100.0 / dataUsage.getIncluded() + 0.5); dataUsageProgress.setMax(100); dataUsageProgress.setProgress(progress); // Color primary value to be proportionate to amount of used data. if (progress >= 90) { dataUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_orange_900)); } else if (progress >= 75) { dataUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_amber_800)); } else { dataUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_green_500)); } // Make usage protection switch visible. dataProtectionSwitch.setVisibility(View.VISIBLE); // If usage is unlimited: } else if (isUnlimited) { // Format and set primary values. dataUsagePrimarySubheader.setText(R.string.subheader_remaining); dataUsagePrimaryValue.setText(R.string.unlimited); dataUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_green_500)); // Set progress bar to green. dataUsageProgress.setMax(100); dataUsageProgress.setProgress(100); dataUsageProgress.getProgressDrawable() .setColorFilter(getResources().getColor(R.color.md_green_500), PorterDuff.Mode.SRC_IN); // If there is an overage: } else if (isOverage) { // Format and set primary values. dataUsagePrimarySubheader.setText(R.string.subheader_overage); dataUsagePrimaryValue.setText(humanReadableByteCount(dataUsage.getOverage())); dataUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_red_700)); // Set progress bar to red. dataUsageProgress.setMax(100); dataUsageProgress.setProgress(100); dataUsageProgress.getProgressDrawable() .setColorFilter(getResources().getColor(R.color.md_red_700), PorterDuff.Mode.SRC_IN); // Make usage protection switch visible. dataProtectionSwitch.setVisibility(View.VISIBLE); } break; case Constants.VOICE_PEAKTIME: // Get Realm model. PeakTimeVoiceUsage peakTimeVoiceUsage = realm.where(PeakTimeVoiceUsage.class).findFirst(); // Format and set secondary values. used = String.format("%d min", peakTimeVoiceUsage.getUsed()); included = (peakTimeVoiceUsage.getIncluded() < 0) ? getString(R.string.unlimited) : String.format("%d min", peakTimeVoiceUsage.getIncluded()); secondaryValue = String.format(getString(R.string.subheader_secondary_value), used, included); voiceUsageSecondaryValue.setText(secondaryValue); // Set boolean values for usage states. isRemaining = peakTimeVoiceUsage.getRemaining() > 0; isUnlimited = peakTimeVoiceUsage.getRemaining() < 0; isOverage = peakTimeVoiceUsage.getOverage() > 0; // If there is usage remaining: if (isRemaining) { // Format and set primary values. voiceUsagePrimarySubheader.setText(R.string.subheader_remaining); voiceUsagePrimaryValue.setText(String.format("%d min", peakTimeVoiceUsage.getRemaining())); voiceUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_green_500)); // Set progress bar to proportional values. int progress = (int) (peakTimeVoiceUsage.getUsed() * 100.0 / peakTimeVoiceUsage.getIncluded() + 0.5); voiceUsageProgress.setMax(100); voiceUsageProgress.setProgress(progress); // Color primary value to be proportionate to amount of used data. if (progress >= 90) { voiceUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_orange_900)); } else if (progress >= 75) { voiceUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_amber_800)); } else { voiceUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_green_500)); } // Make usage protection switch visible. voiceProtectionSwitch.setVisibility(View.VISIBLE); // If usage is unlimited: } else if (isUnlimited) { // Format and set primary values. voiceUsagePrimarySubheader.setText(R.string.subheader_remaining); voiceUsagePrimaryValue.setText(R.string.unlimited); voiceUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_green_500)); // Set progress bar to green. voiceUsageProgress.setMax(100); voiceUsageProgress.setProgress(100); voiceUsageProgress.getProgressDrawable() .setColorFilter(getResources().getColor(R.color.md_green_500), PorterDuff.Mode.SRC_IN); // If there is an overage: } else if (isOverage) { // Format and set primary values. voiceUsagePrimarySubheader.setText(R.string.subheader_overage); voiceUsagePrimaryValue.setText(String.format("%d min", peakTimeVoiceUsage.getOverage())); voiceUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_red_700)); // Set progress bar to red. voiceUsageProgress.setMax(100); voiceUsageProgress.setProgress(100); voiceUsageProgress.getProgressDrawable() .setColorFilter(getResources().getColor(R.color.md_red_700), PorterDuff.Mode.SRC_IN); // Make usage protection switch visible. voiceProtectionSwitch.setVisibility(View.VISIBLE); } break; case Constants.MESSAGING_TEXT: // Get Realm model. TextMessageUsage textMessageUsage = realm.where(TextMessageUsage.class).findFirst(); // Format and set secondary values. used = String.valueOf(textMessageUsage.getUsed()); included = (textMessageUsage.getIncluded() < 0) ? getString(R.string.unlimited) : String.valueOf(textMessageUsage.getIncluded()); secondaryValue = String.format(getString(R.string.subheader_secondary_value), used, included); messagingUsageSecondaryValue.setText(secondaryValue); // Set boolean values for usage states. isRemaining = textMessageUsage.getRemaining() > 0; isUnlimited = textMessageUsage.getRemaining() < 0; isOverage = textMessageUsage.getOverage() > 0; // If there is usage remaining: if (isRemaining) { // Format and set primary values. messagingUsagePrimarySubheader.setText(R.string.subheader_remaining); messagingUsagePrimaryValue.setText(String.format("%d min", textMessageUsage.getRemaining())); messagingUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_green_500)); // Set progress bar to proportional values. int progress = (int) (textMessageUsage.getUsed() * 100.0 / textMessageUsage.getIncluded() + 0.5); messagingUsageProgress.setMax(100); messagingUsageProgress.setProgress(progress); // Color primary value to be proportionate to amount of used data. if (progress >= 90) { messagingUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_orange_900)); } else if (progress >= 75) { messagingUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_amber_800)); } else { messagingUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_green_500)); } // Make usage protection switch visible. messagingProtectionSwitch.setVisibility(View.VISIBLE); // If usage is unlimited: } else if (isUnlimited) { // Format and set primary values. messagingUsagePrimarySubheader.setText(R.string.subheader_remaining); messagingUsagePrimaryValue.setText(R.string.unlimited); messagingUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_green_500)); // Set progress bar to green. messagingUsageProgress.setMax(100); messagingUsageProgress.setProgress(100); messagingUsageProgress.getProgressDrawable() .setColorFilter(getResources().getColor(R.color.md_green_500), PorterDuff.Mode.SRC_IN); // If there is an overage: } else if (isOverage) { // Format and set primary values. messagingUsagePrimarySubheader.setText(R.string.subheader_overage); messagingUsagePrimaryValue.setText(String.format("%d min", textMessageUsage.getOverage())); messagingUsagePrimaryValue.setTextColor(getResources().getColor(R.color.md_red_700)); // Set progress bar to red. messagingUsageProgress.setMax(100); messagingUsageProgress.setProgress(100); messagingUsageProgress.getProgressDrawable() .setColorFilter(getResources().getColor(R.color.md_red_700), PorterDuff.Mode.SRC_IN); // Make usage protection switch visible. messagingProtectionSwitch.setVisibility(View.VISIBLE); } break; case Constants.OVERAGE_FEES: final float dataOverageFee = OverageFeesHelper.calculateData(getApplicationContext()); final float peakTimeVoiceOverageFee = OverageFeesHelper .calculatePeakTimeVoice(getApplicationContext()); // Sum all overage fees into a total amount. final float totalOverageFee = dataOverageFee + peakTimeVoiceOverageFee; // Update bottom toolbar with the overage status. if (totalOverageFee > 0) { overageStatus .setText(String.format(getString(R.string.overage_fees_estimate), totalOverageFee)); overageStatus.setTextColor(getResources().getColor(R.color.md_red_700)); } else { overageStatus.setText(getString(R.string.overage_fees_none)); overageStatus.setTextColor(getResources().getColor(R.color.md_green_700)); } // Update the data overage fee. if (dataOverageFee >= 0) { dataOverageFeesValue.setText(String.format("$%1$.2f", dataOverageFee)); } else { dataOverageFeesValue.setText(getString(R.string.fees_panel_default_value)); } // Update the peak time voice overage fee. if (peakTimeVoiceOverageFee >= 0) { weekdayMinutesOverageFeesValue.setText(String.format("$%1$.2f", peakTimeVoiceOverageFee)); } else { weekdayMinutesOverageFeesValue.setText(getString(R.string.fees_panel_default_value)); } break; default: break; } } }; /** * Set panel icon to match whether the panel has expanded or collapsed. */ private PanelSlideListener panelSlideListener = new SlidingUpPanelLayout.PanelSlideListener() { @Override public void onPanelExpanded(View panel) { // Set panel icon state to expand more. panelStateIcon = (ImageView) findViewById(R.id.imageView_panelStateIcon); panelStateIcon.setImageResource(R.drawable.ic_expand_more_grey600_48dp); } @Override public void onPanelCollapsed(View panel) { // Set panel icon state to expand less. panelStateIcon = (ImageView) findViewById(R.id.imageView_panelStateIcon); panelStateIcon.setImageResource(R.drawable.ic_expand_less_grey600_48dp); } @Override public void onPanelAnchored(View view) { } @Override public void onPanelHidden(View view) { } @Override public void onPanelSlide(View view, float v) { } }; /** * Launch mobile data settings page when the disable mobile data switch has changed. */ private CompoundButton.OnCheckedChangeListener dataProtectionSwitchListener = new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) setMobileDataEnabled(getApplicationContext(), connectivityManager, false); else setMobileDataEnabled(getApplicationContext(), connectivityManager, true); } }; private CompoundButton.OnCheckedChangeListener voiceProtectionSwitchListener = new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) PreferenceHelper.setVoiceOverageProtectionEnabled(getApplicationContext(), true); else PreferenceHelper.setVoiceOverageProtectionEnabled(getApplicationContext(), false); } }; private CompoundButton.OnCheckedChangeListener messagingProtectionSwitchListener = new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) PreferenceHelper.setMessagingOverageProtectionEnabled(getApplicationContext(), true); else PreferenceHelper.setMessagingOverageProtectionEnabled(getApplicationContext(), false); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Set app bar. Toolbar appBar = (Toolbar) findViewById(R.id.app_bar); setSupportActionBar(appBar); // Instantiate managers. connectivityManager = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE); wifiManager = (WifiManager) this.getSystemService(Context.WIFI_SERVICE); telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); networkBrand = telephonyManager.getNetworkOperatorName(); // Get objects in the data usage card. dataUsageCard = (CardView) findViewById(R.id.cardView_dataUsage); dataUsagePrimarySubheader = (TextView) findViewById(R.id.textView_dataUsagePrimarySubheader); dataUsagePrimaryValue = (TextView) findViewById(R.id.textView_dataUsagePrimaryValue); dataUsageSecondaryValue = (TextView) findViewById(R.id.textView_dataUsageSecondaryValue); dataUsageProgress = (ProgressBar) findViewById(R.id.progressBar_dataUsage); dataProtectionSwitch = (Switch) findViewById(R.id.switch_dataProtection); // Get objects in the voice usage card. voiceUsageCard = (CardView) findViewById(R.id.cardView_voiceUsage); voiceUsagePrimarySubheader = (TextView) findViewById(R.id.textView_voiceUsagePrimarySubheader); voiceUsagePrimaryValue = (TextView) findViewById(R.id.textView_voiceUsagePrimaryValue); voiceUsageSecondaryValue = (TextView) findViewById(R.id.textView_voiceUsageSecondaryValue); voiceUsageProgress = (ProgressBar) findViewById(R.id.progressBar_voiceUsage); voiceProtectionSwitch = (Switch) findViewById(R.id.switch_voiceProtection); // Get objects in the messaging usage card. messagingUsageCard = (CardView) findViewById(R.id.cardView_messagingUsage); messagingUsagePrimarySubheader = (TextView) findViewById(R.id.textView_messagingUsagePrimarySubheader); messagingUsagePrimaryValue = (TextView) findViewById(R.id.textView_messagingUsagePrimaryValue); messagingUsageSecondaryValue = (TextView) findViewById(R.id.textView_messagingUsageSecondaryValue); messagingUsageProgress = (ProgressBar) findViewById(R.id.progressBar_messagingUsage); messagingProtectionSwitch = (Switch) findViewById(R.id.switch_messagingProtection); // Get objects in the sliding panel layout. feesPanel = (SlidingUpPanelLayout) findViewById(R.id.sliding_layout); bottomBar = (Toolbar) findViewById(R.id.bottom_bar); overageStatus = (TextView) findViewById(R.id.textView_overageStatus); planNameHeader = (TextView) findViewById(R.id.textView_planNameHeader); totalAmountDueValue = (TextView) findViewById(R.id.textView_totalAmountDueValue); paymentDueDateValue = (TextView) findViewById(R.id.textView_paymentDueDateValue); nextBillDateValue = (TextView) findViewById(R.id.textView_nextBillDateValue); dataOverageFeesValue = (TextView) findViewById(R.id.textView_dataOverageFeesValue); weekdayMinutesOverageFeesValue = (TextView) findViewById(R.id.textView_weekdayMinutesOverageFeesValue); // Instantiate Realm. realm = Realm.getInstance(this); // Register broadcast receiver for UI refreshes. LocalBroadcastManager.getInstance(this).registerReceiver(uiRefreshReceiver, new IntentFilter(Constants.ACTION_REFRESH_UI_SECTION)); LocalBroadcastManager.getInstance(this).registerReceiver(asyncTaskReceiver, new IntentFilter(Constants.ACTION_ASYNC_TASK)); // Check if the user is connected to the right network. if (!isConnectedNetworkOperatorValid(telephonyManager) && !isAirplaneModeOn(this)) { InvalidNetworkDialog invalidNetworkDialog = InvalidNetworkDialog .create(telephonyManager.getNetworkOperatorName()); invalidNetworkDialog.show(getSupportFragmentManager(), InvalidNetworkDialog.TAG); } else { // Refresh UI with the most recent cached information. startActionRefreshUI(this); } } @Override public void onStart() { super.onStart(); // Set Realm change listener. realm.addChangeListener(new RealmChangeListener() { @Override public void onChange() { // TODO: Something useful should happen here. Log.i(TAG, "RealmChangeListener: Changed."); } }); // Set TelephonyManager listener. telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DATA_CONNECTION_STATE); // Set mobile data protection switch and listener. if (isMobileDataEnabled(connectivityManager)) dataProtectionSwitch.setChecked(false); else dataProtectionSwitch.setChecked(true); dataProtectionSwitch.setOnCheckedChangeListener(dataProtectionSwitchListener); // Set voice protection switch and listener. if (PreferenceHelper.isVoiceOverageProtectionEnabled(this)) voiceProtectionSwitch.setChecked(true); else voiceProtectionSwitch.setChecked(false); voiceProtectionSwitch.setOnCheckedChangeListener(voiceProtectionSwitchListener); // Set messaging protection switch and listener. if (PreferenceHelper.isMessagingOverageProtectionEnabled(this)) messagingProtectionSwitch.setChecked(true); else messagingProtectionSwitch.setChecked(false); messagingProtectionSwitch.setOnCheckedChangeListener(messagingProtectionSwitchListener); // Set PanelSlideListener to change panel state icon based on expanded/collapsed state. feesPanel.setPanelSlideListener(panelSlideListener); // Set OnClickListener on bottomBar to expand/collapse the sliding panel. bottomBar.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (feesPanel.getPanelState() == PanelState.COLLAPSED) { feesPanel.expandPanel(); } else if (feesPanel.getPanelState() == PanelState.EXPANDED) { feesPanel.collapsePanel(); } } }); } @Override public void onResume() { super.onResume(); } @Override public void onStop() { super.onStop(); // Remove LocalBroadcastManager listeners. LocalBroadcastManager.getInstance(this).unregisterReceiver(uiRefreshReceiver); LocalBroadcastManager.getInstance(this).unregisterReceiver(asyncTaskReceiver); // Remove TelephonyManager listener. telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE); // Remove OnCheckedChangeListener on dataProtectionSwitch. dataProtectionSwitch.setOnCheckedChangeListener(null); // Remove PanelSlideListener on feesPanel. feesPanel.setPanelSlideListener(null); // Remove OnClickListener on bottomBar. bottomBar.setOnClickListener(null); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { MenuItem refreshItem = menu.findItem(R.id.action_refresh); Log.d(TAG, "isMobileDataEnabled = " + isMobileDataEnabled(connectivityManager)); Log.d(TAG, "isMobileDataConnected = " + isMobileDataConnected(telephonyManager)); Log.d(TAG, "isWifiConnected = " + isWifiConnected(wifiManager)); // Stop refresh icon animation if it's already ongoing. if (refreshItemActionView != null) refreshItemActionView.clearAnimation(); refreshItem.setActionView(null); boolean waitingForData = (!taskQueue.isEmpty()) && (!isMobileDataConnected(telephonyManager)); // (Re-)Start preparing refresh icon animation while waiting for mobile data connection. if (waitingForData) { LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); @SuppressLint("InflateParams") ImageView animatingRefreshIcon = (ImageView) layoutInflater.inflate(R.layout.icon_refresh, null); Animation clockwiseRotation = AnimationUtils.loadAnimation(this, R.anim.counterclockwise_refresh); animatingRefreshIcon.startAnimation(clockwiseRotation); refreshItem.setActionView(animatingRefreshIcon); // getActionView() always returns null in the else block, so it's assigned to a member variable. refreshItemActionView = refreshItem.getActionView(); } // (Re-)Start refresh icon animation only if there are ongoing tasks. if (ongoingTasks > 0) { LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); @SuppressLint("InflateParams") ImageView animatingRefreshIcon = (ImageView) layoutInflater.inflate(R.layout.icon_refresh, null); Animation clockwiseRotation = AnimationUtils.loadAnimation(this, R.anim.clockwise_refresh); animatingRefreshIcon.startAnimation(clockwiseRotation); refreshItem.setActionView(animatingRefreshIcon); // getActionView() always returns null in the else block, so it's assigned to a member variable. refreshItemActionView = refreshItem.getActionView(); } return super.onPrepareOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); Log.d(TAG, "isMobileDataEnabled = " + isMobileDataEnabled(connectivityManager)); Log.d(TAG, "isMobileDataConnected = " + isMobileDataConnected(telephonyManager)); Log.d(TAG, "isWifiConnected = " + isWifiConnected(wifiManager)); switch (id) { case R.id.action_refresh: Log.i(TAG, "taskQueue = " + String.valueOf(taskQueue.size())); Log.i(TAG, "ongoingTasks = " + String.valueOf(ongoingTasks)); // Remind user to disable airplane mode if it's on. if (isAirplaneModeOn(this)) { Toast.makeText(this, R.string.disable_airplane_mode, Toast.LENGTH_LONG).show(); break; } // Remind user to enable mobile data if it's disabled. if (!isMobileDataEnabled(connectivityManager)) { Toast.makeText(this, R.string.enable_mobile_data, Toast.LENGTH_LONG).show(); break; } if (!taskQueue.isEmpty()) { // Finish any already queued tasks. runTasks(); } else if (ongoingTasks == 0) { // Add tasks to queue since the queue is empty and there are no ongoing tasks. taskQueue.add(new DataUsageTask()); taskQueue.add(new VoiceUsageTask()); taskQueue.add(new MessagingUsageTask()); runTasks(); } else { // Wait for tasks to complete. Toast.makeText(this, R.string.wait_for_tasks, Toast.LENGTH_LONG).show(); } break; case R.id.action_settings: Intent settingsIntent = new Intent(this, SettingsActivity.class); startActivity(settingsIntent); break; case R.id.action_about: final Long recommendedBytes = DownloadManager.getRecommendedMaxBytesOverMobile(this); final Long maximumBytes = DownloadManager.getMaxBytesOverMobile(this); Log.i(TAG, "recommendedBytes = " + recommendedBytes); Log.i(TAG, "maximumBytes = " + maximumBytes); break; case R.id.action_exit: this.finish(); break; } return super.onOptionsItemSelected(item); } @Override public void onBackPressed() { if (feesPanel.getPanelState() == PanelState.EXPANDED) { feesPanel.collapsePanel(); } else { super.onBackPressed(); } } /** * Run queued tasks provided that mobile data is enabled and connected. If it is not enabled, * prompt the user to enable it so that the device can connect to data. If it is enabled but * connected to WiFi instead, disable WiFi first. Once a data connection is established, the * PhoneStateListener will call this method. */ private void runTasks() { if (isMobileDataEnabled(connectivityManager)) { Log.i(TAG, "runTasks: Data is enabled."); if (isMobileDataConnected(telephonyManager)) { Log.i(TAG, "runTasks: Data is connected."); // Empty and execute tasks in the task queue. while (!taskQueue.isEmpty()) taskQueue.remove().execute(); } else { Log.i(TAG, "runTasks: Data is disconnected."); if (taskQueue.isEmpty()) return; if (isWifiConnected(wifiManager)) { Log.i(TAG, "runTasks: Disabling WiFi..."); // Start prepare refresh animation in the app bar via onPrepareOptionsMenu(). supportInvalidateOptionsMenu(); // Disable WiFi, and call runTasks() again after data is connected. telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE); telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DATA_CONNECTION_STATE); Toast.makeText(this, R.string.disabling_wifi, Toast.LENGTH_SHORT).show(); wifiManager.setWifiEnabled(false); wifiWasEnabled = true; } } } else { Log.i(TAG, "runTasks: Data is disabled."); Toast.makeText(this, R.string.enable_mobile_data, Toast.LENGTH_LONG).show(); } } private void onAllTasksComplete() { // Stop refresh animation in the app bar via onPrepareOptionsMenu(). supportInvalidateOptionsMenu(); if (wifiWasEnabled) { Toast.makeText(this, R.string.enabling_wifi, Toast.LENGTH_SHORT).show(); wifiManager.setWifiEnabled(true); wifiWasEnabled = false; } } private class BalanceTask extends AsyncTask<Void, Void, Void> { @Override protected void onPreExecute() { super.onPreExecute(); // Increment ongoingTasks. ongoingTasks += 1; // Start refresh animation in the app bar via onPrepareOptionsMenu(). supportInvalidateOptionsMenu(); // Set UI values to loading. totalAmountDueValue.setText(R.string.loading_value); paymentDueDateValue.setText(R.string.loading_value); nextBillDateValue.setText(R.string.loading_value); } @Override protected Void doInBackground(Void... params) { try { BalanceParser.parseBalance(getApplicationContext(), networkBrand); } catch (UnknownHostException e) { Log.e(TAG, "BalanceTask: UnknownHostException"); Log.e(TAG, e.getMessage()); taskQueue.add(new BalanceTask()); runTasks(); } catch (IOException e) { e.printStackTrace(); Log.e(TAG, e.getMessage()); } catch (NullPointerException e) { Log.e(TAG, e.getMessage()); e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void result) { super.onPostExecute(result); // Decrement ongoingTasks and call onAllTasksComplete(). ongoingTasks -= 1; if ((taskQueue.isEmpty()) && (ongoingTasks == 0)) { onAllTasksComplete(); } // Refresh UI. refreshUiSection(getApplicationContext(), Constants.BALANCE); } } private class DataUsageTask extends AsyncTask<Void, Void, Void> { @Override protected void onPreExecute() { super.onPreExecute(); // Increment ongoingTasks. ongoingTasks += 1; // Start refresh animation in the app bar via onPrepareOptionsMenu(). supportInvalidateOptionsMenu(); // Set progress bar to indeterminate state to have a loading animation. dataUsageProgress.setIndeterminate(true); } @Override protected Void doInBackground(Void... params) { try { DataParser.parseDataUsage(getApplicationContext(), networkBrand); } catch (UnknownHostException e) { Log.e(TAG, "DataUsageTask: UnknownHostException"); Log.e(TAG, e.getMessage()); taskQueue.add(new DataUsageTask()); runTasks(); } catch (IOException e) { e.printStackTrace(); Log.e(TAG, e.getMessage()); } catch (NullPointerException e) { Log.e(TAG, e.getMessage()); e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void result) { super.onPostExecute(result); // Decrement ongoingTasks and call onAllTasksComplete(). ongoingTasks -= 1; if ((taskQueue.isEmpty()) && (ongoingTasks == 0)) { onAllTasksComplete(); } // Set progress bar to determinate state to disable the loading animation. dataUsageProgress.setIndeterminate(false); // Refresh UI. refreshUiSection(getApplicationContext(), Constants.DATA); refreshUiSection(getApplicationContext(), Constants.OVERAGE_FEES); } } private class VoiceUsageTask extends AsyncTask<Void, Void, Void> { @Override protected void onPreExecute() { super.onPreExecute(); // Increment ongoingTasks. ongoingTasks += 1; // Start refresh animation in the app bar via onPrepareOptionsMenu(). supportInvalidateOptionsMenu(); // Set progress bar to indeterminate state to have a loading animation. voiceUsageProgress.setIndeterminate(true); } @Override protected Void doInBackground(Void... params) { try { VoiceParser.parsePeakTimeUsage(getApplicationContext(), networkBrand); } catch (UnknownHostException e) { Log.e(TAG, "VoiceUsageTask: UnknownHostException"); Log.e(TAG, e.getMessage()); taskQueue.add(new VoiceUsageTask()); runTasks(); } catch (IOException e) { e.printStackTrace(); Log.e(TAG, e.getMessage()); } catch (NullPointerException e) { Log.e(TAG, e.getMessage()); e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void result) { super.onPostExecute(result); // Decrement ongoingTasks and call onAllTasksComplete(). ongoingTasks -= 1; if ((taskQueue.isEmpty()) && (ongoingTasks == 0)) { onAllTasksComplete(); } // Set progress bar to determinate state to disable the loading animation. voiceUsageProgress.setIndeterminate(false); // Refresh UI. refreshUiSection(getApplicationContext(), Constants.VOICE_PEAKTIME); refreshUiSection(getApplicationContext(), Constants.OVERAGE_FEES); } } private class MessagingUsageTask extends AsyncTask<Void, Void, Void> { @Override protected void onPreExecute() { super.onPreExecute(); // Increment ongoingTasks. ongoingTasks += 1; // Start refresh animation in the app bar via onPrepareOptionsMenu(). supportInvalidateOptionsMenu(); // Set progress bar to indeterminate state to have a loading animation. messagingUsageProgress.setIndeterminate(true); } @Override protected Void doInBackground(Void... params) { try { MessageParser.parseTextUsage(getApplicationContext(), networkBrand); } catch (UnknownHostException e) { Log.e(TAG, "MessagingUsageTask: UnknownHostException"); Log.e(TAG, e.getMessage()); taskQueue.add(new MessagingUsageTask()); runTasks(); } catch (IOException e) { e.printStackTrace(); Log.e(TAG, e.getMessage()); } catch (NullPointerException e) { Log.e(TAG, e.getMessage()); e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void result) { super.onPostExecute(result); // Decrement ongoingTasks and call onAllTasksComplete(). ongoingTasks -= 1; if ((taskQueue.isEmpty()) && (ongoingTasks == 0)) { onAllTasksComplete(); } // Set progress bar to determinate state to disable the loading animation. messagingUsageProgress.setIndeterminate(false); // Refresh UI. refreshUiSection(getApplicationContext(), Constants.MESSAGING_TEXT); refreshUiSection(getApplicationContext(), Constants.OVERAGE_FEES); } } }