/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.android.hcgallery; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.app.ActionBar; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.RemoteViews; /** This is the main "launcher" activity. * When running on a "large" or larger screen, this activity displays both the * TitlesFragments and the Content Fragment. When on a smaller screen size, this * activity displays only the TitlesFragment. In which case, selecting a list * item opens the ContentActivity, holds only the ContentFragment. */ public class MainActivity extends Activity implements TitlesFragment.OnItemSelectedListener { private Animator mCurrentTitlesAnimator; private String[] mToggleLabels = {"Show Titles", "Hide Titles"}; private static final int NOTIFICATION_DEFAULT = 1; private static final String ACTION_DIALOG = "com.example.android.hcgallery.action.DIALOG"; private int mThemeId = -1; private boolean mDualFragments = false; private boolean mTitlesHidden = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if(savedInstanceState != null) { if (savedInstanceState.getInt("theme", -1) != -1) { mThemeId = savedInstanceState.getInt("theme"); this.setTheme(mThemeId); } mTitlesHidden = savedInstanceState.getBoolean("titlesHidden"); } setContentView(R.layout.main); ActionBar bar = getActionBar(); bar.setDisplayShowTitleEnabled(false); ContentFragment frag = (ContentFragment) getFragmentManager() .findFragmentById(R.id.content_frag); if (frag != null) mDualFragments = true; if (mTitlesHidden) { getFragmentManager().beginTransaction() .hide(getFragmentManager().findFragmentById(R.id.titles_frag)).commit(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main_menu, menu); // If the device doesn't support camera, remove the camera menu item if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) { menu.removeItem(R.id.menu_camera); } return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { // If not showing both fragments, remove the "toggle titles" menu item if (!mDualFragments) { menu.removeItem(R.id.menu_toggleTitles); } else { menu.findItem(R.id.menu_toggleTitles).setTitle(mToggleLabels[mTitlesHidden ? 0 : 1]); } return super.onPrepareOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_camera: Intent intent = new Intent(this, CameraActivity.class); intent.putExtra("theme", mThemeId); startActivity(intent); return true; case R.id.menu_toggleTitles: toggleVisibleTitles(); return true; case R.id.menu_toggleTheme: if (mThemeId == R.style.AppTheme_Dark) { mThemeId = R.style.AppTheme_Light; } else { mThemeId = R.style.AppTheme_Dark; } this.recreate(); return true; case R.id.menu_showDialog: showDialog("This is indeed an awesome dialog."); return true; case R.id.menu_showStandardNotification: showNotification(false); return true; case R.id.menu_showCustomNotification: showNotification(true); return true; default: return super.onOptionsItemSelected(item); } } /** Respond to the "toogle titles" item in the action bar */ public void toggleVisibleTitles() { // Use these for custom animations. final FragmentManager fm = getFragmentManager(); final TitlesFragment f = (TitlesFragment) fm .findFragmentById(R.id.titles_frag); final View titlesView = f.getView(); // Determine if we're in portrait, and whether we're showing or hiding the titles // with this toggle. final boolean isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; final boolean shouldShow = f.isHidden() || mCurrentTitlesAnimator != null; // Cancel the current titles animation if there is one. if (mCurrentTitlesAnimator != null) mCurrentTitlesAnimator.cancel(); // Begin setting up the object animator. We'll animate the bottom or right edge of the // titles view, as well as its alpha for a fade effect. ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder( titlesView, PropertyValuesHolder.ofInt( isPortrait ? "bottom" : "right", shouldShow ? getResources().getDimensionPixelSize(R.dimen.titles_size) : 0), PropertyValuesHolder.ofFloat("alpha", shouldShow ? 1 : 0) ); // At each step of the animation, we'll perform layout by calling setLayoutParams. final ViewGroup.LayoutParams lp = titlesView.getLayoutParams(); objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator valueAnimator) { // *** WARNING ***: triggering layout at each animation frame highly impacts // performance so you should only do this for simple layouts. More complicated // layouts can be better served with individual animations on child views to // avoid the performance penalty of layout. if (isPortrait) { lp.height = (Integer) valueAnimator.getAnimatedValue(); } else { lp.width = (Integer) valueAnimator.getAnimatedValue(); } titlesView.setLayoutParams(lp); } }); if (shouldShow) { fm.beginTransaction().show(f).commit(); objectAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animator) { mCurrentTitlesAnimator = null; mTitlesHidden = false; invalidateOptionsMenu(); } }); } else { objectAnimator.addListener(new AnimatorListenerAdapter() { boolean canceled; @Override public void onAnimationCancel(Animator animation) { canceled = true; super.onAnimationCancel(animation); } @Override public void onAnimationEnd(Animator animator) { if (canceled) return; mCurrentTitlesAnimator = null; fm.beginTransaction().hide(f).commit(); mTitlesHidden = true; invalidateOptionsMenu(); } }); } // Start the animation. objectAnimator.start(); mCurrentTitlesAnimator = objectAnimator; // Manually trigger onNewIntent to check for ACTION_DIALOG. onNewIntent(getIntent()); } @Override protected void onNewIntent(Intent intent) { if (ACTION_DIALOG.equals(intent.getAction())) { showDialog(intent.getStringExtra(Intent.EXTRA_TEXT)); } } void showDialog(String text) { // DialogFragment.show() will take care of adding the fragment // in a transaction. We also want to remove any currently showing // dialog, so make our own transaction and take care of that here. FragmentTransaction ft = getFragmentManager().beginTransaction(); DialogFragment newFragment = MyDialogFragment.newInstance(text); // Show the dialog. newFragment.show(ft, "dialog"); } void showNotification(boolean custom) { final Resources res = getResources(); final NotificationManager notificationManager = (NotificationManager) getSystemService( NOTIFICATION_SERVICE); Notification.Builder builder = new Notification.Builder(this) .setSmallIcon(R.drawable.ic_stat_notify_example) .setAutoCancel(true) .setTicker(getString(R.string.notification_text)) .setContentIntent(getDialogPendingIntent("Tapped the notification entry.")); if (custom) { // Sets a custom content view for the notification, including an image button. RemoteViews layout = new RemoteViews(getPackageName(), R.layout.notification); layout.setTextViewText(R.id.notification_title, getString(R.string.app_name)); layout.setOnClickPendingIntent(R.id.notification_button, getDialogPendingIntent("Tapped the 'dialog' button in the notification.")); builder.setContent(layout); // Notifications in Android 3.0 now have a standard mechanism for displaying large // bitmaps such as contact avatars. Here, we load an example image and resize it to the // appropriate size for large bitmaps in notifications. Bitmap largeIconTemp = BitmapFactory.decodeResource(res, R.drawable.notification_default_largeicon); Bitmap largeIcon = Bitmap.createScaledBitmap( largeIconTemp, res.getDimensionPixelSize(android.R.dimen.notification_large_icon_width), res.getDimensionPixelSize(android.R.dimen.notification_large_icon_height), false); largeIconTemp.recycle(); builder.setLargeIcon(largeIcon); } else { builder .setNumber(7) // An example number. .setContentTitle(getString(R.string.app_name)) .setContentText(getString(R.string.notification_text)); } notificationManager.notify(NOTIFICATION_DEFAULT, builder.getNotification()); } PendingIntent getDialogPendingIntent(String dialogText) { return PendingIntent.getActivity( this, dialogText.hashCode(), // Otherwise previous PendingIntents with the same // requestCode may be overwritten. new Intent(ACTION_DIALOG) .putExtra(Intent.EXTRA_TEXT, dialogText) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0); } @Override public void onSaveInstanceState (Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("theme", mThemeId); outState.putBoolean("titlesHidden", mTitlesHidden); } /** Implementation for TitlesFragment.OnItemSelectedListener. * When the TitlesFragment receives an onclick event for a list item, * it's passed back to this activity through this method so that we can * deliver it to the ContentFragment in the manner appropriate */ public void onItemSelected(int category, int position) { if (!mDualFragments) { // If showing only the TitlesFragment, start the ContentActivity and // pass it the info about the selected item Intent intent = new Intent(this, ContentActivity.class); intent.putExtra("category", category); intent.putExtra("position", position); intent.putExtra("theme", mThemeId); startActivity(intent); } else { // If showing both fragments, directly update the ContentFragment ContentFragment frag = (ContentFragment) getFragmentManager() .findFragmentById(R.id.content_frag); frag.updateContentAndRecycleBitmap(category, position); } } /** Dialog implementation that shows a simple dialog as a fragment */ public static class MyDialogFragment extends DialogFragment { public static MyDialogFragment newInstance(String title) { MyDialogFragment frag = new MyDialogFragment(); Bundle args = new Bundle(); args.putString("text", title); frag.setArguments(args); return frag; } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { String text = getArguments().getString("text"); return new AlertDialog.Builder(getActivity()) .setTitle("A Dialog of Awesome") .setMessage(text) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } } ) .create(); } } }