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
17package com.example.android.hcgallery;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
22import android.animation.PropertyValuesHolder;
23import android.animation.ValueAnimator;
24import android.app.ActionBar;
25import android.app.Activity;
26import android.app.AlertDialog;
27import android.app.Dialog;
28import android.app.DialogFragment;
29import android.app.FragmentManager;
30import android.app.FragmentTransaction;
31import android.app.Notification;
32import android.app.NotificationManager;
33import android.app.PendingIntent;
34import android.content.DialogInterface;
35import android.content.Intent;
36import android.content.pm.PackageManager;
37import android.content.res.Configuration;
38import android.content.res.Resources;
39import android.graphics.Bitmap;
40import android.graphics.BitmapFactory;
41import android.os.Bundle;
42import android.view.Menu;
43import android.view.MenuInflater;
44import android.view.MenuItem;
45import android.view.View;
46import android.view.ViewGroup;
47import 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. */
54public 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