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