/* * Copyright 2012 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.notificationstudio; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.content.Context; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.util.DisplayMetrics; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnLayoutChangeListener; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.FrameLayout.LayoutParams; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RemoteViews; import android.widget.TextView; import com.android.notificationstudio.action.ShareCodeAction; import com.android.notificationstudio.action.ShareMockupAction; import com.android.notificationstudio.editor.Editors; import com.android.notificationstudio.generator.NotificationGenerator; import com.android.notificationstudio.model.EditableItem; import com.android.notificationstudio.model.EditableItemConstants; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class NotificationStudioActivity extends Activity implements EditableItemConstants{ private static final String TAG = NotificationStudioActivity.class.getSimpleName(); private static final int PREVIEW_NOTIFICATION = 1; private static final int REFRESH_DELAY = 50; private static final ExecutorService BACKGROUND = Executors.newSingleThreadExecutor(); private boolean mRefreshPending; private final Handler mHandler = new Handler(); private final Runnable mRefreshNotificationInner = new Runnable() { public void run() { refreshNotificationInner(); }}; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setBackgroundDrawableResource(android.R.color.black); setContentView(R.layout.studio); initPreviewScroller(); EditableItem.initIfNecessary(this); initEditors(); } private void initPreviewScroller() { MaxHeightScrollView preview = (MaxHeightScrollView) findViewById(R.id.preview_scroller); if (preview == null) return; final int margin = ((ViewGroup.MarginLayoutParams) preview.getLayoutParams()).bottomMargin; preview.addOnLayoutChangeListener(new OnLayoutChangeListener(){ public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { // animate preview height changes if (oldBottom != bottom) { final View e = findViewById(R.id.editors); final int y = bottom + margin; e.animate() .translationY(y - oldBottom) .setListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { FrameLayout.LayoutParams lp = (LayoutParams) e.getLayoutParams(); lp.topMargin = y; e.setTranslationY(0); e.setLayoutParams(lp); } }); } }}); // limit the max height for preview, leave room for editors + soft keyboard DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); float actualHeight = dm.heightPixels / dm.ydpi; float pct = actualHeight < 3.5 ? .32f : actualHeight < 4 ? .35f : .38f; preview.setMaxHeight((int)(dm.heightPixels * pct)); } private void initEditors() { LinearLayout items = (LinearLayout) findViewById(R.id.items); items.removeAllViews(); String currentCategory = null; for (EditableItem item : EditableItem.values()) { String itemCategory = item.getCategory(this); if (!itemCategory.equals(currentCategory)) { View dividerView = getLayoutInflater().inflate(R.layout.divider, null); ((TextView) dividerView.findViewById(R.id.divider_text)).setText(itemCategory); items.addView(dividerView); currentCategory = itemCategory; } View editorView = Editors.newEditor(this, items, item); if (editorView != null) items.addView(editorView); } refreshNotification(); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { // we'll take care of restoring state } public void refreshNotification() { mRefreshPending = true; mHandler.postDelayed(mRefreshNotificationInner, REFRESH_DELAY); } private void refreshNotificationInner() { if (!mRefreshPending) { return; } final Notification notification = NotificationGenerator.build(this); ViewGroup oneU = (ViewGroup) findViewById(R.id.oneU); ViewGroup fourU = (ViewGroup) findViewById(R.id.fourU); View oneUView = refreshRemoteViews(oneU, notification.contentView); if (Build.VERSION.SDK_INT >= 16) refreshRemoteViews(fourU, notification.bigContentView); else if (Build.VERSION.SDK_INT >= 11) { ImageView largeIcon = (ImageView) findViewById(R.id.large_icon); largeIcon.setVisibility(notification.largeIcon == null ? View.GONE : View.VISIBLE); if (notification.largeIcon != null) largeIcon.setImageBitmap(notification.largeIcon); } else if (oneUView != null) { oneUView.setBackgroundColor(getResources().getColor(R.color.gb_background)); oneUView.setMinimumHeight(100); } mRefreshPending = false; // this can take a while, run on a background thread BACKGROUND.submit(new Runnable() { public void run() { NotificationManager mgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); try { mgr.notify(PREVIEW_NOTIFICATION, notification); } catch (Throwable t) { Log.w(TAG, "Error displaying notification", t); } }}); } private View refreshRemoteViews(ViewGroup parent, RemoteViews remoteViews) { parent.removeAllViews(); if (remoteViews != null) { parent.setVisibility(View.VISIBLE); try { View v = remoteViews.apply(this, parent); parent.addView(v); return v; } catch (Exception e) { TextView exceptionView = new TextView(this); exceptionView.setText(e.getClass().getSimpleName() + ": " + e.getMessage()); parent.addView(exceptionView); return exceptionView; } } else { parent.setVisibility(View.GONE); return null; } } // action bar setup @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.action_bar, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_share_code: ShareCodeAction.launch(this, item.getTitle()); return true; case R.id.action_share_mockup: ShareMockupAction.launch(this, item.getTitle()); return true; } return false; } // hides the soft keyboard more aggressively when leaving text editors @Override public boolean dispatchTouchEvent(MotionEvent event) { View v = getCurrentFocus(); boolean ret = super.dispatchTouchEvent(event); if (v instanceof EditText) { View currentFocus = getCurrentFocus(); int screenCoords[] = new int[2]; currentFocus.getLocationOnScreen(screenCoords); float x = event.getRawX() + currentFocus.getLeft() - screenCoords[0]; float y = event.getRawY() + currentFocus.getTop() - screenCoords[1]; if (event.getAction() == MotionEvent.ACTION_UP && (x < currentFocus.getLeft() || x >= currentFocus.getRight() || y < currentFocus.getTop() || y > currentFocus.getBottom())) { InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(getWindow().getCurrentFocus().getWindowToken(), 0); } } return ret; } }