1/*
2 * Copyright (C) 2010 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.gallery3d.app;
18
19import android.annotation.TargetApi;
20import android.app.Activity;
21import android.app.AlertDialog;
22import android.content.BroadcastReceiver;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.DialogInterface;
26import android.content.DialogInterface.OnCancelListener;
27import android.content.DialogInterface.OnClickListener;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.content.ServiceConnection;
31import android.content.res.Configuration;
32import android.net.Uri;
33import android.os.Bundle;
34import android.os.IBinder;
35import android.support.v4.print.PrintHelper;
36import android.view.Menu;
37import android.view.MenuItem;
38import android.view.Window;
39import android.view.WindowManager;
40
41import com.android.gallery3d.R;
42import com.android.gallery3d.common.ApiHelper;
43import com.android.gallery3d.data.DataManager;
44import com.android.gallery3d.data.MediaItem;
45import com.android.gallery3d.filtershow.cache.ImageLoader;
46import com.android.gallery3d.ui.GLRoot;
47import com.android.gallery3d.ui.GLRootView;
48import com.android.gallery3d.util.PanoramaViewHelper;
49import com.android.gallery3d.util.ThreadPool;
50import com.android.photos.data.GalleryBitmapPool;
51
52import java.io.FileNotFoundException;
53
54public class AbstractGalleryActivity extends Activity implements GalleryContext {
55    private static final String TAG = "AbstractGalleryActivity";
56    private GLRootView mGLRootView;
57    private StateManager mStateManager;
58    private GalleryActionBar mActionBar;
59    private OrientationManager mOrientationManager;
60    private TransitionStore mTransitionStore = new TransitionStore();
61    private boolean mDisableToggleStatusBar;
62    private PanoramaViewHelper mPanoramaViewHelper;
63
64    private AlertDialog mAlertDialog = null;
65    private BroadcastReceiver mMountReceiver = new BroadcastReceiver() {
66        @Override
67        public void onReceive(Context context, Intent intent) {
68            if (getExternalCacheDir() != null) onStorageReady();
69        }
70    };
71    private IntentFilter mMountFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);
72
73    @Override
74    protected void onCreate(Bundle savedInstanceState) {
75        super.onCreate(savedInstanceState);
76        mOrientationManager = new OrientationManager(this);
77        toggleStatusBarByOrientation();
78        getWindow().setBackgroundDrawable(null);
79        mPanoramaViewHelper = new PanoramaViewHelper(this);
80        mPanoramaViewHelper.onCreate();
81        doBindBatchService();
82    }
83
84    @Override
85    protected void onSaveInstanceState(Bundle outState) {
86        mGLRootView.lockRenderThread();
87        try {
88            super.onSaveInstanceState(outState);
89            getStateManager().saveState(outState);
90        } finally {
91            mGLRootView.unlockRenderThread();
92        }
93    }
94
95    @Override
96    public void onConfigurationChanged(Configuration config) {
97        super.onConfigurationChanged(config);
98        mStateManager.onConfigurationChange(config);
99        getGalleryActionBar().onConfigurationChanged();
100        invalidateOptionsMenu();
101        toggleStatusBarByOrientation();
102    }
103
104    @Override
105    public boolean onCreateOptionsMenu(Menu menu) {
106        super.onCreateOptionsMenu(menu);
107        return getStateManager().createOptionsMenu(menu);
108    }
109
110    @Override
111    public Context getAndroidContext() {
112        return this;
113    }
114
115    @Override
116    public DataManager getDataManager() {
117        return ((GalleryApp) getApplication()).getDataManager();
118    }
119
120    @Override
121    public ThreadPool getThreadPool() {
122        return ((GalleryApp) getApplication()).getThreadPool();
123    }
124
125    public synchronized StateManager getStateManager() {
126        if (mStateManager == null) {
127            mStateManager = new StateManager(this);
128        }
129        return mStateManager;
130    }
131
132    public GLRoot getGLRoot() {
133        return mGLRootView;
134    }
135
136    public OrientationManager getOrientationManager() {
137        return mOrientationManager;
138    }
139
140    @Override
141    public void setContentView(int resId) {
142        super.setContentView(resId);
143        mGLRootView = (GLRootView) findViewById(R.id.gl_root_view);
144    }
145
146    protected void onStorageReady() {
147        if (mAlertDialog != null) {
148            mAlertDialog.dismiss();
149            mAlertDialog = null;
150            unregisterReceiver(mMountReceiver);
151        }
152    }
153
154    @Override
155    protected void onStart() {
156        super.onStart();
157        if (getExternalCacheDir() == null) {
158            OnCancelListener onCancel = new OnCancelListener() {
159                @Override
160                public void onCancel(DialogInterface dialog) {
161                    finish();
162                }
163            };
164            OnClickListener onClick = new OnClickListener() {
165                @Override
166                public void onClick(DialogInterface dialog, int which) {
167                    dialog.cancel();
168                }
169            };
170            AlertDialog.Builder builder = new AlertDialog.Builder(this)
171                    .setTitle(R.string.no_external_storage_title)
172                    .setMessage(R.string.no_external_storage)
173                    .setNegativeButton(android.R.string.cancel, onClick)
174                    .setOnCancelListener(onCancel);
175            if (ApiHelper.HAS_SET_ICON_ATTRIBUTE) {
176                setAlertDialogIconAttribute(builder);
177            } else {
178                builder.setIcon(android.R.drawable.ic_dialog_alert);
179            }
180            mAlertDialog = builder.show();
181            registerReceiver(mMountReceiver, mMountFilter);
182        }
183        mPanoramaViewHelper.onStart();
184    }
185
186    @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB)
187    private static void setAlertDialogIconAttribute(
188            AlertDialog.Builder builder) {
189        builder.setIconAttribute(android.R.attr.alertDialogIcon);
190    }
191
192    @Override
193    protected void onStop() {
194        super.onStop();
195        if (mAlertDialog != null) {
196            unregisterReceiver(mMountReceiver);
197            mAlertDialog.dismiss();
198            mAlertDialog = null;
199        }
200        mPanoramaViewHelper.onStop();
201    }
202
203    @Override
204    protected void onResume() {
205        super.onResume();
206        mGLRootView.lockRenderThread();
207        try {
208            getStateManager().resume();
209            getDataManager().resume();
210        } finally {
211            mGLRootView.unlockRenderThread();
212        }
213        mGLRootView.onResume();
214        mOrientationManager.resume();
215    }
216
217    @Override
218    protected void onPause() {
219        super.onPause();
220        mOrientationManager.pause();
221        mGLRootView.onPause();
222        mGLRootView.lockRenderThread();
223        try {
224            getStateManager().pause();
225            getDataManager().pause();
226        } finally {
227            mGLRootView.unlockRenderThread();
228        }
229        GalleryBitmapPool.getInstance().clear();
230        MediaItem.getBytesBufferPool().clear();
231    }
232
233    @Override
234    protected void onDestroy() {
235        super.onDestroy();
236        mGLRootView.lockRenderThread();
237        try {
238            getStateManager().destroy();
239        } finally {
240            mGLRootView.unlockRenderThread();
241        }
242        doUnbindBatchService();
243    }
244
245    @Override
246    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
247        mGLRootView.lockRenderThread();
248        try {
249            getStateManager().notifyActivityResult(
250                    requestCode, resultCode, data);
251        } finally {
252            mGLRootView.unlockRenderThread();
253        }
254    }
255
256    @Override
257    public void onBackPressed() {
258        // send the back event to the top sub-state
259        GLRoot root = getGLRoot();
260        root.lockRenderThread();
261        try {
262            getStateManager().onBackPressed();
263        } finally {
264            root.unlockRenderThread();
265        }
266    }
267
268    public GalleryActionBar getGalleryActionBar() {
269        if (mActionBar == null) {
270            mActionBar = new GalleryActionBar(this);
271        }
272        return mActionBar;
273    }
274
275    @Override
276    public boolean onOptionsItemSelected(MenuItem item) {
277        GLRoot root = getGLRoot();
278        root.lockRenderThread();
279        try {
280            return getStateManager().itemSelected(item);
281        } finally {
282            root.unlockRenderThread();
283        }
284    }
285
286    protected void disableToggleStatusBar() {
287        mDisableToggleStatusBar = true;
288    }
289
290    // Shows status bar in portrait view, hide in landscape view
291    private void toggleStatusBarByOrientation() {
292        if (mDisableToggleStatusBar) return;
293
294        Window win = getWindow();
295        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
296            win.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
297        } else {
298            win.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
299        }
300    }
301
302    public TransitionStore getTransitionStore() {
303        return mTransitionStore;
304    }
305
306    public PanoramaViewHelper getPanoramaViewHelper() {
307        return mPanoramaViewHelper;
308    }
309
310    protected boolean isFullscreen() {
311        return (getWindow().getAttributes().flags
312                & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
313    }
314
315    private BatchService mBatchService;
316    private boolean mBatchServiceIsBound = false;
317    private ServiceConnection mBatchServiceConnection = new ServiceConnection() {
318        @Override
319        public void onServiceConnected(ComponentName className, IBinder service) {
320            mBatchService = ((BatchService.LocalBinder)service).getService();
321        }
322
323        @Override
324        public void onServiceDisconnected(ComponentName className) {
325            mBatchService = null;
326        }
327    };
328
329    private void doBindBatchService() {
330        bindService(new Intent(this, BatchService.class), mBatchServiceConnection, Context.BIND_AUTO_CREATE);
331        mBatchServiceIsBound = true;
332    }
333
334    private void doUnbindBatchService() {
335        if (mBatchServiceIsBound) {
336            // Detach our existing connection.
337            unbindService(mBatchServiceConnection);
338            mBatchServiceIsBound = false;
339        }
340    }
341
342    public ThreadPool getBatchServiceThreadPoolIfAvailable() {
343        if (mBatchServiceIsBound && mBatchService != null) {
344            return mBatchService.getThreadPool();
345        } else {
346            throw new RuntimeException("Batch service unavailable");
347        }
348    }
349
350    public void printSelectedImage(Uri uri) {
351        if (uri == null) {
352            return;
353        }
354        String path = ImageLoader.getLocalPathFromUri(this, uri);
355        if (path != null) {
356            Uri localUri = Uri.parse(path);
357            path = localUri.getLastPathSegment();
358        } else {
359            path = uri.getLastPathSegment();
360        }
361        PrintHelper printer = new PrintHelper(this);
362        try {
363            printer.printBitmap(path, uri);
364        } catch (FileNotFoundException fnfe) {
365            Log.e(TAG, "Error printing an image", fnfe);
366        }
367    }
368}
369