Back to project page Aviary-Android-SDK.
The source code is released under:
AVIARY API TERMS OF USE Full Legal Agreement The following terms and conditions and the terms and conditions at http://www.aviary.com/terms (collectively, the ?Terms??) govern your use of any and ...
If you think the Android project Aviary-Android-SDK listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/* * AVIARY API TERMS OF USE//from w ww .j av a2 s . com * Full Legal Agreement * The following terms and conditions and the terms and conditions at * http://www.aviary.com/terms (collectively, the Terms??) govern your use * of any and all data, text, software, tools, documents and other materials * associated with the application programming interface offered by Aviary, Inc. * (the "API"). By clicking on the Accept?? button, OR BY USING OR ACCESSING * ANY PORTION OF THE API, you or the entity or company that you represent are * unconditionally agreeing to be bound by the terms, including those available * by hyperlink from within this document, and are becoming a party to the * Terms. Your continued use of the API shall also constitute assent to the * Terms. If you do not unconditionally agree to all of the Terms, DO NOT USE OR * ACCESS ANY PORTION OF THE API. If the terms set out herein are considered an * offer, acceptance is expressly limited to these terms. IF YOU DO NOT AGREE TO * THE TERMS, YOU MAY NOT USE THE API, IN WHOLE OR IN PART. * * Human-Friendly Summary * If you use the API, you automatically agree to these Terms of Service. Don't * use our API if you don't agree with this document! * 1. GRANT OF LICENSE. * Subject to your ("Licensee") full compliance with all of Terms of this * agreement ("Agreement"), Aviary, Inc. ("Aviary") grants Licensee a * non-exclusive, revocable, nonsublicensable, nontransferable license to * download and use the API solely to embed a launchable Aviary application * within Licensees mobile or website application (App??) and to access * data from Aviary in connection with such application. Licensee may not * install or use the API for any other purpose without Aviary's prior written * consent. * * Licensee shall not use the API in connection with or to promote any products, * services, or materials that constitute, promote or are used primarily for the * purpose of dealing in: spyware, adware, or other malicious programs or code, * counterfeit goods, items subject to U.S. embargo, unsolicited mass * distribution of email ("spam"), multi-level marketing proposals, hate * materials, hacking/surveillance/interception/descrambling equipment, * libelous, defamatory, obscene, pornographic, abusive or otherwise offensive * content, prostitution, body parts and bodily fluids, stolen products and * items used for theft, fireworks, explosives, and hazardous materials, * government IDs, police items, gambling, professional services regulated by * state licensing regimes, non-transferable items such as airline tickets or * event tickets, weapons and accessories. * * Use Aviary the way it's intended (as a photo-enhancement service), or get our * permission first. * If your service does anything illegal or potentially offensive, we don't want * Aviary to be in your app. Nothing personal - it just reflects badly on our * brand. * 2. BRANDING. * Licensee agrees to the following: (a) on every App page that makes use of the * Aviary API, Licensee shall display an Aviary logo crediting Aviary only in * accordance with the branding instructions available at * [www.aviary.com/branding]; (b) it shall maintain a clickable link to the * following location [www.aviary.com/api-info], prominently in the licensee App * whenever the API is displayed to the end user. (c) it may not otherwise use * the Aviary logo without specific written permission from Aviary; and (d) any * use of the Aviary logo on an App page shall be less prominent than the logo * or mark that primarily describes the Licensee website, and Licensees use * of the Aviary logo shall not imply any endorsement of the Licensee website by * Aviary. * * (a) Don't remove or obscure the Aviary logo in the editor. * (b) Don't remove or obscure the link to Aviary's mobile info. We link the * Aviary logo to this info so you don't need to add anything extra to your app. * (c) We're probably cool with you using our logo as part of a press release * announcing our editor or otherwise promoting your use of Aviary. Just please * ask us first. :) * (d) Please make sure that your users aren't confused as to who made your app * by always keeping your logo more prominent than ours. You did most of the * hard work for your app and should get all of the credit. :) * 3. PROPRIETARY RIGHTS. * As between Aviary and Licensee, the API and all intellectual property rights * in and to the API are and shall at all times remain the sole and exclusive * property of Aviary and are protected by applicable intellectual property laws * and treaties. Except for the limited license expressly granted herein, no * other license is granted, no other use is permitted and Aviary (and its * licensors) shall retain all right, title and interest in and to the API and * the Aviary logos. * * Aviary owns all of the rights in the API it is allowing you to use. Our * allowance of you to use it, does not mean we are transferring ownership to * you. * 4. OTHER RESTRICTIONS. * Except as expressly and unambiguously authorized under this Agreement, * Licensee may not (i) copy, rent, lease, sell, transfer, assign, sublicense, * disassemble, reverse engineer or decompile (except to the limited extent * expressly authorized by applicable statutory law), modify or alter any part * of the API; (ii) otherwise use the API on behalf of any third party. * * (i) Please don't break our API down and redistribute it without our consent. * (ii) Please don't agree to use our API if you are not the party using it. If * you are a developer building the API into a third party app, please have your * client review these terms, as they have to agree to them before they can use * the API. * 5. MODIFICATIONS TO THIS AGREEMENT. * Aviary reserves the right, in its sole discretion to modify this Agreement at * any time by posting a notice to Aviary.com. You shall be responsible for * reviewing and becoming familiar with any such modification. Such * modifications are effective upon first posting or notification and use of the * Aviary API by Licensee following any such notification constitutes * Licensees acceptance of the terms and conditions of this Agreement as * modified. * * We may update this agreement from time to time, as needed. We don't * anticipate any major changes, just tweaks to the legalese to reflect any new * feature updates or material changes to how the API is offered. While we will * make a good faith effort to notify everyone when these terms update with * posts on our blog, etc... it's your responsibility to keep up-to-date with * these terms on a regular basis. We'll post the last-update date at the bottom * of the agreement to make it easier to know if the terms have changed. * 6. WARRANTY DISCLAIMER. * THE API IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. EXCEPT TO THE * EXTENT REQUIRED BY APPLICABLE LAW, AVIARY AND ITS VENDORS EACH DISCLAIM ALL * WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, REGARDING THE API, * INCLUDING WITHOUT LIMITATION ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, ACCURACY, RESULTS OF USE, RELIABILITY, FITNESS FOR A * PARTICULAR PURPOSE, TITLE, INTERFERENCE WITH QUIET ENJOYMENT, AND * NON-INFRINGEMENT OF THIRD-PARTY RIGHTS. FURTHER, AVIARY DISCLAIMS ANY * WARRANTY THAT LICENSEE'S USE OF THE API WILL BE UNINTERRUPTED OR ERROR FREE. * * Things might break. Hopefully not and if so, we'll do our best to fix it * immediately. But if it happens, please note that we aren't responsible. You * are using the API "as is" and understand the risk inherent in that. * 7. SUPPORT AND UPGRADES. * This Agreement does not entitle Licensee to any support and/or upgrades for * the APIs, unless Licensee makes separate arrangements with Aviary and pays * all fees associated with such support. Any such support and/or upgrades * provided by Aviary shall be subject to the terms of this Agreement as * modified by the associated support Agreement. * * We can't promise to offer any kind of support or future upgrades. We plan to * help all of our partners to the best of our ability, but use of our API * doesn't entitle you to this. * 8. LIABILITY LIMITATION. * REGARDLESS OF WHETHER ANY REMEDY SET FORTH HEREIN FAILS OF ITS ESSENTIAL * PURPOSE OR OTHERWISE, AND EXCEPT FOR BODILY INJURY, IN NO EVENT WILL AVIARY * OR ITS VENDORS, BE LIABLE TO LICENSEE OR TO ANY THIRD PARTY UNDER ANY TORT, * CONTRACT, NEGLIGENCE, STRICT LIABILITY OR OTHER LEGAL OR EQUITABLE THEORY FOR * ANY LOST PROFITS, LOST OR CORRUPTED DATA, COMPUTER FAILURE OR MALFUNCTION, * INTERRUPTION OF BUSINESS, OR OTHER SPECIAL, INDIRECT, INCIDENTAL OR * CONSEQUENTIAL DAMAGES OF ANY KIND ARISING OUT OF THE USE OR INABILITY TO USE * THE API, EVEN IF AVIARY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSS OR * DAMAGES AND WHETHER OR NOT SUCH LOSS OR DAMAGES ARE FORESEEABLE. ANY CLAIM * ARISING OUT OF OR RELATING TO THIS AGREEMENT MUST BE BROUGHT WITHIN ONE (1) * YEAR AFTER THE OCCURRENCE OF THE EVENT GIVING RISE TO SUCH CLAIM. IN * ADDITION, AVIARY DISCLAIMS ALL LIABILITY OF ANY KIND OF AVIARY'S VENDORS. * * Want to sue us anyway? That's cool (really not), but you only have a year to * do it. Better act quick, Perry Mason. * 9. INDEMNITY. * Licensee agrees that Aviary shall have no liability whatsoever for any use * Licensee makes of the API. Licensee shall indemnify and hold harmless Aviary * from any and all claims, damages, liabilities, costs and fees (including * reasonable attorneys' fees) arising from Licensee's use of the API. * * You acknowledge that we aren't responsible at all, for anything that happens * resulting from your use of our API. * 10. TERM AND TERMINATION. * This Agreement shall continue until terminated as set forth in this Section. * Either party may terminate this Agreement at any time, for any reason, or for * no reason including, but not limited to, if Licensee violates any provision * of this Agreement. Any termination of this Agreement shall also terminate the * license granted hereunder. Upon termination of this Agreement for any reason, * Licensee shall destroy and remove from all computers, hard drives, networks, * and other storage media all copies of the API, and shall so certify to Aviary * that such actions have occurred. Aviary shall have the right to inspect and * audit Licensee's facilities to confirm the foregoing. Sections 3 through 11 * and all accrued rights to payment shall survive termination of this * Agreement. * * We can revoke your license (and you can choose to end your license) at any * time, for any reason. We won't take this lightly, but we do hold onto this * right should it be needed. * If either party terminates this license, you will need to remove the Aviary * API from your code entirely. We'll have the right to double-check to make * sure you have. * Terminating the agreement ends your ability to use our App. It doesn't change * some of the other sections (namely 3-11). * 11. GOVERNMENT USE. * If Licensee is part of an agency, department, or other entity of the United * States Government ("Government"), the use, duplication, reproduction, * release, modification, disclosure or transfer of the API are restricted in * accordance with the Federal Acquisition Regulations as applied to civilian * agencies and the Defense Federal Acquisition Regulation Supplement as applied * to military agencies. The API are a "commercial item," * "commercial computer software" and * "commercial computer software documentation." In accordance with such * provisions, any use of the API by the Government shall be governed solely by * the terms of this Agreement. * * Work for the government? Here is some special legalese just for you. * 12. EXPORT CONTROLS. * Licensee shall comply with all export laws and restrictions and regulations * of the Department of Commerce, the United States Department of Treasury * Office of Foreign Assets Control ("OFAC"), or other United States or foreign * agency or authority, and Licensee shall not export, or allow the export or * re-export of the API in violation of any such restrictions, laws or * regulations. By downloading or using the API, Licensee agrees to the * foregoing and represents and warrants that Licensee is not located in, under * the control of, or a national or resident of any restricted country. * * To any potential partner located in a country with whom it is illegal for the * USA to do business: We're genuinely sorry our governments are being jerks to * each other and look forward to the day when it isn't illegal for us to do * business together. * 13. MISCELLANEOUS. * Unless the parties have entered into a written amendment to this agreement * that is signed by both parties regarding the Aviary API, this Agreement * constitutes the entire agreement between Licensee and Aviary pertaining to * the subject matter hereof, and supersedes any and all written or oral * agreements with respect to such subject matter. This Agreement, and any * disputes arising from or relating to the interpretation thereof, shall be * governed by and construed under New York law as such law applies to * agreements between New York residents entered into and to be performed within * New York by two residents thereof and without reference to its conflict of * laws principles or the United Nations Conventions for the International Sale * of Goods. Except to the extent otherwise determined by Aviary, any action or * proceeding arising from or relating to this Agreement must be brought in a * federal court in the Southern District of New York or in state court in New * York County, New York, and each party irrevocably submits to the jurisdiction * and venue of any such court in any such action or proceeding. The prevailing * party in any action arising out of this Agreement shall be entitled to an * award of its costs and attorneys' fees. This Agreement may be amended only by * a writing executed by Aviary. If any provision of this Agreement is held to * be unenforceable for any reason, such provision shall be reformed only to the * extent necessary to make it enforceable. The failure of Aviary to act with * respect to a breach of this Agreement by Licensee or others does not * constitute a waiver and shall not limit Aviary's rights with respect to such * breach or any subsequent breaches. This Agreement is personal to Licensee and * may not be assigned or transferred for any reason whatsoever (including, * without limitation, by operation of law, merger, reorganization, or as a * result of an acquisition or change of control involving Licensee) without * Aviary's prior written consent and any action or conduct in violation of the * foregoing shall be void and without effect. Aviary expressly reserves the * right to assign this Agreement and to delegate any of its obligations * hereunder. * * This is the entire and only material agreement on this matter between our * companies (unless we have another one, signed by both of us). * Any disputes on this agreement will be governed by NY law and NY courts. * While you are in town suing us, please do make sure to stop in a real NY deli * and get some pastrami and rye. It's delicious! * More discussion of where the court will be located. Like boyscouts, our motto * is "Always Be Prepared" and courts and wedding halls book up early this time * of year. * You sue us and we win, you're buying our attorneys a new Mercedes. * Even if you use white-out on your screen to erase some of this agreement, it * doesn't matter. Only Aviary can put the white-out on the screen. * If some of this agreement isn't legally valid, whatever remains if it will * hold strong. * If we don't respond quickly to your breaching this agreement, it doesn't mean * we can't do so in the future. * This agreement will always be between Aviary, Inc and you. You can't transfer * this agreement. If someone buys your product or company and plans to continue * using it, they will need to agree to these terms separately. * In the event that Aviary, Inc is sold or the API changes ownership, Aviary * will be able to transfer the API to a new owner without impacting our * agreement. * If some of this agreement isn't legally valid, whatever remains if it will * hold strong. * Last Updated September 15, 2011 * * It's a sunny, brisk day in NYC. We hope you're having a good day whenever you * read and agree to this! Please do drop us an email with any further questions * about this agreement to api@aviary.com and either way please do let us know * how you plan to use our API so we can promote you! Cheers, Avi */ package com.aviary.android.feather; import it.sephiroth.android.library.imagezoom.ImageViewTouch; import it.sephiroth.android.library.imagezoom.ImageViewTouchBase.DisplayType; import it.sephiroth.android.library.imagezoom.graphics.IBitmapDrawable; import it.sephiroth.android.library.media.ExifInterfaceExtended; import it.sephiroth.android.library.widget.AdapterView; import it.sephiroth.android.library.widget.AdapterView.OnItemClickListener; import it.sephiroth.android.library.widget.HListView; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.Matrix; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.provider.MediaStore.Images.Media; import android.view.LayoutInflater; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.Window; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.AnimationUtils; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.aviary.android.feather.AviaryMainController.FeatherContext; import com.aviary.android.feather.AviaryMainController.OnBitmapChangeListener; import com.aviary.android.feather.AviaryMainController.OnToolListener; import com.aviary.android.feather.async_tasks.DownloadImageAsyncTask; import com.aviary.android.feather.async_tasks.DownloadImageAsyncTask.OnImageDownloadListener; import com.aviary.android.feather.async_tasks.ExifTask; import com.aviary.android.feather.cds.AviaryCds; import com.aviary.android.feather.cds.CdsUtils; import com.aviary.android.feather.common.log.LoggerFactory; import com.aviary.android.feather.common.log.LoggerFactory.Logger; import com.aviary.android.feather.common.log.LoggerFactory.LoggerType; import com.aviary.android.feather.common.threading.Future; import com.aviary.android.feather.common.threading.FutureListener; import com.aviary.android.feather.common.utils.DateTimeUtils; import com.aviary.android.feather.common.utils.IOUtils; import com.aviary.android.feather.common.utils.PackageManagerUtils; import com.aviary.android.feather.common.utils.ReflectionUtils; import com.aviary.android.feather.common.utils.SDKUtils; import com.aviary.android.feather.common.utils.ScreenUtils; import com.aviary.android.feather.common.utils.SystemUtils; import com.aviary.android.feather.effects.AbstractPanel.ContentPanel; import com.aviary.android.feather.effects.AbstractPanelLoaderService; import com.aviary.android.feather.headless.AviaryInitializationException; import com.aviary.android.feather.headless.filters.NativeFilterProxy; import com.aviary.android.feather.library.Constants; import com.aviary.android.feather.library.MonitoredActivity; import com.aviary.android.feather.library.content.ToolEntry; import com.aviary.android.feather.library.filters.FilterLoaderFactory; import com.aviary.android.feather.library.services.LocalDataService; import com.aviary.android.feather.library.services.ThreadPoolService; import com.aviary.android.feather.library.services.drag.DragLayer; import com.aviary.android.feather.library.tracking.Tracker; import com.aviary.android.feather.library.utils.BitmapUtils; import com.aviary.android.feather.library.utils.ImageSizes; import com.aviary.android.feather.library.utils.UIConfiguration; import com.aviary.android.feather.utils.ThreadUtils; import com.aviary.android.feather.widget.AviaryBottomBarViewFlipper; import com.aviary.android.feather.widget.AviaryBottomBarViewFlipper.OnBottomBarItemClickListener; import com.aviary.android.feather.widget.AviaryImageRestoreSwitcher; import com.aviary.android.feather.widget.AviaryImageRestoreSwitcher.OnRestoreStateListener; import com.aviary.android.feather.widget.AviaryImageRestoreSwitcher.RestoreState; import com.aviary.android.feather.widget.AviaryNavBarViewFlipper; import com.aviary.android.feather.widget.AviaryNavBarViewFlipper.OnToolbarClickListener; import com.aviary.android.feather.widget.AviaryToast; /** * FeatherActivity is the main activity controller. * * @author alessandro */ public class FeatherActivity extends MonitoredActivity implements OnToolbarClickListener, OnImageDownloadListener, OnToolListener, FeatherContext, OnBitmapChangeListener, OnBottomBarItemClickListener, OnRestoreStateListener, OnItemClickListener { private static final int ALERT_CONFIRM_EXIT = 0; private static final int ALERT_DOWNLOAD_ERROR = 1; private static final int ALERT_REVERT_IMAGE = 2; private static final int ALERT_FEEDBACK = 3; private static final int ALERT_ABOUT = 4; private static final int ALERT_CONFIRM_EXIT_WITH_NO_CHANGES = 5; static { logger = LoggerFactory.getLogger( FeatherActivity.class.getSimpleName(), LoggerType.ConsoleLoggerType ); } /** SHA-1 version id. */ public static final String ID = "$Id: d6e8b15a32d57de787894ab437cee84d35ac40ff $"; /** delay between click and panel opening */ private static final int TOOLS_OPEN_DELAY_TIME = 50; private int pResultCode = RESULT_CANCELED; /** The main toolbar view. */ private AviaryNavBarViewFlipper mToolbar; /** the main tools list view */ private HListView mToolsList; /** The custom Toast used for modal loaders */ private AviaryToast mToastLoader; /** * The main drawing view container for tools implementing {@link ContentPanel}. */ private ViewGroup mDrawingViewContainer; /** inline progress loader. */ private View mInlineProgressLoader; /** The main controller. */ protected AviaryMainController mMainController; /** tool list to show. */ protected List<String> mToolList; /** saving variable. */ protected boolean mSaving; /** The current screen orientation. */ private int mOrientation; /** The bottom bar view. */ private AviaryBottomBarViewFlipper mBottomBarFlipper; /** default logger. */ protected static Logger logger; /** default handler. */ protected final Handler mHandler = new Handler(); /** hide exit alert confirmation. */ protected boolean mHideExitAlertConfirmation = false; /** The list of tools entries. */ private List<ToolEntry> mListEntries; /** the popup container */ private ViewGroup mPopupContainer; private DragLayer mDragLayer; /** Main image downloader task **/ private DownloadImageAsyncTask mDownloadTask; /** The current Activity is visible and active */ private boolean mIsRunning; private Handler mUIHandler = new Handler( new Handler.Callback() { @Override public boolean handleMessage( Message msg ) { onStateChanged( msg.what, msg.arg1, msg.obj ); return true; } } ); protected void onStateChanged( int new_state, int arg1, Object obj ) { logger.info( "onStateChanged: " + new_state ); switch ( new_state ) { case AviaryMainController.STATE_OPENING: mToolbar.setClickable( false ); break; case AviaryMainController.STATE_OPENED: mToolbar.setClickable( true ); break; case AviaryMainController.STATE_CLOSING: mToolbar.setClickable( false ); mImageRestore.setVisibility( View.VISIBLE ); break; case AviaryMainController.STATE_CLOSED: mToolsList.setEnabled( true ); mToolbar.setClickable( true ); mToolbar.close(); mToolbar.setSaveEnabled( true ); mToolsList.requestFocus(); break; case AviaryMainController.STATE_DISABLED: mToolsList.setEnabled( false ); mToolbar.setClickable( false ); mToolbar.setSaveEnabled( false ); break; case AviaryMainController.STATE_CONTENT_READY: mImageRestore.setVisibility( View.GONE ); break; case AviaryMainController.STATE_READY: mToolbar.setTitle( mMainController.getActiveTool().labelResourceId, false ); mToolbar.open(); // once a tool panel has been opened, reset the main image view display // TODO: this is not correct getMainImage().resetMatrix(); mImageRestore.clearStatus(); break; case AviaryMainController.TOOLBAR_TITLE: mToolbar.setTitle( (CharSequence) obj ); break; case AviaryMainController.TOOLBAR_TITLE_INT: mToolbar.setTitle( arg1 ); break; case AviaryMainController.TOOLBAR_APPLY_VISIBILITY: mToolbar.setApplyVisible( arg1 == 0 ? false : true ); break; } } private AviaryImageRestoreSwitcher mImageRestore; /** * Override the internal setResult in order to register the internal close * status. * * @param resultCode * the result code * @param data * the data */ protected final void onSetResult( int resultCode, Intent data ) { pResultCode = resultCode; if ( null != data && null != mMainController ) { LocalDataService service = mMainController.getService( LocalDataService.class ); // copy the relevant extras from the original intent if ( service.getIntentContainsKey( Constants.EXTRA_OUTPUT_HIRES_SESSION_ID ) ) { data.putExtra( Constants.EXTRA_OUTPUT_HIRES_SESSION_ID, service.getIntentValue( Constants.EXTRA_OUTPUT_HIRES_SESSION_ID, "" ) ); } } setResult( resultCode, data ); } @Override public void onCreate( Bundle savedInstanceState ) { long t1 = DateTimeUtils.tick(); onPreCreate(); super.onCreate( savedInstanceState ); // if the device is not considered a tablet // let's disable the landascape orientation if ( !ScreenUtils.isTablet( this ) ) { setRequestedOrientation( ActivityInfo.SCREEN_ORIENTATION_PORTRAIT ); } setContentView( R.layout.aviary_main_view ); // Create the main Controller initializeController(); onInitializeUtils(); onInitializeUI(); // initiate the filter manager mMainController.setOnToolListener( this ); mMainController.setOnBitmapChangeListener( this ); mMainController.setDragLayer( mDragLayer ); // first check the validity of the incoming intent Uri srcUri = handleIntent( getIntent() ); if ( srcUri == null ) { onSetResult( RESULT_CANCELED, null ); finish(); return; } // download the requested image loadImage( srcUri ); // initialize filters delayedInitializeTools(); // calling post-create onPostCreate(); logger.error( "MAX MEMORY", SystemUtils.getApplicationTotalMemory() ); Tracker.recordTag( "feather: opened" ); DateTimeUtils.tick( t1, "onCreate finished" ); } private void initializeController() { mMainController = new AviaryMainController( this, mUIHandler ); onControllerLoaded( mMainController ); } protected void onControllerLoaded( AviaryMainController controller ) {} protected void onPostCreate() {} protected void onPreCreate() {} protected void onInitializeUtils() { try { NativeFilterProxy.init( this ); } catch ( AviaryInitializationException e ) { e.printStackTrace(); Toast.makeText( getApplicationContext(), "Sorry an error occurred: " + e.getMessage(), Toast.LENGTH_LONG ).show(); finish(); } } @Override protected void onSaveInstanceState( Bundle outState ) { logger.info( "onSaveInstanceState" ); super.onSaveInstanceState( outState ); } @Override protected void onRestoreInstanceState( Bundle savedInstanceState ) { logger.info( "onRestoreInstanceState: " + savedInstanceState ); super.onRestoreInstanceState( savedInstanceState ); } @Override protected void onDestroy() { logger.info( "onDestroy" ); if ( pResultCode != RESULT_OK ) Tracker.recordTag( "feather: cancelled" ); super.onDestroy(); mToolbar.setOnToolbarClickListener( null ); mBottomBarFlipper.setOnBottomBarItemClickListener( null ); mMainController.setOnBitmapChangeListener( null ); mMainController.setOnToolListener( null ); if ( null != mDownloadTask ) { mDownloadTask.setOnLoadListener( null ); mDownloadTask = null; } if ( mMainController != null ) { mMainController.dispose(); } mUIHandler = null; mMainController = null; } /** * Initialize varius UI elements. */ private void onInitializeUI() { // register the toolbar listeners mToolbar.setOnToolbarClickListener( this ); // image view mImageRestore.getDefaultImageView().setDoubleTapEnabled( false ); mImageRestore.getDefaultImageView().setDisplayType( DisplayType.FIT_IF_BIGGER ); mImageRestore.getRestoredImageView().setDisplayType( DisplayType.FIT_IF_BIGGER ); // initialize tools UI // mToolsList.setOverScrollMode( HListView.OVER_SCROLL_ALWAYS ); mToolsList.setAdapter( null ); mBottomBarFlipper.setOnBottomBarItemClickListener( this ); LocalDataService service = mMainController.getService( LocalDataService.class ); if ( service.getIntentContainsKey( Constants.EXTRA_WHITELABEL ) ) { mBottomBarFlipper.toggleLogoVisibility( false ); } } @Override protected Dialog onCreateDialog( int id ) { Dialog dialog = null; switch ( id ) { case ALERT_CONFIRM_EXIT: dialog = new AlertDialog.Builder( this ).setTitle( R.string.feather_confirm ).setMessage( R.string.confirm_quit_message ) .setPositiveButton( R.string.feather_yes_leave, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which ) { dialog.dismiss(); onBackPressed( true ); } } ).setNegativeButton( R.string.feather_keep_editing, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which ) { dialog.dismiss(); } } ).create(); break; case ALERT_DOWNLOAD_ERROR: dialog = new AlertDialog.Builder( this ).setTitle( R.string.feather_attention ).setMessage( R.string.feather_error_download_image_message ) .create(); break; case ALERT_REVERT_IMAGE: dialog = new AlertDialog.Builder( this ).setTitle( R.string.feather_revert_dialog_title ).setMessage( R.string.feather_revert_dialog_message ) .setPositiveButton( android.R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which ) { dialog.dismiss(); onRevert(); } } ).setNegativeButton( android.R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which ) { dialog.dismiss(); } } ).create(); break; case ALERT_FEEDBACK: dialog = createFeedbackDialog(); break; case ALERT_ABOUT: dialog = createAboutDialog(); break; case ALERT_CONFIRM_EXIT_WITH_NO_CHANGES: dialog = new AlertDialog.Builder( this ).setTitle( R.string.feather_confirm ).setMessage( R.string.feather_unsaved_from_camera ) .setPositiveButton( R.string.feather_yes_leave, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which ) { dialog.dismiss(); onBackPressed( true ); } } ).setNegativeButton( R.string.feather_keep_editing, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which ) { dialog.dismiss(); } } ).create(); break; } return dialog; } protected Dialog createBaseDialog( int message, String buttonText, final OnClickListener button1Listener ) { final Dialog dialog = new Dialog( FeatherActivity.this, R.style.AviaryTheme_Dialog_Custom ); dialog.requestWindowFeature( Window.FEATURE_NO_TITLE ); dialog.setContentView( R.layout.aviary_feedback_dialog_view ); dialog.setCanceledOnTouchOutside( true ); Window dialogWindow = dialog.getWindow(); TextView textVersion = (TextView) dialogWindow.findViewById( R.id.aviary_version ); TextView textMessage = (TextView) dialogWindow.findViewById( R.id.aviary_text ); Button button1 = (Button) dialogWindow.findViewById( R.id.aviary_button1 ); Button button2 = (Button) dialogWindow.findViewById( R.id.aviary_button2 ); textVersion.setText( getString( R.string.feather_version ) + " " + SDKUtils.SDK_VERSION ); textMessage.setText( message ); button1.setText( buttonText ); button1.setOnClickListener( new OnClickListener() { @Override public void onClick( View v ) { if ( null != button1Listener ) { button1Listener.onClick( v ); } dialog.dismiss(); } } ); button2.setOnClickListener( new OnClickListener() { @Override public void onClick( View v ) { dialog.dismiss(); } } ); return dialog; } /** * Creates the About dialog * * @return */ protected Dialog createAboutDialog() { final OnClickListener listener = new OnClickListener() { @Override public void onClick( View v ) { final Intent intent = new Intent( android.content.Intent.ACTION_VIEW ); intent.setData( Uri.parse( "http://www.aviary.com/android" ) ); FeatherActivity.this.startActivity( intent ); } }; final Dialog dialog = createBaseDialog( R.string.feather_about_dialog_message, "aviary.com/android", listener ); return dialog; } /** * Creates the Feedback dialog * * @return */ protected Dialog createFeedbackDialog() { final OnClickListener listener = new OnClickListener() { @Override public void onClick( View v ) { Intent intent = new Intent( Intent.ACTION_VIEW ); intent.setData( Uri.parse( "http://support.aviary.com/" ) ); FeatherActivity.this.startActivity( intent ); } }; final Dialog dialog = createBaseDialog( R.string.feather_feedback_dialog_message, getString( R.string.feather_send_feedback ), listener ); return dialog; } /** * Revert the original image. */ private void onRevert() { Tracker.recordTag( "feather: reset image" ); LocalDataService service = mMainController.getService( LocalDataService.class ); loadImage( service.getSourceImageUri() ); } /** * Manage the screen configuration change if the screen orientation has * changed, notify the filter manager and reload the main workspace view. * * @param newConfig * the new config */ @Override public void onConfigurationChanged( final Configuration newConfig ) { super.onConfigurationChanged( newConfig ); if ( mOrientation != newConfig.orientation ) { mOrientation = newConfig.orientation; if ( null != mMainController ) { mMainController.onConfigurationChanged( newConfig ); } } mOrientation = newConfig.orientation; } @Override public boolean onPrepareOptionsMenu( Menu menu ) { if ( mSaving ) return false; if ( mMainController.getEnabled() && mMainController.isClosed() ) { return true; } else { return false; } } @SuppressWarnings ( "deprecation" ) @Override public void onBottomBarItemClick( int id ) { if ( id == R.id.aviary_white_logo ) { showDialog( ALERT_ABOUT ); } } /** * Load an image using an async task. * @param data */ protected void loadImage( Uri data ) { if ( null != mDownloadTask ) { mDownloadTask.setOnLoadListener( null ); mDownloadTask = null; } LocalDataService dataService = mMainController.getService( LocalDataService.class ); dataService.setSourceImageUri( data ); int maxSize = dataService.getIntentValue( Constants.EXTRA_MAX_IMAGE_SIZE, 0 ); mDownloadTask = new DownloadImageAsyncTask( data, maxSize ); mDownloadTask.setOnLoadListener( this ); mDownloadTask.execute( getBaseContext() ); } @Override public void onContentChanged() { super.onContentChanged(); mDragLayer = (DragLayer) findViewById( R.id.dragLayer ); mToolbar = (AviaryNavBarViewFlipper) findViewById( R.id.aviary_navbar ); mBottomBarFlipper = (AviaryBottomBarViewFlipper) findViewById( R.id.aviary_bottombar ); mToolsList = mBottomBarFlipper.getToolsListView(); mDrawingViewContainer = (ViewGroup) findViewById( R.id.drawing_view_container ); mInlineProgressLoader = findViewById( R.id.image_loading_view ); mPopupContainer = (ViewGroup) findViewById( R.id.feather_dialogs_container ); mBottomBarFlipper.setDisplayedChild( 1 ); mImageRestore = (AviaryImageRestoreSwitcher) findViewById( R.id.aviary_restore ); } @SuppressWarnings ( "deprecation" ) @Override public void onBackPressed() { if ( mToolbar.restored() || mImageRestore.getStatus() != RestoreState.None ) { logger.log( "Restore enabled, let's close that first!" ); mImageRestore.clearStatus(); return; } if ( !mMainController.onBackPressed() ) { if ( mToastLoader != null ) mToastLoader.hide(); if ( mMainController.getBitmapIsChanged() ) { if ( mHideExitAlertConfirmation ) { super.onBackPressed(); } else { showDialog( ALERT_CONFIRM_EXIT ); } } else { if ( !handleBackPressedOnUnchangedImage() ) { super.onBackPressed(); } } } } @SuppressWarnings ( "deprecation" ) protected boolean handleBackPressedOnUnchangedImage() { final AviaryMainController controller = getMainController(); if ( null != controller ) { LocalDataService service = getMainController().getService( LocalDataService.class ); if ( null != service ) { if ( service.getIntentContainsKey( Constants.EXTRA_IN_SOURCE_TYPE ) ) { String value = service.getIntentValue( Constants.EXTRA_IN_SOURCE_TYPE, "Gallery" ); if ( "Camera".equals( value ) ) { // ok show the alert message... showDialog( ALERT_CONFIRM_EXIT_WITH_NO_CHANGES ); return true; } } } } return false; } /** * Override the default behavior of back pressed * * @param force * the super backpressed behavior */ protected void onBackPressed( boolean force ) { if ( force ) super.onBackPressed(); else onBackPressed(); } /** * Handles the original input Intent. * * @param intent * @return the uri of the image to be loaded */ protected Uri handleIntent( Intent intent ) { logger.info( "handleIntent" ); LocalDataService service = mMainController.getService( LocalDataService.class ); if ( intent != null && intent.getData() != null ) { final String action = intent.getAction(); HashMap<String, String> map = new HashMap<String, String>(); map.put( "action", null != action ? action : "null" ); Tracker.recordTag( "feather: intent", map ); Uri data = intent.getData(); if ( SystemUtils.isIceCreamSandwich() ) { if ( data.toString().startsWith( "content://com.android.gallery3d.provider" ) ) { // use the com.google provider, not the com.android provider // ( for ICS only ) data = Uri.parse( data.toString().replace( "com.android.gallery3d", "com.google.android.gallery3d" ) ); } } logger.log( "src: " + data ); Bundle extras = intent.getExtras(); if ( extras != null ) { Uri destUri = (Uri) extras.getParcelable( Constants.EXTRA_OUTPUT ); if ( destUri != null ) { logger.log( "dest: " + destUri ); service.setDestImageUri( destUri ); String outputFormatString = extras.getString( Constants.EXTRA_OUTPUT_FORMAT ); if ( outputFormatString != null ) { CompressFormat format = Bitmap.CompressFormat.valueOf( outputFormatString ); service.setOutputFormat( format ); } } if ( extras.containsKey( Constants.EXTRA_TOOLS_LIST ) ) { mToolList = Arrays.asList( extras.getStringArray( Constants.EXTRA_TOOLS_LIST ) ); } if ( extras.containsKey( Constants.EXTRA_HIDE_EXIT_UNSAVE_CONFIRMATION ) ) { mHideExitAlertConfirmation = extras.getBoolean( Constants.EXTRA_HIDE_EXIT_UNSAVE_CONFIRMATION ); } } return data; } return null; } /** * Load the current tools list in a separate thread */ private void delayedInitializeTools() { Thread t = new Thread( new Runnable() { @Override public void run() { final List<ToolEntry> listEntries = loadTools(); final List<String> permissions = CdsUtils.getPermissions( FeatherActivity.this ); mHandler.post( new Runnable() { @Override public void run() { onToolsLoaded( listEntries, permissions ); } } ); } } ); t.start(); } private List<String> loadStandaloneTools() { if ( PackageManagerUtils.isStandalone( this ) ) { logger.info( "isStandalone, loadStandaloneTools" ); // let's use a global try..catch try { // This is the preference class used in the standalone app // if the tool list is empty, let's try to use // the user defined toolset Object instance = ReflectionUtils.invokeStaticMethod( "com.aviary.android.feather.utils.SettingsUtils", "getInstance", new Class[] { Context.class }, this ); if ( null != instance ) { Object toolList = ReflectionUtils.invokeMethod( instance, "getToolList" ); if ( null != toolList && toolList instanceof String[] ) { return Arrays.asList( (String[]) toolList ); } } } catch ( Exception t ) { } } return null; } private List<ToolEntry> loadTools() { AbstractPanelLoaderService service = mMainController.getService( AbstractPanelLoaderService.class ); if ( service == null ) return null; if ( mToolList == null ) { mToolList = loadStandaloneTools(); if ( null == mToolList ) { mToolList = Arrays.asList( FilterLoaderFactory.getDefaultFilters() ); } } List<ToolEntry> listEntries = new ArrayList<ToolEntry>(); Map<String, ToolEntry> entryMap = new HashMap<String, ToolEntry>(); ToolEntry[] all_entries = service.getToolsEntries(); for ( int i = 0; i < all_entries.length; i++ ) { FilterLoaderFactory.Filters entry_name = all_entries[i].name; if ( !mToolList.contains( entry_name.name() ) ) continue; entryMap.put( entry_name.name(), all_entries[i] ); } for ( String toolName : mToolList ) { if ( !entryMap.containsKey( toolName ) ) continue; listEntries.add( entryMap.get( toolName ) ); } return listEntries; } /** * List of tools loaded, we're ready to display them * * @param listEntries */ protected void onToolsLoaded( final List<ToolEntry> listEntries, final List<String> permissions ) { logger.info( "onToolsLoaded: %s", permissions ); LocalDataService data = mMainController.getService( LocalDataService.class ); boolean white_label = permissions.contains( AviaryCds.Permission.whitelabel.name() ) && data.getIntentContainsKey( Constants.EXTRA_WHITELABEL ); mListEntries = listEntries; mToolsList.setAdapter( new ListAdapter( this, mListEntries, white_label ) ); mToolsList.setOnItemClickListener( this ); mBottomBarFlipper.toggleLogoVisibility( !white_label ); } /** * Return the current panel used to populate the active tool options. * * @return the options panel container */ @Override public ViewGroup getOptionsPanelContainer() { return mBottomBarFlipper.getContentPanel(); } @Override public AviaryBottomBarViewFlipper getBottomBar() { return mBottomBarFlipper; } /** * Return the main image view. * * @return the main image */ @Override public ImageViewTouch getMainImage() { return mImageRestore.getDefaultImageView(); } /** * Return the actual view used to populate a {@link ContentPanel}. * * @see {@link ContentPanel#getContentView(LayoutInflater)} * @return the drawing image container */ @Override public ViewGroup getDrawingImageContainer() { return mDrawingViewContainer; } @Override public ViewGroup activatePopupContainer() { mPopupContainer.setVisibility( View.VISIBLE ); return mPopupContainer; } @Override public void deactivatePopupContainer() { mPopupContainer.removeAllViews(); mPopupContainer.setVisibility( View.GONE ); } // --------------------- // Toolbar events // --------------------- @Override public void onRestoreClick() { logger.info( "onRestoreClick" ); if ( null != mMainController ) { if ( mMainController.getEnabled() ) { Tracker.recordTag( "prepost: RestoreOriginalClicked" ); mMainController.onRestoreOriginal(); mImageRestore.clearStatus(); } } } /** * User clicked on the Done button * Start the save process */ @Override public void onDoneClick() { if ( null != mMainController ) { if ( mMainController.getEnabled() ) { LocalDataService dataService = mMainController.getService( LocalDataService.class ); final boolean changed = mMainController.getBitmapIsChanged(); final boolean saveWithNoChanges = dataService.getIntentValue( Constants.EXTRA_IN_SAVE_ON_NO_CHANGES, true ); if ( LoggerFactory.LOG_ENABLED ) { logger.log( "bitmap changed: " + changed ); logger.log( "save with no changes: " + saveWithNoChanges ); } // If the Image is not modified and the calling Intent // does not allow to save unmodified images, then return // a RESULT_CANCELED value if ( !changed && !saveWithNoChanges ) { exitWithNoModifications(); return; } mMainController.onSave(); Bitmap bitmap = mMainController.getBitmap(); if ( bitmap != null ) { performSave( bitmap, changed ); } } } } /** * Exit the Activity with a RESULT_CANCELED value and an extra * value to indicate that no modifications were made to the image. */ protected void exitWithNoModifications() { logger.info( "exitWithNoModifications" ); Intent result = new Intent(); result.putExtra( Constants.EXTRA_OUT_BITMAP_CHANGED, false ); onSetResult( RESULT_CANCELED, result ); finish(); } /** * User clicked on the Apply button of a tool * Apply the current tool modifications and update the main image */ @Override public void onApplyClick() { mMainController.onApply(); } /** * load the original file EXIF data and store the result into the local data * instance */ protected void loadExif() { logger.log( "loadExif" ); final LocalDataService data = mMainController.getService( LocalDataService.class ); ThreadPoolService thread = mMainController.getService( ThreadPoolService.class ); if ( null != data && thread != null ) { final String path = data.getSourceImagePath(); FutureListener<Bundle> listener = new FutureListener<Bundle>() { @Override public void onFutureDone( Future<Bundle> future ) { try { Bundle result = future.get(); if ( null != result ) { data.setOriginalExifBundle( result ); } } catch ( Throwable e ) { e.printStackTrace(); } } }; if ( null != path ) { thread.submit( new ExifTask(), listener, path ); } else { logger.warn( "orinal file path not available" ); } } } /** * Try to compute the original file absolute path */ protected void computeOriginalFilePath() { final LocalDataService data = mMainController.getService( LocalDataService.class ); if ( null != data ) { data.setSourceImagePath( null ); Uri uri = data.getSourceImageUri(); if ( null != uri ) { String path = IOUtils.getRealFilePath( this, uri ); if ( null != path ) { data.setSourceImagePath( path ); } } } } // -------------------------------- // DownloadImageAsyncTask listener // -------------------------------- /** * Local or remote image has been completely loaded. Now it's time to enable * all the tools and fade in the image * * @param result * the result * @param originalSize * int array containing the width and height of the loaded bitmap */ @Override public void onDownloadComplete( final Bitmap result, final ImageSizes sizes ) { logger.info( "onDownloadComplete" ); mDownloadTask = null; getMainImage().setImageBitmap( result, null, -1, UIConfiguration.IMAGE_VIEW_MAX_ZOOM ); Animation animation = AnimationUtils.loadAnimation( FeatherActivity.this, android.R.anim.fade_in ); animation.setFillEnabled( true ); mImageRestore.setVisibility( View.VISIBLE ); mImageRestore.startAnimation( animation ); hideProgressLoader(); int[] originalSize = { -1, -1 }; if ( null != sizes ) { originalSize = sizes.getRealSize(); onImageSize( sizes.getOriginalSize(), sizes.getNewSize(), sizes.getBucketSize() ); } if ( mMainController != null ) { if ( !mMainController.getEnabled() ) { mMainController.onActivate( result, originalSize ); } if ( mMainController.getOriginalBitmap() != null ) { mImageRestore.getRestoredImageView().setImageBitmap( mMainController.getOriginalBitmap(), null, -1, UIConfiguration.IMAGE_VIEW_MAX_ZOOM ); mImageRestore.setOnRestoreStateListener( FeatherActivity.this ); mImageRestore.setRestoreEnabled( true ); } else { mImageRestore.setRestoreEnabled( false ); mImageRestore.setOnRestoreStateListener( null ); } } if ( null != result && null != originalSize && originalSize.length > 1 ) { logger.error( "original.size: " + originalSize[0] + "x" + originalSize[1] ); logger.error( "final.size: " + result.getWidth() + "x" + result.getHeight() ); } computeOriginalFilePath(); loadExif(); } @SuppressWarnings ( "deprecation" ) @Override public void onDownloadError( final String error ) { logger.error( "onDownloadError", error ); mDownloadTask = null; hideProgressLoader(); showDialog( ALERT_DOWNLOAD_ERROR ); } @Override protected void onActivityResult( int requestCode, int resultCode, Intent data ) { super.onActivityResult( requestCode, resultCode, data ); mMainController.onActivityResult( requestCode, resultCode, data ); } /** * Hide progress loader. */ private void hideProgressLoader() { Animation fadeout = new AlphaAnimation( 1, 0 ); fadeout.setDuration( getResources().getInteger( android.R.integer.config_mediumAnimTime ) ); fadeout.setAnimationListener( new AnimationListener() { @Override public void onAnimationStart( Animation animation ) {} @Override public void onAnimationRepeat( Animation animation ) {} @Override public void onAnimationEnd( Animation animation ) { mInlineProgressLoader.setVisibility( View.GONE ); } } ); mInlineProgressLoader.startAnimation( fadeout ); } @Override public void onDownloadStart() { mImageRestore.setVisibility( View.INVISIBLE ); mInlineProgressLoader.setVisibility( View.VISIBLE ); } // ------------------------------- // Bitmap change listener // ------------------------------- /** * We have a new preview bitmap to display * in the main ImageView */ @Override public void onPreviewChange( Bitmap bitmap, boolean reset ) { boolean changed = true; if ( !reset ) { changed = BitmapUtils.compareBySize( ( (IBitmapDrawable) getMainImage().getDrawable() ).getBitmap(), bitmap ); } logger.info( "onPreviewChange: " + bitmap + ", changed: " + changed ); Matrix matrix = null; if ( !changed ) { matrix = getMainImage().getDisplayMatrix(); } getMainImage().setImageBitmap( bitmap, matrix, -1, UIConfiguration.IMAGE_VIEW_MAX_ZOOM ); mImageRestore.clearStatus(); } /** * Invalidate the main ImageView */ @Override public void onInvalidateBitmap() { getMainImage().invalidate(); mImageRestore.clearStatus(); } /** * Replace the current Bitmap with a new one */ @Override public void onBitmapChange( Bitmap bitmap, boolean update, Matrix matrix ) { if ( !update && matrix == null ) { matrix = getMainImage().getDisplayMatrix(); } getMainImage().setImageBitmap( bitmap, matrix, -1, UIConfiguration.IMAGE_VIEW_MAX_ZOOM ); mImageRestore.clearStatus(); }; /** * Perform save. * * @param bitmap * the bitmap */ protected void performSave( final Bitmap bitmap, final boolean changed ) { if ( mSaving ) return; mSaving = true; Tracker.recordTag( "feather: saved" ); // disable the filter manager... mMainController.setEnabled( false ); LocalDataService service = mMainController.getService( LocalDataService.class ); Bundle myExtras = getIntent().getExtras(); // if request intent has "return-data" then the result bitmap // will be encoded into the result itent if ( myExtras != null && myExtras.getBoolean( Constants.EXTRA_RETURN_DATA ) ) { Bundle extras = new Bundle(); extras.putParcelable( "data", bitmap ); onSetResult( RESULT_OK, new Intent().setData( service.getDestImageUri() ).setAction( "inline-data" ).putExtras( extras ) ); finish(); } else { ThreadUtils.startBackgroundJob( this, null, getString( R.string.feather_save_progress ), new Runnable() { @Override public void run() { doSave( bitmap, changed ); } }, mHandler ); } } /** * Final save operation * * @param bitmap * the current Bitmap handled by the {@link AviaryMainController} * @param changed * bitmap has been changed by the user */ protected void doSave( Bitmap bitmap, boolean changed ) { // result extras Bundle extras = new Bundle(); // save the "changed" information extras.putBoolean( Constants.EXTRA_OUT_BITMAP_CHANGED, changed ); LocalDataService service = mMainController.getService( LocalDataService.class ); Uri saveUri = service.getDestImageUri(); // if the request intent has EXTRA_OUTPUT declared // then save the image into the output uri and return it if ( saveUri != null ) { OutputStream outputStream = null; String scheme = saveUri.getScheme(); try { if ( scheme == null ) { outputStream = new FileOutputStream( saveUri.getPath() ); } else { outputStream = getContentResolver().openOutputStream( saveUri ); } if ( outputStream != null ) { int quality = service.getIntentValue( Constants.EXTRA_OUTPUT_QUALITY, 80 ); bitmap.compress( service.getOutputFormat(), quality, outputStream ); } } catch ( IOException ex ) { logger.error( "Cannot open file", saveUri, ex ); } finally { IOUtils.closeSilently( outputStream ); } onSetResult( RESULT_OK, new Intent().setData( saveUri ).putExtras( extras ) ); } else { // no output uri declared, save the image in a new path // and return it String url = Media.insertImage( getContentResolver(), bitmap, "title", "modified with Aviary Feather" ); if ( url != null ) { saveUri = Uri.parse( url ); getContentResolver().notifyChange( saveUri, null ); } onSetResult( RESULT_OK, new Intent().setData( saveUri ).putExtras( extras ) ); } final Bitmap b = bitmap; mHandler.post( new Runnable() { @Override public void run() { getMainImage().clear(); mImageRestore.getRestoredImageView().clear(); b.recycle(); } } ); if ( null != saveUri ) { saveExif( saveUri ); } mSaving = false; finish(); } /** * Save the exif tags * * @param uri */ protected void saveExif( Uri uri ) { logger.log( "saveExif: " + uri ); if ( null != uri ) { saveExif( uri.getPath() ); } } protected void saveExif( String path ) { logger.log( "saveExif: " + path ); if ( null == path ) { return; } LocalDataService data = mMainController.getService( LocalDataService.class ); ExifInterfaceExtended newexif = null; if ( null != data ) { try { newexif = new ExifInterfaceExtended( path ); } catch ( IOException e ) { logger.error( e.getMessage() ); e.printStackTrace(); return; } ; Bundle bundle = data.getOriginalExifBundle(); if ( null != bundle ) { try { int imageWidth = newexif.getAttributeInt( ExifInterfaceExtended.TAG_JPEG_IMAGE_WIDTH, 0 ); int imageLength = newexif.getAttributeInt( ExifInterfaceExtended.TAG_JPEG_IMAGE_HEIGHT, 0 ); // copy all the tags from the original exif // to the new exif newexif.copyFrom( bundle, true ); newexif.setAttribute( ExifInterfaceExtended.TAG_JPEG_IMAGE_WIDTH, String.valueOf( imageWidth ) ); newexif.setAttribute( ExifInterfaceExtended.TAG_JPEG_IMAGE_HEIGHT, String.valueOf( imageLength ) ); newexif.setAttribute( ExifInterfaceExtended.TAG_EXIF_ORIENTATION, "0" ); newexif.setAttribute( ExifInterfaceExtended.TAG_EXIF_SOFTWARE, "Aviary for Android " + SDKUtils.SDK_VERSION ); // implements this to include your own tags onSaveCustomTags( newexif ); newexif.saveAttributes(); } catch ( Throwable t ) { t.printStackTrace(); logger.error( t.getMessage() ); } } } } protected void onSaveCustomTags( ExifInterfaceExtended exif ) {} @Override public void onToolCompleted() {} /** * show the progress indicator in the toolbar content. */ @Override public void showToolProgress() { mToolbar.setApplyProgressVisible( true ); } /** * hide the progress indicator in the toolbar content reset to the first * null state. */ @Override public void hideToolProgress() { mToolbar.setApplyProgressVisible( false ); } @Override public void showModalProgress() { if ( mToastLoader == null ) { mToastLoader = com.aviary.android.feather.utils.UIUtils.createModalLoaderToast( this ); } mToastLoader.show(); } @Override public void hideModalProgress() { if ( mToastLoader != null ) { mToastLoader.hide(); } } /** * Gets the uI handler. * * @return the uI handler */ Handler getUIHandler() { return mUIHandler; } @Override public void onStart() { logger.info( "onStart" ); super.onStart(); mOrientation = getResources().getConfiguration().orientation; // getWindowManager().getDefaultDisplay().getRotation(); } @Override public void onStop() { logger.info( "onStop" ); super.onStop(); } @Override protected void onRestart() { logger.info( "onRestart" ); super.onRestart(); } @Override protected void onResume() { super.onResume(); mIsRunning = true; } @Override protected void onPause() { super.onPause(); mIsRunning = false; // here we can tweak the default android animation between activities } /** * Current Activity is visible and active ( ie. within the {@link #onResume()} and the * {@link #onPause()} methods ) * * @return */ public boolean isActive() { return mIsRunning; } protected void onImageSize( String originalSize, String scaledSize, String bucket ) { HashMap<String, String> attributes = new HashMap<String, String>(); attributes.put( "originalSize", originalSize ); attributes.put( "newSize", scaledSize ); attributes.put( "bucketSize", bucket ); Tracker.recordTag( "image: scaled", attributes ); } class ListAdapter extends ArrayAdapter<ToolEntry> { static final int TYPE_TOOL = 0; static final int TYPE_FEDDBACK = 1; Object mLock = new Object(); LayoutInflater mInflater; List<ToolEntry> mObjects; int mViewWidth; int mToolViewWidth; boolean mWhiteLabel; public ListAdapter ( Context context, List<ToolEntry> objects, boolean whiteLabel ) { super( context, -1 ); mViewWidth = context.getResources().getDisplayMetrics().widthPixels; mToolViewWidth = -1; mInflater = LayoutInflater.from( context ); mObjects = objects; mWhiteLabel = whiteLabel; } @Override public int getViewTypeCount() { if ( mWhiteLabel ) return 1; return 2; } @Override public int getItemViewType( int position ) { if ( mWhiteLabel ) return TYPE_TOOL; return position == ( getCount() - 1 ) ? TYPE_FEDDBACK : TYPE_TOOL; } @Override public View getView( int position, View convertView, ViewGroup parent ) { final int type = getItemViewType( position ); if ( null == convertView ) { if ( type == TYPE_TOOL ) { convertView = mInflater.inflate( R.layout.aviary_tool_layout, parent, false ); LayoutParams params = convertView.getLayoutParams(); if ( mToolViewWidth == -1 ) { int[] sizes = mToolsList.measureChild( convertView ); logger.log( "child size: " + sizes[0] + "x" + sizes[1] ); double numberOfItems = Math.floor( (double) mViewWidth / sizes[0] ) + 0.5; logger.log( "new number of items: " + numberOfItems ); mToolViewWidth = (int) ( (double) mViewWidth / numberOfItems ); logger.log( "new size will be: " + mToolViewWidth ); } if ( null != params ) { params.width = mToolViewWidth; convertView.setLayoutParams( params ); } } else { convertView = mInflater.inflate( R.layout.aviary_tool_feedback_layout, parent, false ); } } if ( type == TYPE_TOOL ) { final ToolEntry item = getItem( position ); convertView.setTag( item ); } return convertView; } @Override public ToolEntry getItem( int position ) { return mObjects.get( position ); } @Override public int getCount() { if ( mWhiteLabel ) return mObjects.size(); return mObjects.size() + 1; } @Override public long getItemId( int position ) { return 0; } } /** * Returns the main controller * * @return */ public AviaryMainController getMainController() { return mMainController; } @SuppressWarnings ( "deprecation" ) @Override public void onItemClick( AdapterView<?> parent, View view, int position, long id ) { logger.info( "onItemClick: " + position ); if ( null != view && parent.isEnabled() && parent.getAdapter() != null ) { int type = parent.getAdapter().getItemViewType( position ); if ( type == ListAdapter.TYPE_TOOL ) { // tool final Object tag = parent.getAdapter().getItem( position ); if ( tag instanceof ToolEntry ) { mUIHandler.postDelayed( new Runnable() { @Override public void run() { // mark the badge for this tool as read // ( (AviaryBadgeToolLayout) view ).markAsRead(); mMainController.activateTool( (ToolEntry) tag ); } }, TOOLS_OPEN_DELAY_TIME ); } } else if ( type == ListAdapter.TYPE_FEDDBACK ) { // feedback showDialog( ALERT_FEEDBACK ); } } } @Override public boolean onRestoreBegin() { logger.info( "onRestoreBegin" ); if ( null != mMainController ) { if ( null != mMainController.getOriginalBitmap() ) { if ( !mMainController.getPanelIsRendering() && mMainController.getBitmapIsChangedOrChanging() ) { mImageRestore.setDisplayedChild( 1 ); Tracker.recordTag( "prepost: Pressed" ); return true; } } } return false; } @Override public void onRestoreChanged() { logger.info( "onRestoreChanged" ); mToolbar.toggleRestore( true ); Tracker.recordTag( "prepost: RestoreOriginalShown" ); // nothing... } @Override public void onRestoreEnd() { logger.info( "onRestoreEnd" ); mImageRestore.setDisplayedChild( 0 ); mToolbar.toggleRestore( false ); } }