it.rainbowbreeze.keepmoving.ui.TimetableActivity.java Source code

Java tutorial

Introduction

Here is the source code for it.rainbowbreeze.keepmoving.ui.TimetableActivity.java

Source

package it.rainbowbreeze.keepmoving.ui;

import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.WearableExtender;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;

import java.lang.ref.WeakReference;

import javax.inject.Inject;

import it.rainbowbreeze.keepmoving.R;
import it.rainbowbreeze.keepmoving.common.ILogManager;
import it.rainbowbreeze.keepmoving.common.MyApp;
import it.rainbowbreeze.keepmoving.common.Utils;
import it.rainbowbreeze.keepmoving.domain.GeoPoint;
import it.rainbowbreeze.keepmoving.domain.Route;
import it.rainbowbreeze.keepmoving.logic.PositionManager;
import it.rainbowbreeze.keepmoving.logic.TimetableController;
import it.rainbowbreeze.keepmoving.logic.TimetableModel;

/**
 * This file is part of KeepMoving. KeepMoving 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, version 2.
 * <p/>
 * 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.
 * <p/>
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 * <p/>
 * Copyright Alfredo Morresi
 * <p/>
 * Created by Alfredo "Rainbowbreeze" Morresi on 02/07/14.
 */

public class TimetableActivity extends ActionBarActivity
        implements ActionBar.TabListener, TimetableFragment.OnFragmentInteractionListener {

    private static final String LOG_TAG = TimetableActivity.class.getSimpleName();
    private static final String EXTRA_EVENT_ID = "AlfredoTest";
    /**
     * The {@link android.support.v4.view.PagerAdapter} that will provide
     * fragments for each of the sections. We use a
     * {@link FragmentPagerAdapter} derivative, which will keep every
     * loaded fragment in memory. If this becomes too memory intensive, it
     * may be best to switch to a
     * {@link android.support.v4.app.FragmentStatePagerAdapter}.
     */
    RoutesPagerAdapter mRoutesPagerAdapter;
    private ProgressBar mProgressBar;
    /** The {@link ViewPager} that will host the section contents. */
    private ViewPager mViewPager;
    private ActionBar mActionBar;
    private TextView mLblNoRoutes;
    private boolean mRegisteredReceiver;
    private BroadcastReceiver mMyReceiver;

    @Inject
    protected ILogManager mLogManager;
    @Inject
    protected PositionManager mPositionManager;
    @Inject
    protected TimetableController mController;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ((MyApp) getApplicationContext()).inject(this);

        setContentView(R.layout.act_timetable);

        mProgressBar = (ProgressBar) findViewById(R.id.timetable_prgProgress);
        mLblNoRoutes = (TextView) findViewById(R.id.timetable_lblNoRoutes);

        mActionBar = getSupportActionBar();
        mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        // Create the adapter that will return a fragment for each of the three
        // primary sections of the activity.
        mRoutesPagerAdapter = new RoutesPagerAdapter(getSupportFragmentManager(), mController.getModel());
        // Set up the ViewPager with the sections adapter.
        mViewPager = (ViewPager) findViewById(R.id.timetable_pager);
        mViewPager.setAdapter(mRoutesPagerAdapter);
        // When swiping between different sections, select the corresponding
        // tab. We can also use ActionBar.Tab#select() to do this if we have
        // a reference to the Tab.
        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                mActionBar.setSelectedNavigationItem(position);
            }
        });

        mMyReceiver = new MyReceiver();
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (!mRegisteredReceiver) {
            mLogManager.d(LOG_TAG, "Register receiver");
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction(Utils.MESSAGE_REFRESH_TIME_VIEWS);
            intentFilter.addAction(Utils.MESSAGE_UPDATE_ALL_VIEWS);
            registerReceiver(mMyReceiver, intentFilter);
            mRegisteredReceiver = true;
        }
        mController.startTimetableUpdate(getApplicationContext());
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mRegisteredReceiver) {
            mLogManager.d(LOG_TAG, "Unregister receiver");
            unregisterReceiver(mMyReceiver);
            mRegisteredReceiver = false;
        }
        mController.stopTimetableUpdate();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.timetable, menu);
        return true;
    }

    @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();
        if (id == R.id.action_settings) {
            return true;
        } else if (id == R.id.action_reload) {
            mLogManager.d(LOG_TAG, "Forcing a message with action " + Utils.MESSAGE_UPDATE_ALL_VIEWS);
            Intent i = new Intent(Utils.MESSAGE_UPDATE_ALL_VIEWS);
            sendBroadcast(i);
            return true;
        } else if (id == R.id.action_about) {
            //TODO: implement about activity
            sendNotification();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void sendNotification() {
        sendToWear(mViewPager.getCurrentItem());
    }

    @Override
    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
        // When the given tab is selected, switch to the corresponding page in
        // the ViewPager.
        mViewPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    }

    @Override
    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    }

    @Override
    public Route getRouteForPosition(int pos) {
        TimetableModel model = mController.getModel();
        return null == model ? null : model.getRoutes().get(pos);
    }

    @Override
    public void sendToWear(int fragmentPos) {
        int notificationId = 001;

        // Build intent for notification content
        Intent viewIntent = new Intent(this, TimetableActivity.class);
        viewIntent.putExtra(EXTRA_EVENT_ID, 100);
        PendingIntent viewPendingIntent = PendingIntent.getActivity(this, 0, viewIntent, 0);

        Intent mapIntent = new Intent(Intent.ACTION_VIEW);
        Uri geoUri = Uri.parse("geo:0,0?q=" + Uri.encode("Piazza Duomo Milano"));
        mapIntent.setData(geoUri);
        PendingIntent mapPendingIntent = PendingIntent.getActivity(this, 0, mapIntent, 0);
        NotificationCompat.Action mapAction = new NotificationCompat.Action(android.R.drawable.ic_menu_delete,
                getString(R.string.common_map_title), mapPendingIntent);

        String notificationGroup = "aaa_groups";
        Bitmap bmpSeaPanorama = BitmapFactory.decodeResource(getResources(), R.drawable.seapanorama);
        Bitmap bmpMountainsPanorama = BitmapFactory.decodeResource(getResources(), R.drawable.mountainspanorama);

        // Specify the 'big view' content to display the long
        // event description that may not fit the normal content text.
        NotificationCompat.BigTextStyle bigStyle = new NotificationCompat.BigTextStyle()
                .setBigContentTitle("Titolo del bigcontent").bigText(
                        "Questo e' il testo davvero lungo della notifica, penso che potresti vederlo tutto solo se apri la notifica a tutto schermo");
        Notification secondPageNotification = new NotificationCompat.Builder(this).setStyle(bigStyle).build();

        NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle()
                .setBigContentTitle("Titolo inboxStyle").setSummaryText("Breve summary test")
                .addLine("Nuova riga di testo 1").addLine("Nuova riga di testo 2");
        Notification thirdPageNotification = new NotificationCompat.Builder(this).setStyle(inboxStyle).build();

        NotificationCompat.BigPictureStyle pictureStyle = new NotificationCompat.BigPictureStyle()
                .bigPicture(bmpMountainsPanorama) //on Wear, it doesn't work, use WearExtender.setBackground or Notification.setLargeIcon
                .setSummaryText("Summary text").setBigContentTitle("Content Title");
        Notification forthPageNotification = new NotificationCompat.Builder(this).setStyle(pictureStyle).build();

        //setLargeIcon: put in every stacked notification to change background on wear or add it
        // to stack summary so all wear stacked notifications get it. Or call setBackground on
        // wear extender for the stack summary notification (see tutorial docs)
        Notification stack1Notification = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.type_tube)
                .setContentTitle("Stack 1 map")
                .setContentText("See the map, plus text of first stacked notification. Could be very long!")
                .setGroup(notificationGroup).setSortKey("1").addAction(mapAction).build();
        Notification stack2Notification = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.type_tube)
                .setContentTitle("Stack 2")
                .setContentText(
                        "This time you're reading the second log text of the stacked notification. Again, it could be very long!")
                .setGroup(notificationGroup).setSortKey("2").build();
        Notification stackSummary = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.type_tube)
                //.setLargeIcon(bmpSeaPanorama)
                .setContentTitle("New stack notifications in your wear")
                .setContentText("Watch your wear for some stack notifications")
                .setStyle(new NotificationCompat.InboxStyle().addLine("Stack notification 1")
                        .addLine("Stack notification 2").setBigContentTitle("2 stack notifications")
                        .setSummaryText("Open your wear"))
                .setGroup(notificationGroup).setGroupSummary(true).build();

        WearableExtender extender = new WearableExtender().addAction(mapAction).addPage(secondPageNotification)
                .addPage(thirdPageNotification).addPage(forthPageNotification).setHintHideIcon(true)
                .setBackground(bmpMountainsPanorama);

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(android.R.drawable.ic_dialog_info).setLargeIcon(bmpSeaPanorama).setStyle(pictureStyle)
                .setAutoCancel(true).setContentTitle("Notifica di prova")
                .setContentText("Testo della notifica di prova").setContentIntent(viewPendingIntent)
                .extend(extender);

        // Get an instance of the NotificationManager service
        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);

        // Build the notification and issues it with notification manager.
        notificationManager.notify(notificationId + 100, notificationBuilder.build());

        //for stack notifications
        //        notificationManager.notify(notificationId, stack1Notification);  //for wear
        //        notificationManager.notify(notificationId+1, stack2Notification);  //for wear
        //        notificationManager.notify(notificationId+2, stackSummary); //for device

        /*
        GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
                @Override
                public void onConnected(Bundle connectionHint) {
                    mLogManager.d(LOG_TAG, "onConnected: " + connectionHint);
                    // Now you can use the data layer API
                }
                @Override
                public void onConnectionSuspended(int cause) {
                    mLogManager.d(LOG_TAG, "onConnectionSuspended: " + cause);
                }
            })
            .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
                @Override
                public void onConnectionFailed(ConnectionResult result) {
                    mLogManager.d(LOG_TAG, "onConnectionFailed: " + result);
                }
            })
            .addApi(Wearable.API)
            .build();
            
        PutDataMapRequest dataMap = PutDataMapRequest.create("/count");
        dataMap.getDataMap().putInt(COUNT_KEY, count++);
        PutDataRequest request = dataMap.asPutDataRequest();
        PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi
            .putDataItem(mGoogleApiClient, request);
        */
    }

    /**
     * Redraws all the routes and update activity sections with this brand new data
     */
    private void updateUIViews(boolean fullUpdate) {
        if (fullUpdate) {
            mLogManager.d(LOG_TAG, "Updating all the views");
            mRoutesPagerAdapter.dataChanged(); // Reloads data from the model

            TimetableModel mModel = mController.getModel();
            // Sets the ActionBar tabs with new information
            mActionBar.removeAllTabs();
            // For each of the sections in the app, add a tab to the action bar.
            for (int i = 0; i < mRoutesPagerAdapter.getCount(); i++) {
                // Create a tab with text corresponding to the page title defined by
                // the adapter. Also specify this Activity object, which implements
                // the TabListener interface, as the callback (listener) for when
                // this tab is selected.
                mActionBar.addTab(mActionBar.newTab().setTabListener(this));
            }

            mProgressBar.setVisibility(View.GONE);
            if (mModel.hasNoRoutes()) {
                mLogManager.d(LOG_TAG, "No routes found");
                mViewPager.setVisibility(View.GONE);
                mLblNoRoutes.setVisibility(View.VISIBLE);
            } else {
                mViewPager.setVisibility(View.VISIBLE);
                mLblNoRoutes.setVisibility(View.GONE);
            }
        }

        mLogManager.d(LOG_TAG, "Refreshing time UI elements");
        // Updates the tab titles
        for (int pos = 0; pos < mActionBar.getTabCount(); pos++) {
            ActionBar.Tab tab = mActionBar.getTabAt(pos);
            tab.setText(mRoutesPagerAdapter.getPageTitle(pos));
        }

        //refresh notification
        updateNotification();

        mLogManager.d(LOG_TAG, "Updating fragments");
        for (int index = 0; index < mRoutesPagerAdapter.getInstantiatedFragmentsCount(); index++) {
            // There isn't an easy way to retrieve current instantiated fragments in the ViewPager
            TimetableFragment fragment = (TimetableFragment) mRoutesPagerAdapter.getFragmentAt(index);
            if (null != fragment) {
                fragment.updateUIViews(fullUpdate);
            } else {
                mLogManager.d(LOG_TAG, "Cannot update fragment at pos " + index);
            }
        }

    }

    /**
     * Updates the text of the notification
     */
    private void updateNotification() {
        TimetableModel mModel = mController.getModel();

        // Get an instance of the NotificationManager service
        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);

        if (mModel.hasNoRoutes() || mModel.hasInvalidLeavingTimestamp()) {
            notificationManager.cancel(Utils.NOTIFICATION_ID);
            return;
        }

        Route route = mModel.getRoutes().get(0);

        NotificationCompat.BigTextStyle stepsStyle = new NotificationCompat.BigTextStyle()
                .setBigContentTitle(getString(R.string.timetable_msgNotifStepsTitle));
        SpannableStringBuilder sb = new SpannableStringBuilder();
        for (GeoPoint point : route.getSteps()) {
            String line = String.format(getString(R.string.timetable_msgNotifStepsLine),
                    point.isValidForAllTime() ? "--" : Utils.getTimeFromTimestamp(point.getValidTime()),
                    point.getName(), point.getType().toString());
            sb.append(Html.fromHtml(line));
        }
        stepsStyle.bigText(sb);

        // Notification only for the wear
        Notification stepsNotif = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_launcher)
                .setContentTitle(String.format(getString(R.string.timetable_msgNotifTitle),
                        Utils.getRemainingMinutesToRoute(route)))
                .setContentText(String.format(getString(R.string.timetable_msgNotifContent),
                        Utils.getRemainingMinutesToRoute(route),
                        Utils.getTimeFromTimestamp(route.getArrivalTimestamp())))
                .setStyle(stepsStyle).build();

        WearableExtender extender = new WearableExtender().addPage(stepsNotif)
        //                .setHintHideIcon(true)
        ;

        // Notification displayed on the device
        Notification nextMoveNotif = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_launcher)
                .setContentTitle(String.format(getString(R.string.timetable_msgNotifTitle),
                        Utils.getRemainingMinutesToRoute(route)))
                .setContentText(String.format(getString(R.string.timetable_msgNotifContent),
                        Utils.getRemainingMinutesToRoute(route),
                        Utils.getTimeFromTimestamp(route.getArrivalTimestamp())))
                .extend(extender).build();

        // Build the notification and issues it with notification manager.
        notificationManager.notify(Utils.NOTIFICATION_ID, nextMoveNotif);
    }

    private class MyReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            mLogManager.w(LOG_TAG, "Received broadcast message " + action);
            if (Utils.MESSAGE_REFRESH_TIME_VIEWS.equals(action)) {
                updateUIViews(false);
            } else if (Utils.MESSAGE_UPDATE_ALL_VIEWS.equals(action)) {
                updateUIViews(true);
            } else {
                mLogManager.w(LOG_TAG, "Received strange broadcast message " + action);
            }
        }
    }

    /**
     * A {@link FragmentPagerAdapter} that returns a fragment corresponding to
     * one of the sections/tabs/pages.
     */
    private class RoutesPagerAdapter extends FragmentPagerAdapter {
        private final TimetableModel mModel;
        private final SparseArray<WeakReference<Fragment>> mRegisteredFragments;

        public RoutesPagerAdapter(FragmentManager fm, TimetableModel timetableModel) {
            super(fm);
            mModel = timetableModel;
            mRegisteredFragments = new SparseArray<WeakReference<Fragment>>();
        }

        @Override
        public Fragment getItem(int position) {
            // getItem is called to instantiate the fragment for the given page.
            // Return a PlaceholderFragment (defined as a static inner class below).
            mLogManager.d(LOG_TAG, "Creating new fragment for pos " + position);
            return TimetableFragment.newInstance(position, mLogManager, mPositionManager);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            mLogManager.d(LOG_TAG, "Instantiate fragment for position " + position);
            Fragment fragment = (Fragment) super.instantiateItem(container, position);
            mRegisteredFragments.put(position, new WeakReference<Fragment>(fragment));
            return fragment;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            mLogManager.d(LOG_TAG, "Destroy fragment for position " + position);
            mRegisteredFragments.remove(position);
            super.destroyItem(container, position, object);
        }

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

        @Override
        public CharSequence getPageTitle(int position) {
            Route route = mModel.getRoutes().get(position);
            return String.format("%s%s -> %s", Utils.getRemainingMinutesToRoute(route),
                    getString(R.string.common_minutes_abbreviation),
                    Utils.getTimeFromTimestamp(route.getArrivalTimestamp()));
        }

        public void dataChanged() {
            notifyDataSetChanged();
        }

        public int getInstantiatedFragmentsCount() {
            return mRegisteredFragments.size();
        }

        public Fragment getFragmentAt(int index) {
            // Remember: index, not key (key = fragment position in the adapter)
            WeakReference<Fragment> ref = mRegisteredFragments.valueAt(index);
            return null == ref ? null : ref.get();
        }
    }
}