/* * Copyright (C) 2010 Cyril Mottier (http://www.cyrilmottier.com) * * 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 greendroid.app; import com.cyrilmottier.android.greendroid.R; import greendroid.util.Config; import greendroid.widget.ActionBar; import greendroid.widget.ActionBarHost; import greendroid.widget.ActionBarItem; import greendroid.widget.ActionBar.OnActionBarListener; import greendroid.widget.ActionBar.Type; import android.app.Activity; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.widget.FrameLayout; /** * <p> * An GDActivity is a regular Activity that hosts an {@link ActionBar}. It is * extremely simple to use as you have nothing particular to do. Indeed, the * {@link ActionBar} is automatically added to your own layout when using the * {@link #getContentView()} method. You can also use one of the * setActionBarContentView utility methods. As a result, a basic GDActivity will * often be initialized using the following snippet of code: * </p> * * <pre> * protected void onCreate(Bundle savedInstanceState) { * super.onCreate(savedInstanceState); * * setActionBarContentView(R.layout.main); * } * </pre> * <p> * An {@link ActionBar} is a widget that may contains actions items and a title. * You can also set the title putting an extra string with the key * {@link ActionBarActivity#GD_ACTION_BAR_TITLE} in your Intent: * </p> * * <pre> * Intent intent = new Intent(this, MyGDActivity.class); * intent.putExtra(ActionBarActivity.GD_ACTION_BAR_TITLE, "Next screen title"); * startActivity(intent); * </pre> * <p> * <em><strong>Note</strong>: An GDActivity automatically handle the type of the {@link ActionBar} * (taken from {@link ActionBar.Type}) depending on the value returned by the * {@link GDApplication#getHomeActivityClass()} method. However you can force the * type of the action bar in your constructor. Make the Activity declared in the AndroidManifest.xml * has at least a constructor with no arguments as required by the Android platform.</em> * </p> * * <pre> * public MyGDActivity() { * super(ActionBar.Type.Dashboard); * } * </pre> * <p> * All Activities that inherits from an GDActivity are notified when an action * button is tapped in the * {@link #onHandleActionBarItemClick(ActionBarItem, int)} method. By default * this method does nothing but return false. * </p> * * @see GDApplication#getHomeActivityClass() * @see ActionBarActivity#GD_ACTION_BAR_TITLE * @see GDActivity#setActionBarContentView(int) * @see GDActivity#setActionBarContentView(View) * @see GDActivity#setActionBarContentView(View, LayoutParams) * @author Cyril Mottier */ public class GDActivity extends Activity implements ActionBarActivity { private static final String LOG_TAG = GDActivity.class.getSimpleName(); private boolean mDefaultConstructorUsed = false; private Type mActionBarType; private ActionBarHost mActionBarHost; /** * <p> * Default constructor. * </p> * <p> * <em><strong>Note</strong>: This constructor should never be used manually. * In order to instantiate an Activity you should let the Android system do * it for you by calling startActivity(Intent)</em> * </p> */ public GDActivity() { this(Type.Normal); mDefaultConstructorUsed = true; } /** * <p> * Create a new Activity with an {@link ActionBar} of the given type. * </p> * <p> * <em><strong>Note</strong>: This constructor should never be used manually. * In order to instantiate an Activity you should let the Android system do * it for you by calling startActivity(Intent)</em> * </p> * * @param actionBarType The {@link ActionBar.Type} for this Activity */ public GDActivity(ActionBar.Type actionBarType) { super(); mActionBarType = actionBarType; } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { ensureLayout(); super.onRestoreInstanceState(savedInstanceState); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (mDefaultConstructorUsed) { // HACK cyril: This should have been done in the default // constructor. Unfortunately, the getApplication() method returns // null there. Hence, this has to be done here. if (getClass().equals(getGDApplication().getHomeActivityClass())) { mActionBarType = Type.Dashboard; } } } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); ensureLayout(); } /** * The current {@link ActionBar.Type} of the hosted {@link ActionBar} * * @return The current {@link ActionBar.Type} of the hosted * {@link ActionBar} */ public ActionBar.Type getActionBarType() { return mActionBarType; } public int createLayout() { switch (mActionBarType) { case Dashboard: return R.layout.gd_content_dashboard; case Empty: return R.layout.gd_content_empty; case Normal: default: return R.layout.gd_content_normal; } } /** * Call this method to ensure a layout has already been inflated and * attached to the top-level View of this Activity. */ protected void ensureLayout() { if (!verifyLayout()) { setContentView(createLayout()); } } /** * Verify the given layout contains everything needed by this Activity. A * GDActivity, for instance, manages an {@link ActionBarHost}. As a result * this method will return true of the current layout contains such a * widget. * * @return true if the current layout fits to the current Activity widgets * requirements */ protected boolean verifyLayout() { return mActionBarHost != null; } public GDApplication getGDApplication() { return (GDApplication) getApplication(); } @Override public void onContentChanged() { super.onContentChanged(); onPreContentChanged(); onPostContentChanged(); } public void onPreContentChanged() { mActionBarHost = (ActionBarHost) findViewById(R.id.gd_action_bar_host); if (mActionBarHost == null) { throw new RuntimeException("Your content must have an ActionBarHost whose id attribute is R.id.gd_action_bar_host"); } mActionBarHost.getActionBar().setOnActionBarListener(mActionBarListener); } public void onPostContentChanged() { boolean titleSet = false; final Intent intent = getIntent(); if (intent != null) { String title = intent.getStringExtra(ActionBarActivity.GD_ACTION_BAR_TITLE); if (title != null) { titleSet = true; setTitle(title); } } if (!titleSet) { // No title has been set via the Intent. Let's look in the // ActivityInfo try { final ActivityInfo activityInfo = getPackageManager().getActivityInfo(getComponentName(), 0); if (activityInfo.labelRes != 0) { setTitle(activityInfo.labelRes); } } catch (NameNotFoundException e) { // Do nothing } } final int visibility = intent.getIntExtra(ActionBarActivity.GD_ACTION_BAR_VISIBILITY, View.VISIBLE); getActionBar().setVisibility(visibility); } @Override public void setTitle(CharSequence title) { getActionBar().setTitle(title); } @Override public void setTitle(int titleId) { setTitle(getString(titleId)); } public ActionBar getActionBar() { ensureLayout(); return mActionBarHost.getActionBar(); } public ActionBarItem addActionBarItem(ActionBarItem item) { return getActionBar().addItem(item); } public ActionBarItem addActionBarItem(ActionBarItem item, int itemId) { return getActionBar().addItem(item, itemId); } public ActionBarItem addActionBarItem(ActionBarItem.Type actionBarItemType) { return getActionBar().addItem(actionBarItemType); } public ActionBarItem addActionBarItem(ActionBarItem.Type actionBarItemType, int itemId) { return getActionBar().addItem(actionBarItemType, itemId); } public FrameLayout getContentView() { ensureLayout(); return mActionBarHost.getContentView(); } /** * <p> * Set the activity content from a layout resource. The resource will be * inflated, adding all top-level views to the activity. * </p> * <p> * This method is an equivalent to setContentView(int) that automatically * wraps the given layout in an {@link ActionBarHost} if needed.. * </p> * * @param resID Resource ID to be inflated. * @see #setActionBarContentView(View) * @see #setActionBarContentView(View, LayoutParams) */ public void setActionBarContentView(int resID) { final FrameLayout contentView = getContentView(); contentView.removeAllViews(); LayoutInflater.from(this).inflate(resID, contentView); } /** * <p> * Set the activity content to an explicit view. This view is placed * directly into the activity's view hierarchy. It can itself be a complex * view hierarchy. * </p> * <p> * This method is an equivalent to setContentView(View, LayoutParams) that * automatically wraps the given layout in an {@link ActionBarHost} if * needed. * </p> * * @param view The desired content to display. * @param params Layout parameters for the view. * @see #setActionBarContentView(View) * @see #setActionBarContentView(int) */ public void setActionBarContentView(View view, LayoutParams params) { final FrameLayout contentView = getContentView(); contentView.removeAllViews(); contentView.addView(view, params); } /** * <p> * Set the activity content to an explicit view. This view is placed * directly into the activity's view hierarchy. It can itself be a complex * view hierarchy. * </p> * <p> * This method is an equivalent to setContentView(View) that automatically * wraps the given layout in an {@link ActionBarHost} if needed. * </p> * * @param view The desired content to display. * @see #setActionBarContentView(int) * @see #setActionBarContentView(View, LayoutParams) */ public void setActionBarContentView(View view) { final FrameLayout contentView = getContentView(); contentView.removeAllViews(); contentView.addView(view); } public boolean onHandleActionBarItemClick(ActionBarItem item, int position) { return false; } private OnActionBarListener mActionBarListener = new OnActionBarListener() { public void onActionBarItemClicked(int position) { if (position == OnActionBarListener.HOME_ITEM) { final GDApplication app = getGDApplication(); switch (mActionBarType) { case Normal: final Class<?> klass = app.getHomeActivityClass(); if (klass != null && !klass.equals(GDActivity.this.getClass())) { if (Config.GD_INFO_LOGS_ENABLED) { Log.i(LOG_TAG, "Going back to the home activity"); } Intent homeIntent = new Intent(GDActivity.this, klass); homeIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(homeIntent); } break; case Dashboard: final Intent appIntent = app.getMainApplicationIntent(); if (appIntent != null) { if (Config.GD_INFO_LOGS_ENABLED) { Log.i(LOG_TAG, "Launching the main application Intent"); } startActivity(appIntent); } break; } } else { if (!onHandleActionBarItemClick(getActionBar().getItem(position), position)) { if (Config.GD_WARNING_LOGS_ENABLED) { Log.w(LOG_TAG, "Click on item at position " + position + " dropped down to the floor"); } } } } }; }