summaryrefslogtreecommitdiffstats
path: root/src/com/android/messaging/ui/BugleActionBarActivity.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/messaging/ui/BugleActionBarActivity.java')
-rw-r--r--src/com/android/messaging/ui/BugleActionBarActivity.java356
1 files changed, 356 insertions, 0 deletions
diff --git a/src/com/android/messaging/ui/BugleActionBarActivity.java b/src/com/android/messaging/ui/BugleActionBarActivity.java
new file mode 100644
index 0000000..80dabb8
--- /dev/null
+++ b/src/com/android/messaging/ui/BugleActionBarActivity.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2015 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.android.messaging.ui;
+
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarActivity;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.android.messaging.R;
+import com.android.messaging.util.BugleActivityUtil;
+import com.android.messaging.util.ImeUtil;
+import com.android.messaging.util.LogUtil;
+import com.android.messaging.util.UiUtils;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Base class for app activities that use an action bar. Responsible for logging/telemetry/other
+ * needs that will be common for all activities. We can break out the common code if/when we need
+ * a version that doesn't use an actionbar.
+ */
+public class BugleActionBarActivity extends ActionBarActivity implements ImeUtil.ImeStateHost {
+ // Tracks the list of observers opting in for IME state change.
+ private final Set<ImeUtil.ImeStateObserver> mImeStateObservers = new HashSet<>();
+
+ // Tracks the soft keyboard display state
+ private boolean mImeOpen;
+
+ // The ActionMode that represents the modal contextual action bar, using our own implementation
+ // rather than the built in contextual action bar to reduce jank
+ private CustomActionMode mActionMode;
+
+ // The menu for the action bar
+ private Menu mActionBarMenu;
+
+ // Used to determine if a onDisplayHeightChanged was due to the IME opening or rotation of the
+ // device
+ private int mLastScreenHeight;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (UiUtils.redirectToPermissionCheckIfNeeded(this)) {
+ return;
+ }
+
+ mLastScreenHeight = getResources().getDisplayMetrics().heightPixels;
+ if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.VERBOSE)) {
+ LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onCreate");
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.VERBOSE)) {
+ LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onStart");
+ }
+ }
+
+ @Override
+ protected void onRestart() {
+ super.onStop();
+ if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.VERBOSE)) {
+ LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onRestart");
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.VERBOSE)) {
+ LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onResume");
+ }
+ BugleActivityUtil.onActivityResume(this, BugleActionBarActivity.this);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.VERBOSE)) {
+ LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onPause");
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.VERBOSE)) {
+ LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onStop");
+ }
+ }
+
+ private boolean mDestroyed;
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mDestroyed = true;
+ }
+
+ public boolean getIsDestroyed() {
+ return mDestroyed;
+ }
+
+ @Override
+ public void onDisplayHeightChanged(final int heightSpecification) {
+ int screenHeight = getResources().getDisplayMetrics().heightPixels;
+
+ if (screenHeight != mLastScreenHeight) {
+ // Appears to be an orientation change, don't fire ime updates
+ mLastScreenHeight = screenHeight;
+ LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onDisplayHeightChanged " +
+ " screenHeight: " + screenHeight + " lastScreenHeight: " + mLastScreenHeight +
+ " Skipped, appears to be orientation change.");
+ return;
+ }
+ final ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null && actionBar.isShowing()) {
+ screenHeight -= actionBar.getHeight();
+ }
+ final int height = View.MeasureSpec.getSize(heightSpecification);
+
+ final boolean imeWasOpen = mImeOpen;
+ mImeOpen = screenHeight - height > 100;
+
+ if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.VERBOSE)) {
+ LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onDisplayHeightChanged " +
+ "imeWasOpen: " + imeWasOpen + " mImeOpen: " + mImeOpen + " screenHeight: " +
+ screenHeight + " height: " + height);
+ }
+
+ if (imeWasOpen != mImeOpen) {
+ for (final ImeUtil.ImeStateObserver observer : mImeStateObservers) {
+ observer.onImeStateChanged(mImeOpen);
+ }
+ }
+ }
+
+ @Override
+ public void registerImeStateObserver(final ImeUtil.ImeStateObserver observer) {
+ mImeStateObservers.add(observer);
+ }
+
+ @Override
+ public void unregisterImeStateObserver(final ImeUtil.ImeStateObserver observer) {
+ mImeStateObservers.remove(observer);
+ }
+
+ @Override
+ public boolean isImeOpen() {
+ return mImeOpen;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ mActionBarMenu = menu;
+ if (mActionMode != null &&
+ mActionMode.getCallback().onCreateActionMode(mActionMode, menu)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(final Menu menu) {
+ mActionBarMenu = menu;
+ if (mActionMode != null &&
+ mActionMode.getCallback().onPrepareActionMode(mActionMode, menu)) {
+ return true;
+ }
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem menuItem) {
+ if (mActionMode != null &&
+ mActionMode.getCallback().onActionItemClicked(mActionMode, menuItem)) {
+ return true;
+ }
+
+ switch (menuItem.getItemId()) {
+ case android.R.id.home:
+ if (mActionMode != null) {
+ dismissActionMode();
+ return true;
+ }
+ }
+ return super.onOptionsItemSelected(menuItem);
+ }
+
+ @Override
+ public ActionMode startActionMode(final ActionMode.Callback callback) {
+ mActionMode = new CustomActionMode(callback);
+ supportInvalidateOptionsMenu();
+ invalidateActionBar();
+ return mActionMode;
+ }
+
+ public void dismissActionMode() {
+ if (mActionMode != null) {
+ mActionMode.finish();
+ mActionMode = null;
+ invalidateActionBar();
+ }
+ }
+
+ public ActionMode getActionMode() {
+ return mActionMode;
+ }
+
+ protected ActionMode.Callback getActionModeCallback() {
+ if (mActionMode == null) {
+ return null;
+ }
+
+ return mActionMode.getCallback();
+ }
+
+ /**
+ * Receives and handles action bar invalidation request from sub-components of this activity.
+ *
+ * <p>Normally actions have sole control over the action bar, but in order to support seamless
+ * transitions for components such as the full screen media picker, we have to let it take over
+ * the action bar and then restore its state afterwards</p>
+ *
+ * <p>If a fragment does anything that may change the action bar, it should call this method
+ * and then it is this method's responsibility to figure out which component "controls" the
+ * action bar and delegate the updating of the action bar to that component</p>
+ */
+ public final void invalidateActionBar() {
+ if (mActionMode != null) {
+ mActionMode.updateActionBar(getSupportActionBar());
+ } else {
+ updateActionBar(getSupportActionBar());
+ }
+ }
+
+ protected void updateActionBar(final ActionBar actionBar) {
+ actionBar.setHomeAsUpIndicator(null);
+ }
+
+ /**
+ * Custom ActionMode implementation which allows us to just replace the contents of the main
+ * action bar rather than overlay over it
+ */
+ private class CustomActionMode extends ActionMode {
+ private CharSequence mTitle;
+ private CharSequence mSubtitle;
+ private View mCustomView;
+ private final Callback mCallback;
+
+ public CustomActionMode(final Callback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void setTitle(final CharSequence title) {
+ mTitle = title;
+ }
+
+ @Override
+ public void setTitle(final int resId) {
+ mTitle = getResources().getString(resId);
+ }
+
+ @Override
+ public void setSubtitle(final CharSequence subtitle) {
+ mSubtitle = subtitle;
+ }
+
+ @Override
+ public void setSubtitle(final int resId) {
+ mSubtitle = getResources().getString(resId);
+ }
+
+ @Override
+ public void setCustomView(final View view) {
+ mCustomView = view;
+ }
+
+ @Override
+ public void invalidate() {
+ invalidateActionBar();
+ }
+
+ @Override
+ public void finish() {
+ mActionMode = null;
+ mCallback.onDestroyActionMode(this);
+ supportInvalidateOptionsMenu();
+ invalidateActionBar();
+ }
+
+ @Override
+ public Menu getMenu() {
+ return mActionBarMenu;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ @Override
+ public CharSequence getSubtitle() {
+ return mSubtitle;
+ }
+
+ @Override
+ public View getCustomView() {
+ return mCustomView;
+ }
+
+ @Override
+ public MenuInflater getMenuInflater() {
+ return BugleActionBarActivity.this.getMenuInflater();
+ }
+
+ public Callback getCallback() {
+ return mCallback;
+ }
+
+ public void updateActionBar(final ActionBar actionBar) {
+ actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP);
+ actionBar.setDisplayShowTitleEnabled(false);
+ actionBar.setDisplayShowCustomEnabled(false);
+ mActionMode.getCallback().onPrepareActionMode(mActionMode, mActionBarMenu);
+ actionBar.setBackgroundDrawable(new ColorDrawable(
+ getResources().getColor(R.color.contextual_action_bar_background_color)));
+ actionBar.setHomeAsUpIndicator(R.drawable.ic_cancel_small_dark);
+ actionBar.show();
+ }
+ }
+}