fr.tvbarthel.apps.simpleweatherforcast.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for fr.tvbarthel.apps.simpleweatherforcast.MainActivity.java

Source

package fr.tvbarthel.apps.simpleweatherforcast;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.PageTransformer;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.TypefaceSpan;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.nineoldandroids.view.ViewHelper;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Locale;

import fr.tvbarthel.apps.billing.utils.SupportUtils;
import fr.tvbarthel.apps.simpleweatherforcast.fragments.AboutDialogFragment;
import fr.tvbarthel.apps.simpleweatherforcast.fragments.ForecastFragment;
import fr.tvbarthel.apps.simpleweatherforcast.fragments.LicenseDialogFragment;
import fr.tvbarthel.apps.simpleweatherforcast.fragments.MoreAppsDialogFragment;
import fr.tvbarthel.apps.simpleweatherforcast.fragments.TemperatureUnitPickerDialogFragment;
import fr.tvbarthel.apps.simpleweatherforcast.openweathermap.DailyForecastJsonParser;
import fr.tvbarthel.apps.simpleweatherforcast.openweathermap.DailyForecastModel;
import fr.tvbarthel.apps.simpleweatherforcast.receivers.WeatherWidgetReceiver;
import fr.tvbarthel.apps.simpleweatherforcast.services.DailyForecastUpdateService;
import fr.tvbarthel.apps.simpleweatherforcast.ui.AlphaForegroundColorSpan;
import fr.tvbarthel.apps.simpleweatherforcast.utils.SharedPreferenceUtils;

public class MainActivity extends ActionBarActivity implements SharedPreferences.OnSharedPreferenceChangeListener {

    public static final String EXTRA_PAGE_POSITION = "fr.tvbarthel.apps.simpleweatherforcast.MainActivity.Extra.PagePosition";

