blob: 06905b8571ae37da6dadd79fdbff69ae656cf83f [file] [log] [blame]
/*
* 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();
}
}
}