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
onCreate(Bundle savedInstanceState)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
onCreateOptionsMenu(Menu menu)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
onPrepareOptionsMenu(Menu menu)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
onOptionsItemSelected(MenuItem item)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 */
toggleVisibleTitles()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
onNewIntent(Intent intent)241     protected void onNewIntent(Intent intent) {
242         if (ACTION_DIALOG.equals(intent.getAction())) {
243             showDialog(intent.getStringExtra(Intent.EXTRA_TEXT));
244         }
245     }
246 
showDialog(String text)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 
showNotification(boolean custom)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 
getDialogPendingIntent(String dialogText)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
onSaveInstanceState(Bundle outState)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 */
onItemSelected(int category, int position)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 
newInstance(String title)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
onCreateDialog(Bundle savedInstanceState)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