    private ViewGroup mRootView;
    private ForecastPagerAdapter mSectionsPagerAdapter;
    private ViewPager mViewPager;
    private Toast mTextToast;
    private String mTemperatureUnit;
    private SpannableString mActionBarSpannableTitle;
    private AlphaForegroundColorSpan mAlphaForegroundColorSpan;
    private TypefaceSpan mTypefaceSpanLight;
    private int[] mBackgroundColors;
    private Menu mMenu;
    private SimpleDateFormat mActionBarTitleDateFormat;
    private ProgressBar mProgressBar;
    private String mLoadedWeather;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar) findViewById(R.id.activity_main_toolbar);
        setSupportActionBar(toolbar);

        // Get the colors used for the background
        mBackgroundColors = getResources().getIntArray(R.array.background_colors);
        //Get the temperature unit symbol
        mTemperatureUnit = SharedPreferenceUtils.getTemperatureUnitSymbol(this);

        mAlphaForegroundColorSpan = new AlphaForegroundColorSpan(Color.WHITE);
        mTypefaceSpanLight = new TypefaceSpan("sans-serif-light");
        mActionBarTitleDateFormat = new SimpleDateFormat("EEEE dd MMMM", Locale.getDefault());

        mProgressBar = (ProgressBar) findViewById(R.id.activity_main_progress_bar);
        mRootView = (ViewGroup) findViewById(R.id.activity_main_root);

        initActionBar();
        initViewPager();
        initRootPadding();
    }

    private void initRootPadding() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            final Resources resources = getResources();
            final boolean isPortrait = resources.getBoolean(R.bool.is_portrait);
            final ActionBar actionBar = getSupportActionBar();

            final ViewTreeObserver vto = mRootView.getViewTreeObserver();
            if (vto.isAlive()) {
                vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        mRootView.getViewTreeObserver().removeOnPreDrawListener(this);

                        int paddingBottom = mRootView.getPaddingBottom();
                        int paddingTop = mRootView.getPaddingTop();
                        int paddingRight = mRootView.getPaddingRight();
                        int paddingLeft = mRootView.getPaddingLeft();

                        // Add the status bar height to the top padding.
                        int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
                        if (resourceId > 0) {
                            paddingTop += resources.getDimensionPixelSize(resourceId);
                        }

                        if (isPortrait) {
                            // Add the navigation bar height to the bottom padding.
                            resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
                            if (resourceId > 0) {
                                paddingBottom += resources.getDimensionPixelSize(resourceId);
                            }
                        } else {
                            // Add the navigation bar width to the right padding.
                            resourceId = resources.getIdentifier("navigation_bar_width", "dimen", "android");
                            if (resourceId > 0) {
                                paddingRight += resources.getDimensionPixelSize(resourceId);
                            }
                        }

                        mRootView.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
                        return true;
                    }
                });
            }
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        final String lastKnownWeather = SharedPreferenceUtils.getLastKnownWeather(getApplicationContext());
        final Intent intent = getIntent();
        SharedPreferenceUtils.registerOnSharedPreferenceChangeListener(this, this);

        if (lastKnownWeather == null) {
            // There is no forecast, it should be the first launch
            mProgressBar.setVisibility(View.VISIBLE);
        } else if (mLoadedWeather != lastKnownWeather) {
            // Load the last known weather
            loadDailyForecast(lastKnownWeather);
        } else if (intent != null && intent.hasExtra(EXTRA_PAGE_POSITION)) {
            final int position = intent.getIntExtra(EXTRA_PAGE_POSITION, 0);
            mViewPager.setCurrentItem(position, true);
            intent.removeExtra(EXTRA_PAGE_POSITION);
        }

        //Check if the last known weather is out dated.
        if (SharedPreferenceUtils.isWeatherOutdated(this, false) || lastKnownWeather == null) {
            DailyForecastUpdateService.startForUpdate(this);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        SharedPreferenceUtils.unregisterOnSharedPreferenceChangeListener(this, this);
        hideToast();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        mMenu = menu;
        // If the user is supporting us, add a thanks action.
        SupportUtils.checkSupport(getApplicationContext(), new SupportUtils.OnCheckSupportListener() {
            @Override
            public void onCheckSupport(boolean supporting) {
                if (supporting) {
                    getMenuInflater().inflate(R.menu.thanks, mMenu);
                }
            }
        });
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        boolean isActionConsumed;
        switch (id) {
        case R.id.menu_item_about:
            isActionConsumed = handleActionAbout();
            break;

        case R.id.menu_item_manual_refresh:
            isActionConsumed = handleActionManualRefresh();
            break;

        case R.id.menu_item_license:
            isActionConsumed = handleActionLicense();
            break;

        case R.id.menu_item_more_apps:
            isActionConsumed = handleActionMoreApps();
            break;

        case R.id.menu_item_unit_picker:
            isActionConsumed = handleActionUnitPicker();
            break;

        case R.id.menu_item_support:
            isActionConsumed = handleActionSupport();
            break;

        case R.id.menu_item_contact_us:
            isActionConsumed = handleActionContactUs();
            break;

        case R.id.menu_item_thanks:
            isActionConsumed = handleThanksButton();
            break;

        default:
            isActionConsumed = super.onOptionsItemSelected(item);
        }
        return isActionConsumed;
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        if (key.equals(SharedPreferenceUtils.KEY_TEMPERATURE_UNIT_SYMBOL)) {
            mTemperatureUnit = SharedPreferenceUtils.getTemperatureUnitSymbol(this);
            mSectionsPagerAdapter.notifyDataSetChanged();
            invalidatePageTransformer();

            // Broadcast change to the widgets
            Intent intent = new Intent(this, WeatherWidgetReceiver.class);
            intent.setAction(WeatherWidgetReceiver.APPWIDGET_DATA_CHANGED);
            sendBroadcast(intent);
        } else if (SharedPreferenceUtils.KEY_LAST_UPDATE.equals(key)) {
            final String lastKnownWeather = SharedPreferenceUtils.getLastKnownWeather(getApplicationContext());
            loadDailyForecast(lastKnownWeather);
        }
    }

    private boolean handleThanksButton() {
        makeTextToast(R.string.support_has_supported_us);
        return true;
    }

    private boolean handleActionContactUs() {
        final String uriString = getString(R.string.contact_us_uri,
                Uri.encode(getString(R.string.contact_us_email)),
                Uri.encode(getString(R.string.contact_us_default_subject)));
        final Uri mailToUri = Uri.parse(uriString);
        Intent sendToIntent = new Intent(Intent.ACTION_SENDTO);
        sendToIntent.setData(mailToUri);
        startActivity(sendToIntent);
        return true;
    }

    private boolean handleActionSupport() {
        final int colorSize = mBackgroundColors.length;
        final int currentPosition = mViewPager.getCurrentItem();
        final int currentColor = mBackgroundColors[currentPosition % colorSize];
        final Intent intent = new Intent(this, SupportActivity.class);
        intent.putExtra(SupportActivity.EXTRA_BG_COLOR, currentColor);
        startActivity(intent);
        return true;
    }

    private boolean handleActionUnitPicker() {
        final String[] temperatureUnitNames = getResources().getStringArray(R.array.temperature_unit_names);
        final String[] temperatureUnitSymbols = getResources().getStringArray(R.array.temperature_unit_symbols);
        (TemperatureUnitPickerDialogFragment.newInstance(temperatureUnitNames, temperatureUnitSymbols))
                .show(getSupportFragmentManager(), "dialog_unit_picker");
        return true;
    }

    private boolean handleActionAbout() {
        (new AboutDialogFragment()).show(getSupportFragmentManager(), "dialog_about");
        return true;
    }

    private boolean handleActionMoreApps() {
        (new MoreAppsDialogFragment()).show(getSupportFragmentManager(), "dialog_more_apps");
        return true;
    }

    private boolean handleActionLicense() {
        (new LicenseDialogFragment()).show(getSupportFragmentManager(), "dialog_license");
        return true;
    }

    private boolean handleActionManualRefresh() {
        if (SharedPreferenceUtils.isWeatherOutdated(this, true)) {
            DailyForecastUpdateService.startForUpdate(this);
            mSectionsPagerAdapter.clear();
            mLoadedWeather = null;
            mProgressBar.setVisibility(View.VISIBLE);
        } else {
            makeTextToast(R.string.toast_already_up_to_date);
        }
        return true;
    }

    private void initActionBar() {
        // Hide the app icon in the actionBar
        getSupportActionBar().setDisplayShowHomeEnabled(false);
    }

    private void initViewPager() {
        mSectionsPagerAdapter = new ForecastPagerAdapter(getSupportFragmentManager());
        mViewPager = (ViewPager) findViewById(R.id.activity_main_view_pager);
        mViewPager.setAdapter(mSectionsPagerAdapter);
        mViewPager.setPageTransformer(true, new ForecastPageTransformer());
        mViewPager.setOffscreenPageLimit(2);
        mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int currentPosition, float positionOffset, int i2) {
                setGradientBackgroundColor(currentPosition, positionOffset);
                updateActionBarTitle(currentPosition, positionOffset);
            }

            @Override
            public void onPageSelected(int i) {
            }

            @Override
            public void onPageScrollStateChanged(int i) {
            }
        });
    }

    private void makeTextToast(int stringResourceId) {
        hideToast();
        mTextToast = Toast.makeText(this, stringResourceId, Toast.LENGTH_LONG);
        mTextToast.show();
    }

    private void hideToast() {
        if (mTextToast != null) {
            mTextToast.cancel();
            mTextToast = null;
        }
    }

    private void loadDailyForecast(final String jsonDailyForecast) {
        mProgressBar.setVisibility(View.VISIBLE);
        if (jsonDailyForecast != null) {
            new DailyForecastJsonParser() {
                @Override
                protected void onPostExecute(ArrayList<DailyForecastModel> dailyForecastModels) {
                    super.onPostExecute(dailyForecastModels);
                    mLoadedWeather = jsonDailyForecast;
                    mProgressBar.setVisibility(View.INVISIBLE);
                    mSectionsPagerAdapter.updateModels(dailyForecastModels);

                    final Intent intent = getIntent();
                    if (intent != null && intent.hasExtra(EXTRA_PAGE_POSITION)) {
                        final int position = intent.getIntExtra(EXTRA_PAGE_POSITION, 0);
                        mViewPager.setCurrentItem(position);
                        intent.removeExtra(EXTRA_PAGE_POSITION);
                    } else {
                        invalidatePageTransformer();
                    }
                }
            }.execute(jsonDailyForecast);
        }
    }

    /**
     * Trick to notify the pageTransformer of a data set change.
     */
    private void invalidatePageTransformer() {
        if (mViewPager.getAdapter().getCount() > 0) {
            new Handler().post(new Runnable() {
                @Override
                public void run() {
                    if (mViewPager.beginFakeDrag()) {
                        mViewPager.fakeDragBy(0f);
                        mViewPager.endFakeDrag();
                    }
                }
            });
        }
    }

    @SuppressLint("NewApi")
    private void setGradientBackgroundColor(int currentPosition, float positionOffset) {
        final GradientDrawable g = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT,
                new int[] { getColor(currentPosition, positionOffset),
                        getColor(currentPosition, (float) Math.pow(positionOffset, 0.40)) });
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
            mRootView.setBackgroundDrawable(g);
        } else {
            mRootView.setBackground(g);
        }
    }

    private void setActionBarAlpha(float alpha) {
        mAlphaForegroundColorSpan.setAlpha(alpha);
        mActionBarSpannableTitle.setSpan(mAlphaForegroundColorSpan, 0, mActionBarSpannableTitle.length(),
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        getSupportActionBar().setTitle(mActionBarSpannableTitle);
    }

    private void updateActionBarTitle(int currentPosition, float positionOffset) {
        float alpha = 1 - positionOffset * 2;
        if (positionOffset >= 0.5) {
            currentPosition++;
            alpha = (positionOffset - 0.5f) * 2;
        }
        setActionBarTitle(currentPosition);
        setActionBarAlpha(alpha);
    }

    private void setActionBarTitle(int position) {
        String newTitle = getActionBarTitle(position);
        if (mActionBarSpannableTitle == null || !mActionBarSpannableTitle.toString().equals(newTitle)) {
            mActionBarSpannableTitle = new SpannableString(newTitle);
            mActionBarSpannableTitle.setSpan(mTypefaceSpanLight, 0, mActionBarSpannableTitle.length(),
                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
    }

    private String getActionBarTitle(int currentPosition) {
        final DailyForecastModel currentModel = mSectionsPagerAdapter.getModel(currentPosition);
        return mActionBarTitleDateFormat.format(currentModel.getDateTime() * 1000);
    }

    private int getColor(int currentPosition, float positionOffset) {
        //retrieve current color and next color relative to the current position.
        final int colorSize = mBackgroundColors.length;
        final int currentColor = mBackgroundColors[(currentPosition) % colorSize];
        final int nextColor = mBackgroundColors[(currentPosition + 1) % colorSize];

        //Compute the deltas relative to the current position offset.
        final int deltaR = (int) ((Color.red(nextColor) - Color.red(currentColor)) * positionOffset);
        final int deltaG = (int) ((Color.green(nextColor) - Color.green(currentColor)) * positionOffset);
        final int deltaB = (int) ((Color.blue(nextColor) - Color.blue(currentColor)) * positionOffset);

        return Color.argb(255, Color.red(currentColor) + deltaR, Color.green(currentColor) + deltaG,
                Color.blue(currentColor) + deltaB);
    }

    /**
     * A {@link FragmentStatePagerAdapter} used for {@link DailyForecastModel}
     */
    public class ForecastPagerAdapter extends FragmentStatePagerAdapter {

        private final ArrayList<DailyForecastModel> mDailyForecastModels;

        public ForecastPagerAdapter(FragmentManager fm) {
            super(fm);
            mDailyForecastModels = new ArrayList<DailyForecastModel>();
        }

        public void updateModels(ArrayList<DailyForecastModel> newModels) {
            mDailyForecastModels.clear();
            mDailyForecastModels.addAll(newModels);
            notifyDataSetChanged();
        }

        public void clear() {
            mDailyForecastModels.clear();
            notifyDataSetChanged();
        }

        public DailyForecastModel getModel(int position) {
            if (position >= mDailyForecastModels.size()) {
                return new DailyForecastModel();
            }
            return mDailyForecastModels.get(position);
        }

        @Override
        public Fragment getItem(int position) {
            return ForecastFragment.newInstance(mDailyForecastModels.get(position), mTemperatureUnit);
        }

        @Override
        public int getItemPosition(Object object) {
            return POSITION_NONE;
        }

        @Override
        public int getCount() {
            return mDailyForecastModels.size();
        }
    }

    /**
     * A {@link PageTransformer} used to scale the fragments.
     */
    public class ForecastPageTransformer implements PageTransformer {

        public void transformPage(View view, float position) {
            final int pageWidth = view.getWidth();

            if (position < -2.0) { // [-Infinity,-2.0)
                // This page is way off-screen to the left.
                ViewHelper.setAlpha(view, 0);

            } else if (position <= 2.0) { // [-2.0,2.0]
                final float normalizedPosition = Math.abs(position / 2);
                final float scaleFactor = 1f - normalizedPosition;
                final float horizontalMargin = pageWidth * normalizedPosition;

                //Translate back the page.
                if (position < 0) {
                    //left
                    ViewHelper.setTranslationX(view, horizontalMargin);
                } else {
                    //right
                    ViewHelper.setTranslationX(view, -horizontalMargin);
                }

                // Scale the page down relative to its size.
                ViewHelper.setScaleX(view, (float) Math.pow(scaleFactor, 0.80));
                ViewHelper.setScaleY(view, (float) Math.pow(scaleFactor, 0.80));

                // Fade the page relative to its size.
                ViewHelper.setAlpha(view, 1f * scaleFactor - (scaleFactor * 0.25f));

            } else { // (2.0,+Infinity]
                // This page is way off-screen to the right.
                ViewHelper.setAlpha(view, 0);
            }
        }
    }

}