Home | History | Annotate | Download | only in hcgallery
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.hcgallery;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.animation.ObjectAnimator;
     22 import android.animation.PropertyValuesHolder;
     23 import android.animation.ValueAnimator;
     24 import android.app.ActionBar;
     25 import android.app.Activity;
     26 import android.app.AlertDialog;
     27 import android.app.Dialog;
     28 import android.app.DialogFragment;
     29 import android.app.FragmentManager;
     30 import android.app.FragmentTransaction;
     31 import android.app.Notification;
     32 import android.app.NotificationManager;
     33 import android.app.PendingIntent;
     34 import android.content.DialogInterface;
     35 import android.content.Intent;
     36 import android.content.pm.PackageManager;
     37 import android.content.res.Configuration;
     38 import android.content.res.Resources;
     39 import android.graphics.Bitmap;
     40 import android.graphics.BitmapFactory;
     41 import android.os.Bundle;
     42 import android.view.Menu;
     43 import android.view.MenuInflater;
     44 import android.view.MenuItem;
     45 import android.view.View;
     46 import android.view.ViewGroup;
     47 import android.widget.RemoteViews;
     48 
     49 /** This is the main "launcher" activity.
     50  * When running on a "large" or larger screen, this activity displays both the
     51  * TitlesFragments and the Content Fragment. When on a smaller screen size, this
     52  * activity displays only the TitlesFragment. In which case, selecting a list
     53  * item opens the ContentActivity, holds only the ContentFragment. */
     54 public class MainActivity extends Activity implements TitlesFragment.OnItemSelectedListener {
     55 
     56     private Animator mCurrentTitlesAnimator;
     57     private String[] mToggleLabels = {"Show Titles", "Hide Titles"};
     58     private static final int NOTIFICATION_DEFAULT = 1;
     59     private static final String ACTION_DIALOG = "com.example.android.hcgallery.action.DIALOG";
     60     private int mThemeId = -1;
     61     private boolean mDualFragments = false;
     62     private boolean mTitlesHidden = false;
     63 
     64     @Override
     65     public void onCreate(Bundle savedInstanceState) {
     66         super.onCreate(savedInstanceState);
     67 
     68         if(savedInstanceState != null) {
     69             if (savedInstanceState.getInt("theme", -1) != -1) {
     70               mThemeId = savedInstanceState.getInt("theme");
     71               this.setTheme(mThemeId);
     72             }
     73             mTitlesHidden = savedInstanceState.getBoolean("titlesHidden");
     74         }
     75 
     76         setContentView(R.layout.main);
     77 
     78         ActionBar bar = getActionBar();
     79         bar.setDisplayShowTitleEnabled(false);
     80 
     81         ContentFragment frag = (ContentFragment) getFragmentManager()
     82                 .findFragmentById(R.id.content_frag);
     83         if (frag != null) mDualFragments = true;
     84 
     85         if (mTitlesHidden) {
     86             getFragmentManager().beginTransaction()
     87                     .hide(getFragmentManager().findFragmentById(R.id.titles_frag)).commit();
     88         }
     89     }
     90 
     91     @Override
     92     public boolean onCreateOptionsMenu(Menu menu) {
     93         MenuInflater inflater = getMenuInflater();
     94         inflater.inflate(R.menu.main_menu, menu);
     95         // If the device doesn't support camera, remove the camera menu item
     96         if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
     97             menu.removeItem(R.id.menu_camera);
     98         }
     99         return true;
    100     }
    101 
    102     @Override
    103     public boolean onPrepareOptionsMenu(Menu menu) {
    104         // If not showing both fragments, remove the "toggle titles" menu item
    105         if (!mDualFragments) {
    106             menu.removeItem(R.id.menu_toggleTitles);
    107         } else {
    108             menu.findItem(R.id.menu_toggleTitles).setTitle(mToggleLabels[mTitlesHidden ? 0 : 1]);
    109         }
    110         return super.onPrepareOptionsMenu(menu);
    111     }
    112 
    113     @Override
    114     public boolean onOptionsItemSelected(MenuItem item) {
    115         switch (item.getItemId()) {
    116         case R.id.menu_camera:
    117             Intent intent = new Intent(this, CameraActivity.class);
    118             intent.putExtra("theme", mThemeId);
    119             startActivity(intent);
    120             return true;
    121 
    122         case R.id.menu_toggleTitles:
    123             toggleVisibleTitles();
    124             return true;
    125 
    126         case R.id.menu_toggleTheme:
    127             if (mThemeId == R.style.AppTheme_Dark) {
    128                 mThemeId = R.style.AppTheme_Light;
    129             } else {
    130                 mThemeId = R.style.AppTheme_Dark;
    131             }
    132             this.recreate();
    133             return true;
    134 
    135         case R.id.menu_showDialog:
    136             showDialog("This is indeed an awesome dialog.");
    137             return true;
    138 
    139         case R.id.menu_showStandardNotification:
    140             showNotification(false);
    141             return true;
    142 
    143         case R.id.menu_showCustomNotification:
    144             showNotification(true);
    145             return true;
    146 
    147         default:
    148             return super.onOptionsItemSelected(item);
    149         }
    150     }
    151 
    152     /** Respond to the "toogle titles" item in the action bar */
    153     public void toggleVisibleTitles() {
    154         // Use these for custom animations.
    155         final FragmentManager fm = getFragmentManager();
    156         final TitlesFragment f = (TitlesFragment) fm
    157                 .findFragmentById(R.id.titles_frag);
    158         final View titlesView = f.getView();
    159 
    160         // Determine if we're in portrait, and whether we're showing or hiding the titles
    161         // with this toggle.
    162         final boolean isPortrait = getResources().getConfiguration().orientation ==
    163                 Configuration.ORIENTATION_PORTRAIT;
    164 
    165         final boolean shouldShow = f.isHidden() || mCurrentTitlesAnimator != null;
    166 
    167         // Cancel the current titles animation if there is one.
    168         if (mCurrentTitlesAnimator != null)
    169             mCurrentTitlesAnimator.cancel();
    170 
    171         // Begin setting up the object animator. We'll animate the bottom or right edge of the
    172         // titles view, as well as its alpha for a fade effect.
    173         ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(
    174                 titlesView,
    175                 PropertyValuesHolder.ofInt(
    176                         isPortrait ? "bottom" : "right",
    177                         shouldShow ? getResources().getDimensionPixelSize(R.dimen.titles_size)
    178                                    : 0),
    179                 PropertyValuesHolder.ofFloat("alpha", shouldShow ? 1 : 0)
    180         );
    181 
    182         // At each step of the animation, we'll perform layout by calling setLayoutParams.
    183         final ViewGroup.LayoutParams lp = titlesView.getLayoutParams();
    184         objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    185             public void onAnimationUpdate(ValueAnimator valueAnimator) {
    186                 // *** WARNING ***: triggering layout at each animation frame highly impacts
    187                 // performance so you should only do this for simple layouts. More complicated
    188                 // layouts can be better served with individual animations on child views to
    189                 // avoid the performance penalty of layout.
    190                 if (isPortrait) {
    191                     lp.height = (Integer) valueAnimator.getAnimatedValue();
    192                 } else {
    193                     lp.width = (Integer) valueAnimator.getAnimatedValue();
    194                 }
    195                 titlesView.setLayoutParams(lp);
    196             }
    197         });
    198 
    199         if (shouldShow) {
    200             fm.beginTransaction().show(f).commit();
    201             objectAnimator.addListener(new AnimatorListenerAdapter() {
    202                 @Override
    203                 public void onAnimationEnd(Animator animator) {
    204                     mCurrentTitlesAnimator = null;
    205                     mTitlesHidden = false;
    206                     invalidateOptionsMenu();
    207                 }
    208             });
    209 
    210         } else {
    211             objectAnimator.addListener(new AnimatorListenerAdapter() {
    212                 boolean canceled;
    213 
    214                 @Override
    215                 public void onAnimationCancel(Animator animation) {
    216                     canceled = true;
    217                     super.onAnimationCancel(animation);
    218                 }
    219 
    220                 @Override
    221                 public void onAnimationEnd(Animator animator) {
    222                     if (canceled)
    223                         return;
    224                     mCurrentTitlesAnimator = null;
    225                     fm.beginTransaction().hide(f).commit();
    226                     mTitlesHidden = true;
    227                     invalidateOptionsMenu();
    228                 }
    229             });
    230         }
    231 
    232         // Start the animation.
    233         objectAnimator.start();
    234         mCurrentTitlesAnimator = objectAnimator;
    235 
    236         // Manually trigger onNewIntent to check for ACTION_DIALOG.
    237         onNewIntent(getIntent());
    238     }
    239 
    240     @Override
    241     protected void onNewIntent(Intent intent) {
    242         if (ACTION_DIALOG.equals(intent.getAction())) {
    243             showDialog(intent.getStringExtra(Intent.EXTRA_TEXT));
    244         }
    245     }
    246 
    247     void showDialog(String text) {
    248         // DialogFragment.show() will take care of adding the fragment
    249         // in a transaction.  We also want to remove any currently showing
    250         // dialog, so make our own transaction and take care of that here.
    251         FragmentTransaction ft = getFragmentManager().beginTransaction();
    252 
    253         DialogFragment newFragment = MyDialogFragment.newInstance(text);
    254 
    255         // Show the dialog.
    256         newFragment.show(ft, "dialog");
    257     }
    258 
    259     void showNotification(boolean custom) {
    260         final Resources res = getResources();
    261         final NotificationManager notificationManager = (NotificationManager) getSystemService(
    262                 NOTIFICATION_SERVICE);
    263 
    264         Notification.Builder builder = new Notification.Builder(this)
    265                 .setSmallIcon(R.drawable.ic_stat_notify_example)
    266                 .setAutoCancel(true)
    267                 .setTicker(getString(R.string.notification_text))
    268                 .setContentIntent(getDialogPendingIntent("Tapped the notification entry."));
    269 
    270         if (custom) {
    271             // Sets a custom content view for the notification, including an image button.
    272             RemoteViews layout = new RemoteViews(getPackageName(), R.layout.notification);
    273             layout.setTextViewText(R.id.notification_title, getString(R.string.app_name));
    274             layout.setOnClickPendingIntent(R.id.notification_button,
    275                     getDialogPendingIntent("Tapped the 'dialog' button in the notification."));
    276             builder.setContent(layout);
    277 
    278             // Notifications in Android 3.0 now have a standard mechanism for displaying large
    279             // bitmaps such as contact avatars. Here, we load an example image and resize it to the
    280             // appropriate size for large bitmaps in notifications.
    281             Bitmap largeIconTemp = BitmapFactory.decodeResource(res,
    282                     R.drawable.notification_default_largeicon);
    283             Bitmap largeIcon = Bitmap.createScaledBitmap(
    284                     largeIconTemp,
    285                     res.getDimensionPixelSize(android.R.dimen.notification_large_icon_width),
    286                     res.getDimensionPixelSize(android.R.dimen.notification_large_icon_height),
    287                     false);
    288             largeIconTemp.recycle();
    289 
    290             builder.setLargeIcon(largeIcon);
    291 
    292         } else {
    293             builder
    294                     .setNumber(7) // An example number.
    295                     .setContentTitle(getString(R.string.app_name))
    296                     .setContentText(getString(R.string.notification_text));
    297         }
    298 
    299         notificationManager.notify(NOTIFICATION_DEFAULT, builder.getNotification());
    300     }
    301 
    302     PendingIntent getDialogPendingIntent(String dialogText) {
    303         return PendingIntent.getActivity(
    304                 this,
    305                 dialogText.hashCode(), // Otherwise previous PendingIntents with the same
    306                                        // requestCode may be overwritten.
    307                 new Intent(ACTION_DIALOG)
    308                         .putExtra(Intent.EXTRA_TEXT, dialogText)
    309                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
    310                 0);
    311     }
    312 
    313     @Override
    314     public void onSaveInstanceState (Bundle outState) {
    315         super.onSaveInstanceState(outState);
    316         outState.putInt("theme", mThemeId);
    317         outState.putBoolean("titlesHidden", mTitlesHidden);
    318     }
    319 
    320     /** Implementation for TitlesFragment.OnItemSelectedListener.
    321      * When the TitlesFragment receives an onclick event for a list item,
    322      * it's passed back to this activity through this method so that we can
    323      * deliver it to the ContentFragment in the manner appropriate */
    324     public void onItemSelected(int category, int position) {
    325       if (!mDualFragments) {
    326           // If showing only the TitlesFragment, start the ContentActivity and
    327           // pass it the info about the selected item
    328           Intent intent = new Intent(this, ContentActivity.class);
    329           intent.putExtra("category", category);
    330           intent.putExtra("position", position);
    331           intent.putExtra("theme", mThemeId);
    332           startActivity(intent);
    333       } else {
    334           // If showing both fragments, directly update the ContentFragment
    335           ContentFragment frag = (ContentFragment) getFragmentManager()
    336                   .findFragmentById(R.id.content_frag);
    337           frag.updateContentAndRecycleBitmap(category, position);
    338       }
    339     }
    340 
    341 
    342     /** Dialog implementation that shows a simple dialog as a fragment */
    343     public static class MyDialogFragment extends DialogFragment {
    344 
    345         public static MyDialogFragment newInstance(String title) {
    346             MyDialogFragment frag = new MyDialogFragment();
    347             Bundle args = new Bundle();
    348             args.putString("text", title);
    349             frag.setArguments(args);
    350             return frag;
    351         }
    352 
    353         @Override
    354         public Dialog onCreateDialog(Bundle savedInstanceState) {
    355             String text = getArguments().getString("text");
    356 
    357             return new AlertDialog.Builder(getActivity())
    358                     .setTitle("A Dialog of Awesome")
    359                     .setMessage(text)
    360                     .setPositiveButton(android.R.string.ok,
    361                             new DialogInterface.OnClickListener() {
    362                                 public void onClick(DialogInterface dialog, int whichButton) {
    363                                 }
    364                             }
    365                     )
    366                     .create();
    367         }
    368     }
    369 }
    370