de.ub0r.android.callmeter.ui.Plans.java Source code

Java tutorial

Introduction

Here is the source code for de.ub0r.android.callmeter.ui.Plans.java

Source

/*
 * Copyright (C) 2009-2013 Felix Bechstein
 * 
 * This file is part of Call Meter 3G.
 * 
 * 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 de.ub0r.android.callmeter.ui;

import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;

import com.viewpagerindicator.TitlePageIndicator;

import android.Manifest;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;

import de.ub0r.android.callmeter.CallMeter;
import de.ub0r.android.callmeter.R;
import de.ub0r.android.callmeter.data.DataProvider;
import de.ub0r.android.callmeter.data.LogRunnerReceiver;
import de.ub0r.android.callmeter.data.LogRunnerService;
import de.ub0r.android.callmeter.ui.prefs.Preferences;
import de.ub0r.android.lib.DonationHelper;
import de.ub0r.android.lib.Utils;
import de.ub0r.android.logg0r.Log;

/**
 * Callmeter's Main Activity
 *
 * @author flx
 */
public final class Plans extends AppCompatActivity implements OnPageChangeListener {

    /**
     * Tag for output.
     */
    private static final String TAG = "Plans";

    /**
     * {@link Message} for {@link Handler}: start background: LogMatcher.
     */
    public static final int MSG_BACKGROUND_START_MATCHER = 1;

    /**
     * {@link Message} for {@link Handler}: stop background: LogMatcher.
     */
    public static final int MSG_BACKGROUND_STOP_MATCHER = 2;

    /**
     * {@link Message} for {@link Handler}: start background: LogRunner.
     */
    public static final int MSG_BACKGROUND_START_RUNNER = 3;

    /**
     * {@link Message} for {@link Handler}: stop background: LogRunner.
     */
    public static final int MSG_BACKGROUND_STOP_RUNNER = 4;

    /**
     * {@link Message} for {@link Handler}: progress: LogMatcher.
     */
    public static final int MSG_BACKGROUND_PROGRESS_MATCHER = 5;

    private static final int PERMISSION_REQUEST_READ_CALL_LOG = 1;
    private static final int PERMISSION_REQUEST_READ_SMS = 2;
    private static final int PERMISSION_REQUEST_READ_CONTACTS = 3;

    /**
     * Delay for LogRunnerService to run.
     */
    private static final long DELAY_LOGRUNNER = 1500;

    /**
     * Display ads?
     */
    private static boolean prefsNoAds;

    /**
     * {@link ViewPager}.
     */
    private ViewPager pager;

    /**
     * {@link PlansFragmentAdapter}.
     */
    private PlansFragmentAdapter fadapter;

    private AdView mAdView;

