Home | History | Annotate | Download | only in notificationstudio
      1 /*
      2  * Copyright 2012 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 
     17 package com.android.notificationstudio;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.app.Activity;
     22 import android.app.Notification;
     23 import android.app.NotificationManager;
     24 import android.content.Context;
     25 import android.os.Build;
     26 import android.os.Bundle;
     27 import android.os.Handler;
     28 import android.util.DisplayMetrics;
     29 import android.util.Log;
     30 import android.view.Menu;
     31 import android.view.MenuInflater;
     32 import android.view.MenuItem;
     33 import android.view.MotionEvent;
     34 import android.view.View;
     35 import android.view.View.OnLayoutChangeListener;
     36 import android.view.ViewGroup;
     37 import android.view.inputmethod.InputMethodManager;
     38 import android.widget.EditText;
     39 import android.widget.FrameLayout;
     40 import android.widget.FrameLayout.LayoutParams;
     41 import android.widget.ImageView;
     42 import android.widget.LinearLayout;
     43 import android.widget.RemoteViews;
     44 import android.widget.TextView;
     45 
     46 import com.android.notificationstudio.action.ShareCodeAction;
     47 import com.android.notificationstudio.action.ShareMockupAction;
     48 import com.android.notificationstudio.editor.Editors;
     49 import com.android.notificationstudio.generator.NotificationGenerator;
     50 import com.android.notificationstudio.model.EditableItem;
     51 import com.android.notificationstudio.model.EditableItemConstants;
     52 
     53 import java.util.concurrent.ExecutorService;
     54 import java.util.concurrent.Executors;
     55 
     56 public class NotificationStudioActivity extends Activity implements EditableItemConstants{
     57     private static final String TAG = NotificationStudioActivity.class.getSimpleName();
     58     private static final int PREVIEW_NOTIFICATION = 1;
     59     private static final int REFRESH_DELAY = 50;
     60     private static final ExecutorService BACKGROUND = Executors.newSingleThreadExecutor();
     61 
     62     private boolean mRefreshPending;
     63 
     64     private final Handler mHandler = new Handler();
     65     private final Runnable mRefreshNotificationInner = new Runnable() {
     66         public void run() {
     67             refreshNotificationInner();
     68         }};
     69 
     70     @Override
     71     public void onCreate(Bundle savedInstanceState) {
     72         super.onCreate(savedInstanceState);
     73 
     74         getWindow().setBackgroundDrawableResource(android.R.color.black);
     75         setContentView(R.layout.studio);
     76         initPreviewScroller();
     77 
     78         EditableItem.initIfNecessary(this);
     79 
     80         initEditors();
     81     }
     82 
     83     private void initPreviewScroller() {
     84 
     85         MaxHeightScrollView preview = (MaxHeightScrollView) findViewById(R.id.preview_scroller);
     86         if (preview == null)
     87             return;
     88         final int margin = ((ViewGroup.MarginLayoutParams) preview.getLayoutParams()).bottomMargin;
     89         preview.addOnLayoutChangeListener(new OnLayoutChangeListener(){
     90             public void onLayoutChange(View v, int left, int top, int right, int bottom,
     91                     int oldLeft, int oldTop, int oldRight, int oldBottom) {
     92                 // animate preview height changes
     93                 if (oldBottom != bottom) {
     94                     final View e = findViewById(R.id.editors);
     95                     final int y = bottom + margin;
     96                     e.animate()
     97                         .translationY(y - oldBottom)
     98                         .setListener(new AnimatorListenerAdapter() {
     99                             public void onAnimationEnd(Animator animation) {
    100                                 FrameLayout.LayoutParams lp = (LayoutParams) e.getLayoutParams();
    101                                 lp.topMargin = y;
    102                                 e.setTranslationY(0);
    103                                 e.setLayoutParams(lp);
    104                             }
    105                         });
    106                 }
    107             }});
    108 
    109         // limit the max height for preview, leave room for editors + soft keyboard
    110         DisplayMetrics dm = new DisplayMetrics();
    111         getWindowManager().getDefaultDisplay().getMetrics(dm);
    112         float actualHeight = dm.heightPixels / dm.ydpi;
    113         float pct = actualHeight < 3.5 ? .32f :
    114                     actualHeight < 4 ? .35f :
    115                     .38f;
    116         preview.setMaxHeight((int)(dm.heightPixels * pct));
    117     }
    118 
    119     private void initEditors() {
    120         LinearLayout items = (LinearLayout) findViewById(R.id.items);
    121         items.removeAllViews();
    122         String currentCategory = null;
    123         for (EditableItem item : EditableItem.values()) {
    124             String itemCategory = item.getCategory(this);
    125             if (!itemCategory.equals(currentCategory)) {
    126                 View dividerView = getLayoutInflater().inflate(R.layout.divider, null);
    127                 ((TextView) dividerView.findViewById(R.id.divider_text)).setText(itemCategory);
    128                 items.addView(dividerView);
    129                 currentCategory = itemCategory;
    130             }
    131             View editorView = Editors.newEditor(this, items, item);
    132             if (editorView != null)
    133                 items.addView(editorView);
    134         }
    135         refreshNotification();
    136     }
    137 
    138     @Override
    139     protected void onRestoreInstanceState(Bundle savedInstanceState) {
    140        // we'll take care of restoring state
    141     }
    142 
    143     public void refreshNotification() {
    144         mRefreshPending = true;
    145         mHandler.postDelayed(mRefreshNotificationInner, REFRESH_DELAY);
    146     }
    147 
    148     private void refreshNotificationInner() {
    149         if (!mRefreshPending) {
    150             return;
    151         }
    152         final Notification notification = NotificationGenerator.build(this);
    153         ViewGroup oneU = (ViewGroup) findViewById(R.id.oneU);
    154         ViewGroup fourU = (ViewGroup) findViewById(R.id.fourU);
    155         View oneUView = refreshRemoteViews(oneU, notification.contentView);
    156         if (Build.VERSION.SDK_INT >= 16)
    157             refreshRemoteViews(fourU, notification.bigContentView);
    158         else if (Build.VERSION.SDK_INT >= 11) {
    159             ImageView largeIcon = (ImageView) findViewById(R.id.large_icon);
    160             largeIcon.setVisibility(notification.largeIcon == null ? View.GONE : View.VISIBLE);
    161             if (notification.largeIcon != null)
    162                 largeIcon.setImageBitmap(notification.largeIcon);
    163         } else if (oneUView != null) {
    164             oneUView.setBackgroundColor(getResources().getColor(R.color.gb_background));
    165             oneUView.setMinimumHeight(100);
    166         }
    167         mRefreshPending = false;
    168 
    169         // this can take a while, run on a background thread
    170         BACKGROUND.submit(new Runnable() {
    171             public void run() {
    172                 NotificationManager mgr =
    173                         (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    174                 try {
    175                     mgr.notify(PREVIEW_NOTIFICATION, notification);
    176                 } catch (Throwable t) {
    177                     Log.w(TAG, "Error displaying notification", t);
    178                 }
    179             }});
    180     }
    181 
    182     private View refreshRemoteViews(ViewGroup parent, RemoteViews remoteViews) {
    183         parent.removeAllViews();
    184         if (remoteViews != null) {
    185             parent.setVisibility(View.VISIBLE);
    186             try {
    187                 View v = remoteViews.apply(this, parent);
    188                 parent.addView(v);
    189                 return v;
    190             } catch (Exception e) {
    191                 TextView exceptionView = new TextView(this);
    192                 exceptionView.setText(e.getClass().getSimpleName() + ": " + e.getMessage());
    193                 parent.addView(exceptionView);
    194                 return exceptionView;
    195             }
    196         } else {
    197             parent.setVisibility(View.GONE);
    198             return null;
    199         }
    200     }
    201 
    202     // action bar setup
    203     @Override
    204     public boolean onCreateOptionsMenu(Menu menu) {
    205         MenuInflater inflater = getMenuInflater();
    206         inflater.inflate(R.menu.action_bar, menu);
    207         return true;
    208     }
    209 
    210     @Override
    211     public boolean onOptionsItemSelected(MenuItem item) {
    212         switch (item.getItemId()) {
    213             case R.id.action_share_code:
    214                 ShareCodeAction.launch(this, item.getTitle());
    215                 return true;
    216             case R.id.action_share_mockup:
    217                 ShareMockupAction.launch(this, item.getTitle());
    218                 return true;
    219         }
    220         return false;
    221     }
    222 
    223     // hides the soft keyboard more aggressively when leaving text editors
    224     @Override
    225     public boolean dispatchTouchEvent(MotionEvent event) {
    226         View v = getCurrentFocus();
    227         boolean ret = super.dispatchTouchEvent(event);
    228 
    229         if (v instanceof EditText) {
    230             View currentFocus = getCurrentFocus();
    231             int screenCoords[] = new int[2];
    232             currentFocus.getLocationOnScreen(screenCoords);
    233             float x = event.getRawX() + currentFocus.getLeft() - screenCoords[0];
    234             float y = event.getRawY() + currentFocus.getTop() - screenCoords[1];
    235 
    236             if (event.getAction() == MotionEvent.ACTION_UP
    237                     && (x < currentFocus.getLeft() ||
    238                         x >= currentFocus.getRight() ||
    239                         y < currentFocus.getTop() ||
    240                         y > currentFocus.getBottom())) {
    241                 InputMethodManager imm =
    242                     (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
    243                 imm.hideSoftInputFromWindow(getWindow().getCurrentFocus().getWindowToken(), 0);
    244             }
    245         }
    246         return ret;
    247     }
    248 
    249 }
    250