NotificationStudioActivity.java revision 3e190ede751474c7e1b87b34e36354c0ef9e8af4
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
17package com.android.notificationstudio;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.app.Activity;
22import android.app.Notification;
23import android.app.NotificationManager;
24import android.content.Context;
25import android.os.Build;
26import android.os.Bundle;
27import android.os.Handler;
28import android.util.DisplayMetrics;
29import android.util.Log;
30import android.view.Menu;
31import android.view.MenuInflater;
32import android.view.MenuItem;
33import android.view.MotionEvent;
34import android.view.View;
35import android.view.View.OnLayoutChangeListener;
36import android.view.ViewGroup;
37import android.view.inputmethod.InputMethodManager;
38import android.widget.EditText;
39import android.widget.FrameLayout;
40import android.widget.FrameLayout.LayoutParams;
41import android.widget.ImageView;
42import android.widget.LinearLayout;
43import android.widget.RemoteViews;
44import android.widget.TextView;
45
46import com.android.notificationstudio.action.ShareCodeAction;
47import com.android.notificationstudio.action.ShareMockupAction;
48import com.android.notificationstudio.editor.Editors;
49import com.android.notificationstudio.generator.NotificationGenerator;
50import com.android.notificationstudio.model.EditableItem;
51import com.android.notificationstudio.model.EditableItemConstants;
52
53import java.util.concurrent.ExecutorService;
54import java.util.concurrent.Executors;
55
56public 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