    /**
     * {@link Handler} for handling messages from background process.
     */
    private final Handler handler = new Handler() {
        /** LogRunner running in background? */
        private boolean inProgressRunner = false;

        /** {@link ProgressDialog} showing LogMatcher's status. */
        private ProgressDialog statusMatcher = null;

        /** Is statusMatcher a {@link ProgressDialog}? */
        private boolean statusMatcherProgress = false;

        /** String for recalculate message. */
        private String recalc = null;

        @Override
        public synchronized void handleMessage(final Message msg) {
            Log.d(TAG, "handleMessage(", msg.what, ")");
            switch (msg.what) {
            case MSG_BACKGROUND_START_RUNNER:
                inProgressRunner = true;
            case MSG_BACKGROUND_START_MATCHER:
                statusMatcherProgress = false;
                Plans.this.setInProgress(1);
                break;
            case MSG_BACKGROUND_STOP_RUNNER:
                inProgressRunner = false;
                Plans.this.setInProgress(-1);
                Plans.this.getSupportActionBar().setSubtitle(null);
                break;
            case MSG_BACKGROUND_STOP_MATCHER:
                Plans.this.setInProgress(-1);
                Plans.this.getSupportActionBar().setSubtitle(null);
                Fragment f = Plans.this.fadapter.getActiveFragment(Plans.this.pager,
                        Plans.this.fadapter.getHomeFragmentPos());
                if (f != null && f instanceof PlansFragment) {
                    ((PlansFragment) f).requery(true);
                }
                break;
            case MSG_BACKGROUND_PROGRESS_MATCHER:
                if (Plans.this.progressCount == 0) {
                    Plans.this.setProgress(1);
                }
                if (statusMatcher == null || (!this.statusMatcherProgress || statusMatcher.isShowing())) {
                    Log.d(TAG, "matcher progress: ", msg.arg1);
                    if (statusMatcher == null || !this.statusMatcherProgress) {
                        final ProgressDialog dold = statusMatcher;
                        statusMatcher = new ProgressDialog(Plans.this);
                        statusMatcher.setCancelable(true);
                        if (recalc == null) {
                            recalc = Plans.this.getString(R.string.reset_data_progr2);
                        }
                        statusMatcher.setMessage(recalc);
                        statusMatcher.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                        statusMatcher.setMax(msg.arg2);
                        statusMatcher.setIndeterminate(false);
                        statusMatcherProgress = true;
                        Log.d(TAG, "showing dialog..");
                        try {
                            statusMatcher.show();
                            if (dold != null) {
                                dold.dismiss();
                            }
                        } catch (Exception e) {
                            Log.w(TAG, "activity already finished?", e);
                        }
                    }
                    statusMatcher.setProgress(msg.arg1);
                }
                if (recalc == null) {
                    recalc = Plans.this.getString(R.string.reset_data_progr2);
                }
                Plans.this.getSupportActionBar().setSubtitle(recalc + " " + msg.arg1 + "/" + msg.arg2);
                break;
            default:
                break;
            }

            if (inProgressRunner) {
                if (statusMatcher == null || (msg.arg1 <= 0 && !this.statusMatcher.isShowing())) {
                    statusMatcher = new ProgressDialog(Plans.this);
                    statusMatcher.setCancelable(true);
                    statusMatcher.setMessage(Plans.this.getString(R.string.reset_data_progr1));
                    statusMatcher.setIndeterminate(true);
                    statusMatcherProgress = false;
                    try {
                        statusMatcher.show();
                    } catch (Exception e) {
                        Log.w(TAG, "activity already finished?", e);
                    }
                }
            } else {
                if (statusMatcher != null && statusMatcher.isShowing()) {
                    try {
                        statusMatcher.dismiss();
                    } catch (IllegalArgumentException e) {
                        Log.e(TAG, "error dismissing dialog", e);
                    }
                    statusMatcher = null;
                }
            }
        }
    };

    /**
     * Number of background tasks.
     */
    private int progressCount = 0;

    /**
     * {@link Handler} for outside.
     */
    private static Handler currentHandler = null;

    /**
     * Show all {@link PlansFragment}s.
     *
     * @author flx
     */
    private static class PlansFragmentAdapter extends FragmentPagerAdapter {

        /**
         * {@link FragmentManager} .
         */
        private final FragmentManager mFragmentManager;

        /**
         * List of positions.
         */
        private final Long[] positions;

        /**
         * List of bill days.
         */
        private final Long[] billDays;

        /**
         * List of titles.
         */
        private final String[] titles;

        /**
         * {@link Context}.
         */
        private final Context ctx;

        /**
         * Default constructor.
         *
         * @param context {@link Context}
         * @param fm      {@link FragmentManager}
         */
        public PlansFragmentAdapter(final Context context, final FragmentManager fm) {
            super(fm);
            mFragmentManager = fm;
            ctx = context;
            ContentResolver cr = context.getContentResolver();
            Cursor c = cr.query(DataProvider.Logs.CONTENT_URI, new String[] { DataProvider.Logs.DATE }, null, null,
                    DataProvider.Logs.DATE + " ASC LIMIT 1");
            if (c == null || !c.moveToFirst()) {
                positions = new Long[] { -1L, -1L };
                billDays = positions;
                if (c != null && !c.isClosed()) {
                    c.close();
                }
            } else {
                final long minDate = c.getLong(0);
                c.close();
                c = cr.query(DataProvider.Plans.CONTENT_URI, DataProvider.Plans.PROJECTION,
                        DataProvider.Plans.TYPE + "=? and " + DataProvider.Plans.BILLPERIOD + "!=?",
                        new String[] { String.valueOf(DataProvider.TYPE_BILLPERIOD),
                                String.valueOf(DataProvider.BILLPERIOD_INFINITE) },
                        DataProvider.Plans.ORDER + " LIMIT 1");
                if (minDate < 0L || c == null || !c.moveToFirst()) {
                    positions = new Long[] { -1L, -1L };
                    billDays = positions;
                    if (c != null) {
                        c.close();
                    }
                } else {
                    ArrayList<Long> list = new ArrayList<>();
                    int bptype = c.getInt(DataProvider.Plans.INDEX_BILLPERIOD);
                    ArrayList<Long> bps = DataProvider.Plans.getBillDays(bptype,
                            c.getLong(DataProvider.Plans.INDEX_BILLDAY), minDate, -1);
                    if (bps != null) {
                        Log.d(TAG, "bill periods: ", bps.size());
                        if (!bps.isEmpty()) {
                            bps.remove(bps.size() - 1);
                            list.addAll(bps);
                        }
                    }
                    c.close();
                    list.add(-1L); // current time
                    list.add(-1L); // logs
                    positions = list.toArray(new Long[list.size()]);
                    int l = positions.length;
                    Arrays.sort(positions, 0, l - 2);

                    billDays = new Long[l];
                    for (int i = 0; i < l - 1; i++) {
                        long pos = positions[i];
                        billDays[i] = DataProvider.Plans.getBillDay(bptype, pos + 1L, pos, false).getTimeInMillis();
                    }
                }
            }
            Common.setDateFormat(context);
            final int l = positions.length;
            titles = new String[l];
            titles[l - 2] = context.getString(R.string.now);
            titles[l - 1] = context.getString(R.string.logs);
        }

        /**
         * Get an active fragment.
         *
         * @param container {@link ViewPager}
         * @param position  position in container
         * @return null if no fragment was initialized
         */
        public Fragment getActiveFragment(final ViewPager container, final int position) {
            String name = makeFragmentName(container.getId(), position);
            return mFragmentManager.findFragmentByTag(name);
        }

        /**
         * Get a {@link Fragment}'s name.
         *
         * @param viewId container view
         * @param index  position
         * @return name of {@link Fragment}
         */
        private static String makeFragmentName(final int viewId, final int index) {
            // this might change in underlying method!
            return "android:switcher:" + viewId + ":" + index;
        }

        @Override
        public int getCount() {
            return positions.length;
        }

        @Override
        public Fragment getItem(final int position) {
            if (position == getLogsFragmentPos()) {
                return new LogsFragment();
            } else {
                return PlansFragment.newInstance(position, positions[position]);
            }
        }

        /**
         * Get position of home {@link Fragment}.
         *
         * @return position of home {@link Fragment}
         */
        public int getHomeFragmentPos() {
            return positions.length - 2;
        }

        /**
         * Get position of Logs {@link Fragment}.
         *
         * @return position of Logs {@link Fragment}
         */
        public int getLogsFragmentPos() {
            return positions.length - 1;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public CharSequence getPageTitle(final int position) {
            String ret;
            if (titles[position] == null) {
                ret = Common.formatDate(ctx, billDays[position]);
                titles[position] = ret;
            } else {
                ret = titles[position];
            }
            return ret;
        }
    }

    @Override
    public void onDestroy() {
        mAdView.destroy();
        super.onDestroy();
    }

    @Override
    public void onCreate(final Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setTheme(Preferences.getTheme(this));
        Utils.setLocale(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.plans);
        getSupportActionBar().setHomeButtonEnabled(true);

        SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(this);
        //noinspection ConstantConditions
        if (p.getAll().isEmpty()) {
            // show intro
            startActivity(new Intent(this, IntroActivity.class));
            // set date of recordings to beginning of last month
            Calendar c = Calendar.getInstance();
            c.set(Calendar.DAY_OF_MONTH, 0);
            c.add(Calendar.MONTH, -1);
            Log.i(TAG, "set date of recording: " + c);
            p.edit().putLong(Preferences.PREFS_DATE_BEGIN, c.getTimeInMillis()).apply();
        }

        pager = (ViewPager) findViewById(R.id.pager);

        prefsNoAds = DonationHelper.hideAds(this);
        mAdView = (AdView) findViewById(R.id.ads);
        mAdView.setVisibility(View.GONE);
        if (!prefsNoAds) {
            mAdView.loadAd(new AdRequest.Builder().build());
            mAdView.setAdListener(new AdListener() {
                @Override
                public void onAdLoaded() {
                    mAdView.setVisibility(View.VISIBLE);
                    super.onAdLoaded();
                }
            });
        } else {
            findViewById(R.id.cookieconsent).setVisibility(View.GONE);
        }

        initAdapter();
    }

    private void initAdapter() {
        // request permissions before doing any real work
        if (!CallMeter.requestPermission(this, Manifest.permission.READ_CALL_LOG, PERMISSION_REQUEST_READ_CALL_LOG,
                R.string.permissions_read_call_log, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        finish();
                    }
                })) {
            return;
        }
        if (!CallMeter.requestPermission(this, Manifest.permission.READ_SMS, PERMISSION_REQUEST_READ_SMS,
                R.string.permissions_read_sms, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        finish();
                    }
                })) {
            return;
        }

        // request semi optional permission
        CallMeter.requestPermission(this, Manifest.permission.READ_CONTACTS, PERMISSION_REQUEST_READ_CONTACTS,
                R.string.permissions_read_contacts, null);

        fadapter = new PlansFragmentAdapter(this, getSupportFragmentManager());
        pager.setAdapter(fadapter);
        pager.setCurrentItem(fadapter.getHomeFragmentPos());

        TitlePageIndicator indicator = (TitlePageIndicator) findViewById(R.id.titles);
        indicator.setViewPager(pager);
        indicator.setOnPageChangeListener(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onResume() {
        super.onResume();
        Utils.setLocale(this);
        currentHandler = handler;
        setInProgress(0);
        PlansFragment.reloadPreferences(this);
        runLogRunner();
        mAdView.resume();
    }

    private void runLogRunner() {
        // schedule next update
        LogRunnerReceiver.schedNext(this, DELAY_LOGRUNNER, LogRunnerService.ACTION_RUN_MATCHER);
        LogRunnerReceiver.schedNext(this, LogRunnerService.ACTION_SHORT_RUN);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onNewIntent(final Intent intent) {
        setIntent(intent);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onPause() {
        currentHandler = null;
        mAdView.pause();
        super.onPause();
    }

    /**
     * Get the current {@link Handler}.
     *
     * @return {@link Handler}.
     */
    public static Handler getHandler() {
        return currentHandler;
    }

    @Override
    public void onRequestPermissionsResult(final int requestCode, @NonNull final String permissions[],
            @NonNull final int[] grantResults) {
        switch (requestCode) {
        case PERMISSION_REQUEST_READ_CALL_LOG:
        case PERMISSION_REQUEST_READ_SMS:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // just try again.
                initAdapter();
                runLogRunner();
            } else {
                // this app is useless without permission for reading sms
                Log.e(TAG, "permission denied: " + requestCode + " , exit");
                finish();
            }
            return;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean onCreateOptionsMenu(final Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        if (prefsNoAds) {
            menu.removeItem(R.id.item_donate);
        }
        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        switch (item.getItemId()) {
        case R.id.item_settings:
            startActivity(new Intent(this, Preferences.class));
            return true;
        case R.id.item_donate:
            try {
                startActivity(new Intent(Intent.ACTION_VIEW,
                        Uri.parse("https://play.google.com/store/apps/details?id=de.ub0r.android.donator")));
            } catch (ActivityNotFoundException e) {
                Log.e(TAG, "error opening play store with donation app", e);
                Toast.makeText(this, R.string.common_google_play_services_unknown_issue, Toast.LENGTH_LONG).show();
            }
            return true;
        case R.id.item_logs:
            showLogsFragment(-1L);
            return true;
        case android.R.id.home:
            pager.setCurrentItem(fadapter.getHomeFragmentPos(), true);
            Fragment f = fadapter.getActiveFragment(pager, fadapter.getLogsFragmentPos());
            if (f != null && f instanceof LogsFragment) {
                ((LogsFragment) f).setPlanId(-1L);
            }
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void showLogsFragment(final long planId) {
        int p = fadapter.getLogsFragmentPos();
        Fragment f = fadapter.getActiveFragment(pager, p);
        if (f != null && f instanceof LogsFragment) {
            ((LogsFragment) f).setPlanId(planId);
        }
        pager.setCurrentItem(p, true);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
        // nothing to do

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onPageSelected(final int position) {
        Log.d(TAG, "onPageSelected(", position, ")");
        if (position == fadapter.getLogsFragmentPos()) {
            Fragment f = fadapter.getActiveFragment(pager, fadapter.getLogsFragmentPos());
            if (f != null && f instanceof LogsFragment) {
                ((LogsFragment) f).setAdapter(false);
            }
        } else {
            Fragment f = fadapter.getActiveFragment(pager, position);
            if (f != null && f instanceof PlansFragment) {
                ((PlansFragment) f).requery(false);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onPageScrollStateChanged(final int state) {
        // nothing to do
    }

    /**
     * Set progress indicator.
     *
     * @param add add number of running tasks
     */
    public synchronized void setInProgress(final int add) {
        Log.d(TAG, "setInProgress(", add, ")");
        progressCount += add;

        if (progressCount < 0) {
            Log.w(TAG, "this.progressCount: " + progressCount);
            progressCount = 0;
        }

        Log.d(TAG, "progressCount: ", progressCount);
        if (progressCount == 0) {
            setSupportProgressBarIndeterminateVisibility(false);
        } else {
            setSupportProgressBarIndeterminateVisibility(true);
        }
    }
}