Home | History | Annotate | Download | only in filtershow
      1 /*
      2  * Copyright (C) 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.gallery3d.filtershow;
     18 
     19 import android.app.ActionBar;
     20 import android.app.AlertDialog;
     21 import android.app.ProgressDialog;
     22 import android.content.ComponentName;
     23 import android.content.ContentValues;
     24 import android.content.Context;
     25 import android.content.DialogInterface;
     26 import android.content.Intent;
     27 import android.content.ServiceConnection;
     28 import android.content.pm.ActivityInfo;
     29 import android.content.res.Configuration;
     30 import android.content.res.Resources;
     31 import android.graphics.Bitmap;
     32 import android.graphics.Color;
     33 import android.graphics.Matrix;
     34 import android.graphics.Point;
     35 import android.graphics.Rect;
     36 import android.graphics.RectF;
     37 import android.graphics.drawable.ColorDrawable;
     38 import android.graphics.drawable.Drawable;
     39 import android.net.Uri;
     40 import android.os.AsyncTask;
     41 import android.os.Bundle;
     42 import android.os.CancellationSignal;
     43 import android.os.Handler;
     44 import android.os.IBinder;
     45 import androidx.fragment.app.DialogFragment;
     46 import androidx.fragment.app.Fragment;
     47 import androidx.fragment.app.FragmentActivity;
     48 import androidx.fragment.app.FragmentTransaction;
     49 import androidx.print.PrintHelper;
     50 import android.util.DisplayMetrics;
     51 import android.util.Log;
     52 import android.util.TypedValue;
     53 import android.view.Menu;
     54 import android.view.MenuItem;
     55 import android.view.MotionEvent;
     56 import android.view.View;
     57 import android.view.View.OnClickListener;
     58 import android.view.ViewPropertyAnimator;
     59 import android.view.WindowManager;
     60 import android.widget.AdapterView;
     61 import android.widget.AdapterView.OnItemClickListener;
     62 import android.widget.FrameLayout;
     63 import android.widget.PopupMenu;
     64 import android.widget.ShareActionProvider;
     65 import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
     66 import android.widget.Spinner;
     67 import android.widget.Toast;
     68 
     69 import com.android.gallery3d.R;
     70 import com.android.gallery3d.app.PhotoPage;
     71 import com.android.gallery3d.data.LocalAlbum;
     72 import com.android.gallery3d.filtershow.cache.ImageLoader;
     73 import com.android.gallery3d.filtershow.category.Action;
     74 import com.android.gallery3d.filtershow.category.CategoryAdapter;
     75 import com.android.gallery3d.filtershow.category.CategorySelected;
     76 import com.android.gallery3d.filtershow.category.CategoryView;
     77 import com.android.gallery3d.filtershow.category.MainPanel;
     78 import com.android.gallery3d.filtershow.category.SwipableView;
     79 import com.android.gallery3d.filtershow.data.UserPresetsManager;
     80 import com.android.gallery3d.filtershow.editors.BasicEditor;
     81 import com.android.gallery3d.filtershow.editors.Editor;
     82 import com.android.gallery3d.filtershow.editors.EditorChanSat;
     83 import com.android.gallery3d.filtershow.editors.EditorColorBorder;
     84 import com.android.gallery3d.filtershow.editors.EditorCrop;
     85 import com.android.gallery3d.filtershow.editors.EditorDraw;
     86 import com.android.gallery3d.filtershow.editors.EditorGrad;
     87 import com.android.gallery3d.filtershow.editors.EditorManager;
     88 import com.android.gallery3d.filtershow.editors.EditorMirror;
     89 import com.android.gallery3d.filtershow.editors.EditorPanel;
     90 import com.android.gallery3d.filtershow.editors.EditorRedEye;
     91 import com.android.gallery3d.filtershow.editors.EditorRotate;
     92 import com.android.gallery3d.filtershow.editors.EditorStraighten;
     93 import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
     94 import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
     95 import com.android.gallery3d.filtershow.filters.FilterDrawRepresentation;
     96 import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation;
     97 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
     98 import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation;
     99 import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation;
    100 import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation;
    101 import com.android.gallery3d.filtershow.filters.FiltersManager;
    102 import com.android.gallery3d.filtershow.filters.ImageFilter;
    103 import com.android.gallery3d.filtershow.history.HistoryItem;
    104 import com.android.gallery3d.filtershow.history.HistoryManager;
    105 import com.android.gallery3d.filtershow.imageshow.ImageShow;
    106 import com.android.gallery3d.filtershow.imageshow.MasterImage;
    107 import com.android.gallery3d.filtershow.imageshow.Spline;
    108 import com.android.gallery3d.filtershow.info.InfoPanel;
    109 import com.android.gallery3d.filtershow.pipeline.CachingPipeline;
    110 import com.android.gallery3d.filtershow.pipeline.ImagePreset;
    111 import com.android.gallery3d.filtershow.pipeline.ProcessingService;
    112 import com.android.gallery3d.filtershow.presets.PresetManagementDialog;
    113 import com.android.gallery3d.filtershow.presets.UserPresetsAdapter;
    114 import com.android.gallery3d.filtershow.provider.SharedImageProvider;
    115 import com.android.gallery3d.filtershow.state.StateAdapter;
    116 import com.android.gallery3d.filtershow.tools.SaveImage;
    117 import com.android.gallery3d.filtershow.tools.XmpPresets;
    118 import com.android.gallery3d.filtershow.tools.XmpPresets.XMresults;
    119 import com.android.gallery3d.filtershow.ui.ExportDialog;
    120 import com.android.gallery3d.filtershow.ui.FramedTextButton;
    121 import com.android.gallery3d.util.GalleryUtils;
    122 import com.android.photos.data.GalleryBitmapPool;
    123 
    124 import java.io.File;
    125 import java.io.FileDescriptor;
    126 import java.io.FileOutputStream;
    127 import java.lang.ref.WeakReference;
    128 import java.util.ArrayList;
    129 import java.util.Vector;
    130 
    131 public class FilterShowActivity extends FragmentActivity implements OnItemClickListener,
    132         OnShareTargetSelectedListener, DialogInterface.OnShowListener,
    133         DialogInterface.OnDismissListener, PopupMenu.OnDismissListener{
    134 
    135     private String mAction = "";
    136     MasterImage mMasterImage = null;
    137 
    138     private static final long LIMIT_SUPPORTS_HIGHRES = 134217728; // 128Mb
    139 
    140     public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
    141     public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
    142     public static final boolean RESET_TO_LOADED = false;
    143     private ImageShow mImageShow = null;
    144 
    145     private View mSaveButton = null;
    146 
    147     private EditorPlaceHolder mEditorPlaceHolder = new EditorPlaceHolder(this);
    148     private Editor mCurrentEditor = null;
    149 
    150     private static final int SELECT_PICTURE = 1;
    151     private static final String LOGTAG = "FilterShowActivity";
    152 
    153     private boolean mShowingTinyPlanet = false;
    154     private boolean mShowingImageStatePanel = false;
    155     private boolean mShowingVersionsPanel = false;
    156 
    157     private final Vector<ImageShow> mImageViews = new Vector<ImageShow>();
    158 
    159     private ShareActionProvider mShareActionProvider;
    160     private File mSharedOutputFile = null;
    161 
    162     private boolean mSharingImage = false;
    163 
    164     private WeakReference<ProgressDialog> mSavingProgressDialog;
    165 
    166     private LoadBitmapTask mLoadBitmapTask;
    167 
    168     private Uri mOriginalImageUri = null;
    169     private ImagePreset mOriginalPreset = null;
    170 
    171     private Uri mSelectedImageUri = null;
    172 
    173     private ArrayList<Action> mActions = new ArrayList<Action>();
    174     private UserPresetsManager mUserPresetsManager = null;
    175     private UserPresetsAdapter mUserPresetsAdapter = null;
    176     private CategoryAdapter mCategoryLooksAdapter = null;
    177     private CategoryAdapter mCategoryBordersAdapter = null;
    178     private CategoryAdapter mCategoryGeometryAdapter = null;
    179     private CategoryAdapter mCategoryFiltersAdapter = null;
    180     private CategoryAdapter mCategoryVersionsAdapter = null;
    181     private int mCurrentPanel = MainPanel.LOOKS;
    182     private Vector<FilterUserPresetRepresentation> mVersions =
    183             new Vector<FilterUserPresetRepresentation>();
    184     private int mVersionsCounter = 0;
    185 
    186     private boolean mHandlingSwipeButton = false;
    187     private View mHandledSwipeView = null;
    188     private float mHandledSwipeViewLastDelta = 0;
    189     private float mSwipeStartX = 0;
    190     private float mSwipeStartY = 0;
    191 
    192     private ProcessingService mBoundService;
    193     private boolean mIsBound = false;
    194     private Menu mMenu;
    195     private DialogInterface mCurrentDialog = null;
    196     private PopupMenu mCurrentMenu = null;
    197     private boolean mLoadingVisible = true;
    198 
    199     public ProcessingService getProcessingService() {
    200         return mBoundService;
    201     }
    202 
    203     public boolean isSimpleEditAction() {
    204         return !PhotoPage.ACTION_NEXTGEN_EDIT.equalsIgnoreCase(mAction);
    205     }
    206 
    207     private ServiceConnection mConnection = new ServiceConnection() {
    208         @Override
    209         public void onServiceConnected(ComponentName className, IBinder service) {
    210             /*
    211              * This is called when the connection with the service has been
    212              * established, giving us the service object we can use to
    213              * interact with the service.  Because we have bound to a explicit
    214              * service that we know is running in our own process, we can
    215              * cast its IBinder to a concrete class and directly access it.
    216              */
    217             mBoundService = ((ProcessingService.LocalBinder)service).getService();
    218             mBoundService.setFiltershowActivity(FilterShowActivity.this);
    219             mBoundService.onStart();
    220         }
    221 
    222         @Override
    223         public void onServiceDisconnected(ComponentName className) {
    224             /*
    225              * This is called when the connection with the service has been
    226              * unexpectedly disconnected -- that is, its process crashed.
    227              * Because it is running in our same process, we should never
    228              * see this happen.
    229              */
    230             mBoundService = null;
    231         }
    232     };
    233 
    234     void doBindService() {
    235         /*
    236          * Establish a connection with the service.  We use an explicit
    237          * class name because we want a specific service implementation that
    238          * we know will be running in our own process (and thus won't be
    239          * supporting component replacement by other applications).
    240          */
    241         bindService(new Intent(FilterShowActivity.this, ProcessingService.class),
    242                 mConnection, Context.BIND_AUTO_CREATE);
    243         mIsBound = true;
    244     }
    245 
    246     void doUnbindService() {
    247         if (mIsBound) {
    248             // Detach our existing connection.
    249             unbindService(mConnection);
    250             mIsBound = false;
    251         }
    252     }
    253 
    254     public void updateUIAfterServiceStarted() {
    255         MasterImage.setMaster(mMasterImage);
    256         ImageFilter.setActivityForMemoryToasts(this);
    257         mUserPresetsManager = new UserPresetsManager(this);
    258         mUserPresetsAdapter = new UserPresetsAdapter(this);
    259 
    260         setupMasterImage();
    261         setupMenu();
    262         setDefaultValues();
    263         fillEditors();
    264         getWindow().setBackgroundDrawable(new ColorDrawable(0));
    265         loadXML();
    266 
    267         fillCategories();
    268         loadMainPanel();
    269         extractXMPData();
    270         processIntent();
    271     }
    272 
    273     @Override
    274     public void onCreate(Bundle savedInstanceState) {
    275         super.onCreate(savedInstanceState);
    276 
    277         boolean onlyUsePortrait = getResources().getBoolean(R.bool.only_use_portrait);
    278         if (onlyUsePortrait) {
    279             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    280         }
    281 
    282         clearGalleryBitmapPool();
    283         doBindService();
    284         getWindow().setBackgroundDrawable(new ColorDrawable(Color.GRAY));
    285         setContentView(R.layout.filtershow_splashscreen);
    286     }
    287 
    288     public boolean isShowingImageStatePanel() {
    289         return mShowingImageStatePanel;
    290     }
    291 
    292     public void loadMainPanel() {
    293         if (findViewById(R.id.main_panel_container) == null) {
    294             return;
    295         }
    296         MainPanel panel = new MainPanel();
    297         FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    298         transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
    299         transaction.commitAllowingStateLoss();
    300     }
    301 
    302     public void loadEditorPanel(FilterRepresentation representation,
    303                                 final Editor currentEditor) {
    304         if (representation.getEditorId() == ImageOnlyEditor.ID) {
    305             currentEditor.reflectCurrentFilter();
    306             return;
    307         }
    308         final int currentId = currentEditor.getID();
    309         Runnable showEditor = new Runnable() {
    310             @Override
    311             public void run() {
    312                 EditorPanel panel = new EditorPanel();
    313                 panel.setEditor(currentId);
    314                 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    315                 transaction.remove(getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG));
    316                 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
    317                 transaction.commit();
    318             }
    319         };
    320         Fragment main = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
    321         boolean doAnimation = false;
    322         if (mShowingImageStatePanel
    323                 && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
    324             doAnimation = true;
    325         }
    326         if (doAnimation && main != null && main instanceof MainPanel) {
    327             MainPanel mainPanel = (MainPanel) main;
    328             View container = mainPanel.getView().findViewById(R.id.category_panel_container);
    329             View bottom = mainPanel.getView().findViewById(R.id.bottom_panel);
    330             int panelHeight = container.getHeight() + bottom.getHeight();
    331             ViewPropertyAnimator anim = mainPanel.getView().animate();
    332             anim.translationY(panelHeight).start();
    333             final Handler handler = new Handler();
    334             handler.postDelayed(showEditor, anim.getDuration());
    335         } else {
    336             showEditor.run();
    337         }
    338     }
    339 
    340     public void toggleInformationPanel() {
    341         FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    342         transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
    343 
    344         InfoPanel panel = new InfoPanel();
    345         panel.show(transaction, InfoPanel.FRAGMENT_TAG);
    346     }
    347 
    348     private void loadXML() {
    349         setContentView(R.layout.filtershow_activity);
    350 
    351         ActionBar actionBar = getActionBar();
    352         actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
    353         actionBar.setCustomView(R.layout.filtershow_actionbar);
    354         actionBar.setBackgroundDrawable(new ColorDrawable(
    355                 getResources().getColor(R.color.background_screen)));
    356 
    357         mSaveButton = actionBar.getCustomView();
    358         mSaveButton.setOnClickListener(new OnClickListener() {
    359             @Override
    360             public void onClick(View view) {
    361                 saveImage();
    362             }
    363         });
    364 
    365         mImageShow = (ImageShow) findViewById(R.id.imageShow);
    366         mImageViews.add(mImageShow);
    367 
    368         setupEditors();
    369 
    370         mEditorPlaceHolder.hide();
    371         mImageShow.attach();
    372 
    373         setupStatePanel();
    374     }
    375 
    376     public void fillCategories() {
    377         fillLooks();
    378         loadUserPresets();
    379         fillBorders();
    380         fillTools();
    381         fillEffects();
    382         fillVersions();
    383     }
    384 
    385     public void setupStatePanel() {
    386         MasterImage.getImage().setHistoryManager(mMasterImage.getHistory());
    387     }
    388 
    389     private void fillVersions() {
    390         if (mCategoryVersionsAdapter != null) {
    391             mCategoryVersionsAdapter.clear();
    392         }
    393         mCategoryVersionsAdapter = new CategoryAdapter(this);
    394         mCategoryVersionsAdapter.setShowAddButton(true);
    395     }
    396 
    397     public void registerAction(Action action) {
    398         if (mActions.contains(action)) {
    399             return;
    400         }
    401         mActions.add(action);
    402     }
    403 
    404     private void loadActions() {
    405         for (int i = 0; i < mActions.size(); i++) {
    406             Action action = mActions.get(i);
    407             action.setImageFrame(new Rect(0, 0, 96, 96), 0);
    408         }
    409     }
    410 
    411     public void updateVersions() {
    412         mCategoryVersionsAdapter.clear();
    413         FilterUserPresetRepresentation originalRep = new FilterUserPresetRepresentation(
    414                 getString(R.string.filtershow_version_original), new ImagePreset(), -1);
    415         mCategoryVersionsAdapter.add(
    416                 new Action(this, originalRep, Action.FULL_VIEW));
    417         ImagePreset current = new ImagePreset(MasterImage.getImage().getPreset());
    418         FilterUserPresetRepresentation currentRep = new FilterUserPresetRepresentation(
    419                 getString(R.string.filtershow_version_current), current, -1);
    420         mCategoryVersionsAdapter.add(
    421                 new Action(this, currentRep, Action.FULL_VIEW));
    422         if (mVersions.size() > 0) {
    423             mCategoryVersionsAdapter.add(new Action(this, Action.SPACER));
    424         }
    425         for (FilterUserPresetRepresentation rep : mVersions) {
    426             mCategoryVersionsAdapter.add(
    427                     new Action(this, rep, Action.FULL_VIEW, true));
    428         }
    429         mCategoryVersionsAdapter.notifyDataSetInvalidated();
    430     }
    431 
    432     public void addCurrentVersion() {
    433         ImagePreset current = new ImagePreset(MasterImage.getImage().getPreset());
    434         mVersionsCounter++;
    435         FilterUserPresetRepresentation rep = new FilterUserPresetRepresentation(
    436                 "" + mVersionsCounter, current, -1);
    437         mVersions.add(rep);
    438         updateVersions();
    439     }
    440 
    441     public void removeVersion(Action action) {
    442         mVersions.remove(action.getRepresentation());
    443         updateVersions();
    444     }
    445 
    446     public void removeLook(Action action) {
    447         FilterUserPresetRepresentation rep =
    448                 (FilterUserPresetRepresentation) action.getRepresentation();
    449         if (rep == null) {
    450             return;
    451         }
    452         mUserPresetsManager.delete(rep.getId());
    453         updateUserPresetsFromManager();
    454     }
    455 
    456     private void fillEffects() {
    457         FiltersManager filtersManager = FiltersManager.getManager();
    458         ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getEffects();
    459         if (mCategoryFiltersAdapter != null) {
    460             mCategoryFiltersAdapter.clear();
    461         }
    462         mCategoryFiltersAdapter = new CategoryAdapter(this);
    463         for (FilterRepresentation representation : filtersRepresentations) {
    464             if (representation.getTextId() != 0) {
    465                 representation.setName(getString(representation.getTextId()));
    466             }
    467             mCategoryFiltersAdapter.add(new Action(this, representation));
    468         }
    469     }
    470 
    471     private void fillTools() {
    472         FiltersManager filtersManager = FiltersManager.getManager();
    473         ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getTools();
    474         if (mCategoryGeometryAdapter != null) {
    475             mCategoryGeometryAdapter.clear();
    476         }
    477         mCategoryGeometryAdapter = new CategoryAdapter(this);
    478         boolean found = false;
    479         for (FilterRepresentation representation : filtersRepresentations) {
    480             mCategoryGeometryAdapter.add(new Action(this, representation));
    481             if (representation instanceof FilterDrawRepresentation) {
    482                 found = true;
    483             }
    484         }
    485         if (!found) {
    486             FilterRepresentation representation = new FilterDrawRepresentation();
    487             Action action = new Action(this, representation);
    488             action.setIsDoubleAction(true);
    489             mCategoryGeometryAdapter.add(action);
    490         }
    491     }
    492 
    493     private void processIntent() {
    494         Intent intent = getIntent();
    495         if (intent.getBooleanExtra(LAUNCH_FULLSCREEN, false)) {
    496             getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
    497         }
    498 
    499         mAction = intent.getAction();
    500         mSelectedImageUri = intent.getData();
    501         Uri loadUri = mSelectedImageUri;
    502         if (mOriginalImageUri != null) {
    503             loadUri = mOriginalImageUri;
    504         }
    505         if (loadUri != null) {
    506             startLoadBitmap(loadUri);
    507         } else {
    508             pickImage();
    509         }
    510     }
    511 
    512     private void setupEditors() {
    513         mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer));
    514         EditorManager.addEditors(mEditorPlaceHolder);
    515         mEditorPlaceHolder.setOldViews(mImageViews);
    516     }
    517 
    518     private void fillEditors() {
    519         mEditorPlaceHolder.addEditor(new EditorChanSat());
    520         mEditorPlaceHolder.addEditor(new EditorGrad());
    521         mEditorPlaceHolder.addEditor(new EditorDraw());
    522         mEditorPlaceHolder.addEditor(new EditorColorBorder());
    523         mEditorPlaceHolder.addEditor(new BasicEditor());
    524         mEditorPlaceHolder.addEditor(new ImageOnlyEditor());
    525         mEditorPlaceHolder.addEditor(new EditorTinyPlanet());
    526         mEditorPlaceHolder.addEditor(new EditorRedEye());
    527         mEditorPlaceHolder.addEditor(new EditorCrop());
    528         mEditorPlaceHolder.addEditor(new EditorMirror());
    529         mEditorPlaceHolder.addEditor(new EditorRotate());
    530         mEditorPlaceHolder.addEditor(new EditorStraighten());
    531     }
    532 
    533     private void setDefaultValues() {
    534         Resources res = getResources();
    535 
    536         // TODO: get those values from XML.
    537         FramedTextButton.setTextSize((int) getPixelsFromDip(14));
    538         FramedTextButton.setTrianglePadding((int) getPixelsFromDip(4));
    539         FramedTextButton.setTriangleSize((int) getPixelsFromDip(10));
    540 
    541         Drawable curveHandle = res.getDrawable(R.drawable.camera_crop);
    542         int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size);
    543         Spline.setCurveHandle(curveHandle, curveHandleSize);
    544         Spline.setCurveWidth((int) getPixelsFromDip(3));
    545 
    546         mOriginalImageUri = null;
    547     }
    548 
    549     private void startLoadBitmap(Uri uri) {
    550         final View imageShow = findViewById(R.id.imageShow);
    551         imageShow.setVisibility(View.INVISIBLE);
    552         startLoadingIndicator();
    553         mShowingTinyPlanet = false;
    554         mLoadBitmapTask = new LoadBitmapTask();
    555         mLoadBitmapTask.execute(uri);
    556     }
    557 
    558     private void fillBorders() {
    559         FiltersManager filtersManager = FiltersManager.getManager();
    560         ArrayList<FilterRepresentation> borders = filtersManager.getBorders();
    561 
    562         for (int i = 0; i < borders.size(); i++) {
    563             FilterRepresentation filter = borders.get(i);
    564             filter.setName(getString(R.string.borders));
    565             if (i == 0) {
    566                 filter.setName(getString(R.string.none));
    567             }
    568         }
    569 
    570         if (mCategoryBordersAdapter != null) {
    571             mCategoryBordersAdapter.clear();
    572         }
    573         mCategoryBordersAdapter = new CategoryAdapter(this);
    574         for (FilterRepresentation representation : borders) {
    575             if (representation.getTextId() != 0) {
    576                 representation.setName(getString(representation.getTextId()));
    577             }
    578             mCategoryBordersAdapter.add(new Action(this, representation, Action.FULL_VIEW));
    579         }
    580     }
    581 
    582     public UserPresetsAdapter getUserPresetsAdapter() {
    583         return mUserPresetsAdapter;
    584     }
    585 
    586     public CategoryAdapter getCategoryLooksAdapter() {
    587         return mCategoryLooksAdapter;
    588     }
    589 
    590     public CategoryAdapter getCategoryBordersAdapter() {
    591         return mCategoryBordersAdapter;
    592     }
    593 
    594     public CategoryAdapter getCategoryGeometryAdapter() {
    595         return mCategoryGeometryAdapter;
    596     }
    597 
    598     public CategoryAdapter getCategoryFiltersAdapter() {
    599         return mCategoryFiltersAdapter;
    600     }
    601 
    602     public CategoryAdapter getCategoryVersionsAdapter() {
    603         return mCategoryVersionsAdapter;
    604     }
    605 
    606     public void removeFilterRepresentation(FilterRepresentation filterRepresentation) {
    607         if (filterRepresentation == null) {
    608             return;
    609         }
    610         ImagePreset oldPreset = MasterImage.getImage().getPreset();
    611         ImagePreset copy = new ImagePreset(oldPreset);
    612         copy.removeFilter(filterRepresentation);
    613         MasterImage.getImage().setPreset(copy, copy.getLastRepresentation(), true);
    614         if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
    615             FilterRepresentation lastRepresentation = copy.getLastRepresentation();
    616             MasterImage.getImage().setCurrentFilterRepresentation(lastRepresentation);
    617         }
    618     }
    619 
    620     public void useFilterRepresentation(FilterRepresentation filterRepresentation) {
    621         if (filterRepresentation == null) {
    622             return;
    623         }
    624         if (!(filterRepresentation instanceof FilterRotateRepresentation)
    625             && !(filterRepresentation instanceof FilterMirrorRepresentation)
    626             && MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
    627             return;
    628         }
    629         if (filterRepresentation instanceof FilterUserPresetRepresentation
    630                 || filterRepresentation instanceof FilterRotateRepresentation
    631                 || filterRepresentation instanceof FilterMirrorRepresentation) {
    632             MasterImage.getImage().onNewLook(filterRepresentation);
    633         }
    634         ImagePreset oldPreset = MasterImage.getImage().getPreset();
    635         ImagePreset copy = new ImagePreset(oldPreset);
    636         FilterRepresentation representation = copy.getRepresentation(filterRepresentation);
    637         if (representation == null) {
    638             filterRepresentation = filterRepresentation.copy();
    639             copy.addFilter(filterRepresentation);
    640         } else {
    641             if (filterRepresentation.allowsSingleInstanceOnly()) {
    642                 // Don't just update the filter representation. Centralize the
    643                 // logic in the addFilter(), such that we can keep "None" as
    644                 // null.
    645                 if (!representation.equals(filterRepresentation)) {
    646                     // Only do this if the filter isn't the same
    647                     // (state panel clicks can lead us here)
    648                     copy.removeFilter(representation);
    649                     copy.addFilter(filterRepresentation);
    650                 }
    651             }
    652         }
    653         MasterImage.getImage().setPreset(copy, filterRepresentation, true);
    654         MasterImage.getImage().setCurrentFilterRepresentation(filterRepresentation);
    655     }
    656 
    657     public void showRepresentation(FilterRepresentation representation) {
    658         if (representation == null) {
    659             return;
    660         }
    661 
    662         if (representation instanceof FilterRotateRepresentation) {
    663             FilterRotateRepresentation r = (FilterRotateRepresentation) representation;
    664             r.rotateCW();
    665         }
    666         if (representation instanceof FilterMirrorRepresentation) {
    667             FilterMirrorRepresentation r = (FilterMirrorRepresentation) representation;
    668             r.cycle();
    669         }
    670         if (representation.isBooleanFilter()) {
    671             ImagePreset preset = MasterImage.getImage().getPreset();
    672             if (preset.getRepresentation(representation) != null) {
    673                 // remove
    674                 ImagePreset copy = new ImagePreset(preset);
    675                 copy.removeFilter(representation);
    676                 FilterRepresentation filterRepresentation = representation.copy();
    677                 MasterImage.getImage().setPreset(copy, filterRepresentation, true);
    678                 MasterImage.getImage().setCurrentFilterRepresentation(null);
    679                 return;
    680             }
    681         }
    682         useFilterRepresentation(representation);
    683 
    684         // show representation
    685         if (mCurrentEditor != null) {
    686             mCurrentEditor.detach();
    687         }
    688         mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId());
    689         loadEditorPanel(representation, mCurrentEditor);
    690     }
    691 
    692     public Editor getEditor(int editorID) {
    693         return mEditorPlaceHolder.getEditor(editorID);
    694     }
    695 
    696     public void setCurrentPanel(int currentPanel) {
    697         mCurrentPanel = currentPanel;
    698     }
    699 
    700     public int getCurrentPanel() {
    701         return mCurrentPanel;
    702     }
    703 
    704     public void updateCategories() {
    705         if (mMasterImage == null) {
    706             return;
    707         }
    708         ImagePreset preset = mMasterImage.getPreset();
    709         mCategoryLooksAdapter.reflectImagePreset(preset);
    710         mCategoryBordersAdapter.reflectImagePreset(preset);
    711     }
    712 
    713     public View getMainStatePanelContainer(int id) {
    714         return findViewById(id);
    715     }
    716 
    717     public void onShowMenu(PopupMenu menu) {
    718         mCurrentMenu = menu;
    719         menu.setOnDismissListener(this);
    720     }
    721 
    722     @Override
    723     public void onDismiss(PopupMenu popupMenu){
    724         if (mCurrentMenu == null) {
    725             return;
    726         }
    727         mCurrentMenu.setOnDismissListener(null);
    728         mCurrentMenu = null;
    729     }
    730 
    731     @Override
    732     public void onShow(DialogInterface dialog) {
    733         mCurrentDialog = dialog;
    734     }
    735 
    736     @Override
    737     public void onDismiss(DialogInterface dialogInterface) {
    738         mCurrentDialog = null;
    739     }
    740 
    741     private class LoadHighresBitmapTask extends AsyncTask<Void, Void, Boolean> {
    742         @Override
    743         protected Boolean doInBackground(Void... params) {
    744             MasterImage master = MasterImage.getImage();
    745             Rect originalBounds = master.getOriginalBounds();
    746             if (master.supportsHighRes()) {
    747                 int highresPreviewSize = master.getOriginalBitmapLarge().getWidth() * 2;
    748                 if (highresPreviewSize > originalBounds.width()) {
    749                     highresPreviewSize = originalBounds.width();
    750                 }
    751                 Rect bounds = new Rect();
    752                 Bitmap originalHires = ImageLoader.loadOrientedConstrainedBitmap(master.getUri(),
    753                         master.getActivity(), highresPreviewSize,
    754                         master.getOrientation(), bounds);
    755                 master.setOriginalBounds(bounds);
    756                 master.setOriginalBitmapHighres(originalHires);
    757                 mBoundService.setOriginalBitmapHighres(originalHires);
    758                 master.warnListeners();
    759             }
    760             return true;
    761         }
    762 
    763         @Override
    764         protected void onPostExecute(Boolean result) {
    765             Bitmap highresBitmap = MasterImage.getImage().getOriginalBitmapHighres();
    766             if (highresBitmap != null) {
    767                 float highResPreviewScale = (float) highresBitmap.getWidth()
    768                         / (float) MasterImage.getImage().getOriginalBounds().width();
    769                 mBoundService.setHighresPreviewScaleFactor(highResPreviewScale);
    770             }
    771             MasterImage.getImage().warnListeners();
    772         }
    773     }
    774 
    775     public boolean isLoadingVisible() {
    776         return mLoadingVisible;
    777     }
    778 
    779     public void startLoadingIndicator() {
    780         final View loading = findViewById(R.id.loading);
    781         mLoadingVisible = true;
    782         loading.setVisibility(View.VISIBLE);
    783     }
    784 
    785     public void stopLoadingIndicator() {
    786         final View loading = findViewById(R.id.loading);
    787         loading.setVisibility(View.GONE);
    788         mLoadingVisible = false;
    789     }
    790 
    791     private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
    792         int mBitmapSize;
    793 
    794         public LoadBitmapTask() {
    795             mBitmapSize = getScreenImageSize();
    796         }
    797 
    798         @Override
    799         protected Boolean doInBackground(Uri... params) {
    800             if (!MasterImage.getImage().loadBitmap(params[0], mBitmapSize)) {
    801                 return false;
    802             }
    803             publishProgress(ImageLoader.queryLightCycle360(MasterImage.getImage().getActivity()));
    804             return true;
    805         }
    806 
    807         @Override
    808         protected void onProgressUpdate(Boolean... values) {
    809             super.onProgressUpdate(values);
    810             if (isCancelled()) {
    811                 return;
    812             }
    813             if (values[0]) {
    814                 mShowingTinyPlanet = true;
    815             }
    816         }
    817 
    818         @Override
    819         protected void onPostExecute(Boolean result) {
    820             MasterImage.setMaster(mMasterImage);
    821             if (isCancelled()) {
    822                 return;
    823             }
    824 
    825             if (!result) {
    826                 if (mOriginalImageUri != null
    827                         && !mOriginalImageUri.equals(mSelectedImageUri)) {
    828                     mOriginalImageUri = mSelectedImageUri;
    829                     mOriginalPreset = null;
    830                     Toast.makeText(FilterShowActivity.this,
    831                             R.string.cannot_edit_original, Toast.LENGTH_SHORT).show();
    832                     startLoadBitmap(mOriginalImageUri);
    833                 } else {
    834                     cannotLoadImage();
    835                 }
    836                 return;
    837             }
    838 
    839             if (null == CachingPipeline.getRenderScriptContext()){
    840                 Log.v(LOGTAG,"RenderScript context destroyed during load");
    841                 return;
    842             }
    843             final View imageShow = findViewById(R.id.imageShow);
    844             imageShow.setVisibility(View.VISIBLE);
    845 
    846 
    847             Bitmap largeBitmap = MasterImage.getImage().getOriginalBitmapLarge();
    848             mBoundService.setOriginalBitmap(largeBitmap);
    849 
    850             float previewScale = (float) largeBitmap.getWidth()
    851                     / (float) MasterImage.getImage().getOriginalBounds().width();
    852             mBoundService.setPreviewScaleFactor(previewScale);
    853             if (!mShowingTinyPlanet) {
    854                 mCategoryFiltersAdapter.removeTinyPlanet();
    855             }
    856             mCategoryLooksAdapter.imageLoaded();
    857             mCategoryBordersAdapter.imageLoaded();
    858             mCategoryGeometryAdapter.imageLoaded();
    859             mCategoryFiltersAdapter.imageLoaded();
    860             mLoadBitmapTask = null;
    861 
    862             MasterImage.getImage().warnListeners();
    863             loadActions();
    864 
    865             if (mOriginalPreset != null) {
    866                 MasterImage.getImage().setLoadedPreset(mOriginalPreset);
    867                 MasterImage.getImage().setPreset(mOriginalPreset,
    868                         mOriginalPreset.getLastRepresentation(), true);
    869                 mOriginalPreset = null;
    870             } else {
    871                 setDefaultPreset();
    872             }
    873 
    874             MasterImage.getImage().resetGeometryImages(true);
    875 
    876             if (mAction == TINY_PLANET_ACTION) {
    877                 showRepresentation(mCategoryFiltersAdapter.getTinyPlanet());
    878             }
    879             LoadHighresBitmapTask highresLoad = new LoadHighresBitmapTask();
    880             highresLoad.execute();
    881             MasterImage.getImage().warnListeners();
    882             super.onPostExecute(result);
    883         }
    884 
    885     }
    886 
    887     private void clearGalleryBitmapPool() {
    888         (new AsyncTask<Void, Void, Void>() {
    889             @Override
    890             protected Void doInBackground(Void... params) {
    891                 // Free memory held in Gallery's Bitmap pool.  May be O(n) for n bitmaps.
    892                 GalleryBitmapPool.getInstance().clear();
    893                 return null;
    894             }
    895         }).execute();
    896     }
    897 
    898     @Override
    899     protected void onDestroy() {
    900         if (mLoadBitmapTask != null) {
    901             mLoadBitmapTask.cancel(false);
    902         }
    903         mUserPresetsManager.close();
    904         doUnbindService();
    905         super.onDestroy();
    906     }
    907 
    908     // TODO: find a more robust way of handling image size selection
    909     // for high screen densities.
    910     private int getScreenImageSize() {
    911         DisplayMetrics outMetrics = new DisplayMetrics();
    912         getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
    913         return Math.max(outMetrics.heightPixels, outMetrics.widthPixels);
    914     }
    915 
    916     private void showSavingProgress(String albumName) {
    917         ProgressDialog progress;
    918         if (mSavingProgressDialog != null) {
    919             progress = mSavingProgressDialog.get();
    920             if (progress != null) {
    921                 progress.show();
    922                 return;
    923             }
    924         }
    925         // TODO: Allow cancellation of the saving process
    926         String progressText;
    927         if (albumName == null) {
    928             progressText = getString(R.string.saving_image);
    929         } else {
    930             progressText = getString(R.string.filtershow_saving_image, albumName);
    931         }
    932         progress = ProgressDialog.show(this, "", progressText, true, false);
    933         mSavingProgressDialog = new WeakReference<ProgressDialog>(progress);
    934     }
    935 
    936     private void hideSavingProgress() {
    937         if (mSavingProgressDialog != null) {
    938             ProgressDialog progress = mSavingProgressDialog.get();
    939             if (progress != null)
    940                 progress.dismiss();
    941         }
    942     }
    943 
    944     public void completeSaveImage(Uri saveUri) {
    945         if (mSharingImage && mSharedOutputFile != null) {
    946             // Image saved, we unblock the content provider
    947             Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
    948                     Uri.encode(mSharedOutputFile.getAbsolutePath()));
    949             ContentValues values = new ContentValues();
    950             values.put(SharedImageProvider.PREPARE, false);
    951             getContentResolver().insert(uri, values);
    952         }
    953         setResult(RESULT_OK, new Intent().setData(saveUri));
    954         hideSavingProgress();
    955         finish();
    956     }
    957 
    958     @Override
    959     public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) {
    960         // First, let's tell the SharedImageProvider that it will need to wait
    961         // for the image
    962         Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
    963                 Uri.encode(mSharedOutputFile.getAbsolutePath()));
    964         ContentValues values = new ContentValues();
    965         values.put(SharedImageProvider.PREPARE, true);
    966         getContentResolver().insert(uri, values);
    967         mSharingImage = true;
    968 
    969         // Process and save the image in the background.
    970         showSavingProgress(null);
    971         mImageShow.saveImage(this, mSharedOutputFile);
    972         return true;
    973     }
    974 
    975     private Intent getDefaultShareIntent() {
    976         Intent intent = new Intent(Intent.ACTION_SEND);
    977         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
    978         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    979         intent.setType(SharedImageProvider.MIME_TYPE);
    980         mSharedOutputFile = SaveImage.getNewFile(this, MasterImage.getImage().getUri());
    981         Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
    982                 Uri.encode(mSharedOutputFile.getAbsolutePath()));
    983         intent.putExtra(Intent.EXTRA_STREAM, uri);
    984         return intent;
    985     }
    986 
    987     @Override
    988     public boolean onCreateOptionsMenu(Menu menu) {
    989         getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu);
    990         MenuItem showState = menu.findItem(R.id.showImageStateButton);
    991         if (mShowingImageStatePanel) {
    992             showState.setTitle(R.string.hide_imagestate_panel);
    993         } else {
    994             showState.setTitle(R.string.show_imagestate_panel);
    995         }
    996         mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share)
    997                 .getActionProvider();
    998         mShareActionProvider.setShareIntent(getDefaultShareIntent());
    999         mShareActionProvider.setOnShareTargetSelectedListener(this);
   1000         mMenu = menu;
   1001         setupMenu();
   1002         return true;
   1003     }
   1004 
   1005     private void setupMenu(){
   1006         if (mMenu == null || mMasterImage == null) {
   1007             return;
   1008         }
   1009         MenuItem undoItem = mMenu.findItem(R.id.undoButton);
   1010         MenuItem redoItem = mMenu.findItem(R.id.redoButton);
   1011         MenuItem resetItem = mMenu.findItem(R.id.resetHistoryButton);
   1012         MenuItem printItem = mMenu.findItem(R.id.printButton);
   1013         if (!PrintHelper.systemSupportsPrint()) {
   1014             printItem.setVisible(false);
   1015         }
   1016         mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
   1017     }
   1018 
   1019     @Override
   1020     public void onPause() {
   1021         super.onPause();
   1022         if (mShareActionProvider != null) {
   1023             mShareActionProvider.setOnShareTargetSelectedListener(null);
   1024         }
   1025     }
   1026 
   1027     @Override
   1028     public void onResume() {
   1029         super.onResume();
   1030         if (mShareActionProvider != null) {
   1031             mShareActionProvider.setOnShareTargetSelectedListener(this);
   1032         }
   1033     }
   1034 
   1035     @Override
   1036     public boolean onOptionsItemSelected(MenuItem item) {
   1037         switch (item.getItemId()) {
   1038             case R.id.undoButton: {
   1039                 HistoryManager adapter = mMasterImage.getHistory();
   1040                 int position = adapter.undo();
   1041                 mMasterImage.onHistoryItemClick(position);
   1042                 backToMain();
   1043                 invalidateViews();
   1044                 return true;
   1045             }
   1046             case R.id.redoButton: {
   1047                 HistoryManager adapter = mMasterImage.getHistory();
   1048                 int position = adapter.redo();
   1049                 mMasterImage.onHistoryItemClick(position);
   1050                 invalidateViews();
   1051                 return true;
   1052             }
   1053             case R.id.resetHistoryButton: {
   1054                 resetHistory();
   1055                 return true;
   1056             }
   1057             case R.id.showImageStateButton: {
   1058                 toggleImageStatePanel();
   1059                 return true;
   1060             }
   1061             case R.id.exportFlattenButton: {
   1062                 showExportOptionsDialog();
   1063                 return true;
   1064             }
   1065             case android.R.id.home: {
   1066                 saveImage();
   1067                 return true;
   1068             }
   1069             case R.id.manageUserPresets: {
   1070                 manageUserPresets();
   1071                 return true;
   1072             }
   1073             case R.id.showInfoPanel: {
   1074                 toggleInformationPanel();
   1075                 return true;
   1076             }
   1077             case R.id.printButton: {
   1078                 print();
   1079                 return true;
   1080             }
   1081         }
   1082         return false;
   1083     }
   1084 
   1085     public void print() {
   1086         Bitmap bitmap = MasterImage.getImage().getHighresImage();
   1087         PrintHelper printer = new PrintHelper(this);
   1088         printer.printBitmap("ImagePrint", bitmap);
   1089     }
   1090 
   1091     public void addNewPreset() {
   1092         DialogFragment dialog = new PresetManagementDialog();
   1093         dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
   1094     }
   1095 
   1096     private void manageUserPresets() {
   1097         DialogFragment dialog = new PresetManagementDialog();
   1098         dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
   1099     }
   1100 
   1101     private void showExportOptionsDialog() {
   1102         DialogFragment dialog = new ExportDialog();
   1103         dialog.show(getSupportFragmentManager(), "ExportDialogFragment");
   1104     }
   1105 
   1106     public void updateUserPresetsFromAdapter(UserPresetsAdapter adapter) {
   1107         ArrayList<FilterUserPresetRepresentation> representations =
   1108                 adapter.getDeletedRepresentations();
   1109         for (FilterUserPresetRepresentation representation : representations) {
   1110             deletePreset(representation.getId());
   1111         }
   1112         ArrayList<FilterUserPresetRepresentation> changedRepresentations =
   1113                 adapter.getChangedRepresentations();
   1114         for (FilterUserPresetRepresentation representation : changedRepresentations) {
   1115             updatePreset(representation);
   1116         }
   1117         adapter.clearDeletedRepresentations();
   1118         adapter.clearChangedRepresentations();
   1119         loadUserPresets();
   1120     }
   1121 
   1122     public void loadUserPresets() {
   1123         mUserPresetsManager.load();
   1124         updateUserPresetsFromManager();
   1125     }
   1126 
   1127     public void updateUserPresetsFromManager() {
   1128         ArrayList<FilterUserPresetRepresentation> presets = mUserPresetsManager.getRepresentations();
   1129         if (presets == null) {
   1130             return;
   1131         }
   1132         if (mCategoryLooksAdapter != null) {
   1133             fillLooks();
   1134         }
   1135         if (presets.size() > 0) {
   1136             mCategoryLooksAdapter.add(new Action(this, Action.SPACER));
   1137         }
   1138         mUserPresetsAdapter.clear();
   1139         for (int i = 0; i < presets.size(); i++) {
   1140             FilterUserPresetRepresentation representation = presets.get(i);
   1141             mCategoryLooksAdapter.add(
   1142                     new Action(this, representation, Action.FULL_VIEW, true));
   1143             mUserPresetsAdapter.add(new Action(this, representation, Action.FULL_VIEW));
   1144         }
   1145         if (presets.size() > 0) {
   1146             mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
   1147         }
   1148         mCategoryLooksAdapter.notifyDataSetChanged();
   1149         mCategoryLooksAdapter.notifyDataSetInvalidated();
   1150     }
   1151 
   1152     public void saveCurrentImagePreset(String name) {
   1153         mUserPresetsManager.save(MasterImage.getImage().getPreset(), name);
   1154     }
   1155 
   1156     private void deletePreset(int id) {
   1157         mUserPresetsManager.delete(id);
   1158     }
   1159 
   1160     private void updatePreset(FilterUserPresetRepresentation representation) {
   1161         mUserPresetsManager.update(representation);
   1162     }
   1163 
   1164     public void enableSave(boolean enable) {
   1165         if (mSaveButton != null) {
   1166             mSaveButton.setEnabled(enable);
   1167         }
   1168     }
   1169 
   1170     private void fillLooks() {
   1171         FiltersManager filtersManager = FiltersManager.getManager();
   1172         ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getLooks();
   1173 
   1174         if (mCategoryLooksAdapter != null) {
   1175             mCategoryLooksAdapter.clear();
   1176         }
   1177         mCategoryLooksAdapter = new CategoryAdapter(this);
   1178         int verticalItemHeight = (int) getResources().getDimension(R.dimen.action_item_height);
   1179         mCategoryLooksAdapter.setItemHeight(verticalItemHeight);
   1180         for (FilterRepresentation representation : filtersRepresentations) {
   1181             mCategoryLooksAdapter.add(new Action(this, representation, Action.FULL_VIEW));
   1182         }
   1183         if (mUserPresetsManager.getRepresentations() == null
   1184             || mUserPresetsManager.getRepresentations().size() == 0) {
   1185             mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
   1186         }
   1187 
   1188         Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
   1189         if (panel != null) {
   1190             if (panel instanceof MainPanel) {
   1191                 MainPanel mainPanel = (MainPanel) panel;
   1192                 mainPanel.loadCategoryLookPanel(true);
   1193             }
   1194         }
   1195     }
   1196 
   1197     public void setDefaultPreset() {
   1198         // Default preset (original)
   1199         ImagePreset preset = new ImagePreset(); // empty
   1200         mMasterImage.setPreset(preset, preset.getLastRepresentation(), true);
   1201     }
   1202 
   1203     // //////////////////////////////////////////////////////////////////////////////
   1204     // Some utility functions
   1205     // TODO: finish the cleanup.
   1206 
   1207     public void invalidateViews() {
   1208         for (ImageShow views : mImageViews) {
   1209             views.updateImage();
   1210         }
   1211     }
   1212 
   1213     public void hideImageViews() {
   1214         for (View view : mImageViews) {
   1215             view.setVisibility(View.GONE);
   1216         }
   1217         mEditorPlaceHolder.hide();
   1218     }
   1219 
   1220     // //////////////////////////////////////////////////////////////////////////////
   1221     // imageState panel...
   1222 
   1223     public void toggleImageStatePanel() {
   1224         invalidateOptionsMenu();
   1225         mShowingImageStatePanel = !mShowingImageStatePanel;
   1226         Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
   1227         if (panel != null) {
   1228             if (panel instanceof EditorPanel) {
   1229                 EditorPanel editorPanel = (EditorPanel) panel;
   1230                 editorPanel.showImageStatePanel(mShowingImageStatePanel);
   1231             } else if (panel instanceof MainPanel) {
   1232                 MainPanel mainPanel = (MainPanel) panel;
   1233                 mainPanel.showImageStatePanel(mShowingImageStatePanel);
   1234             }
   1235         }
   1236     }
   1237 
   1238     public void toggleVersionsPanel() {
   1239         mShowingVersionsPanel = !mShowingVersionsPanel;
   1240         Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
   1241         if (panel != null && panel instanceof MainPanel) {
   1242             MainPanel mainPanel = (MainPanel) panel;
   1243             mainPanel.loadCategoryVersionsPanel();
   1244         }
   1245     }
   1246 
   1247     @Override
   1248     public void onConfigurationChanged(Configuration newConfig)
   1249     {
   1250         super.onConfigurationChanged(newConfig);
   1251 
   1252         setDefaultValues();
   1253         if (mMasterImage == null) {
   1254             return;
   1255         }
   1256         loadXML();
   1257         fillCategories();
   1258         loadMainPanel();
   1259 
   1260         if (mCurrentMenu != null) {
   1261             mCurrentMenu.dismiss();
   1262             mCurrentMenu = null;
   1263         }
   1264         if (mCurrentDialog != null) {
   1265             mCurrentDialog.dismiss();
   1266             mCurrentDialog = null;
   1267         }
   1268         // mLoadBitmapTask==null implies you have looked at the intent
   1269         if (!mShowingTinyPlanet && (mLoadBitmapTask == null)) {
   1270             mCategoryFiltersAdapter.removeTinyPlanet();
   1271         }
   1272         stopLoadingIndicator();
   1273     }
   1274 
   1275     public void setupMasterImage() {
   1276 
   1277         HistoryManager historyManager = new HistoryManager();
   1278         StateAdapter imageStateAdapter = new StateAdapter(this, 0);
   1279         MasterImage.reset();
   1280         mMasterImage = MasterImage.getImage();
   1281         mMasterImage.setHistoryManager(historyManager);
   1282         mMasterImage.setStateAdapter(imageStateAdapter);
   1283         mMasterImage.setActivity(this);
   1284 
   1285         if (Runtime.getRuntime().maxMemory() > LIMIT_SUPPORTS_HIGHRES) {
   1286             mMasterImage.setSupportsHighRes(true);
   1287         } else {
   1288             mMasterImage.setSupportsHighRes(false);
   1289         }
   1290     }
   1291 
   1292     void resetHistory() {
   1293         HistoryManager adapter = mMasterImage.getHistory();
   1294         adapter.reset();
   1295         HistoryItem historyItem = adapter.getItem(0);
   1296         ImagePreset original = null;
   1297         if (RESET_TO_LOADED) {
   1298             original = new ImagePreset(historyItem.getImagePreset());
   1299         } else {
   1300             original = new ImagePreset();
   1301         }
   1302         FilterRepresentation rep = null;
   1303         if (historyItem != null) {
   1304             rep = historyItem.getFilterRepresentation();
   1305         }
   1306         mMasterImage.setPreset(original, rep, true);
   1307         invalidateViews();
   1308         backToMain();
   1309     }
   1310 
   1311     public void showDefaultImageView() {
   1312         mEditorPlaceHolder.hide();
   1313         mImageShow.setVisibility(View.VISIBLE);
   1314         MasterImage.getImage().setCurrentFilter(null);
   1315         MasterImage.getImage().setCurrentFilterRepresentation(null);
   1316     }
   1317 
   1318     public void backToMain() {
   1319         Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
   1320         if (currentPanel instanceof MainPanel) {
   1321             return;
   1322         }
   1323         loadMainPanel();
   1324         showDefaultImageView();
   1325     }
   1326 
   1327     @Override
   1328     public void onBackPressed() {
   1329         Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
   1330         if (currentPanel instanceof MainPanel) {
   1331             if (!mImageShow.hasModifications()) {
   1332                 done();
   1333             } else {
   1334                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
   1335                 builder.setMessage(R.string.unsaved).setTitle(R.string.save_before_exit);
   1336                 builder.setPositiveButton(R.string.save_and_exit, new DialogInterface.OnClickListener() {
   1337                     @Override
   1338                     public void onClick(DialogInterface dialog, int id) {
   1339                         saveImage();
   1340                     }
   1341                 });
   1342                 builder.setNegativeButton(R.string.exit, new DialogInterface.OnClickListener() {
   1343                     @Override
   1344                     public void onClick(DialogInterface dialog, int id) {
   1345                         done();
   1346                     }
   1347                 });
   1348                 builder.show();
   1349             }
   1350         } else {
   1351             backToMain();
   1352         }
   1353     }
   1354 
   1355     public void cannotLoadImage() {
   1356         Toast.makeText(this, R.string.cannot_load_image, Toast.LENGTH_SHORT).show();
   1357         finish();
   1358     }
   1359 
   1360     // //////////////////////////////////////////////////////////////////////////////
   1361 
   1362     public float getPixelsFromDip(float value) {
   1363         Resources r = getResources();
   1364         return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
   1365                 r.getDisplayMetrics());
   1366     }
   1367 
   1368     @Override
   1369     public void onItemClick(AdapterView<?> parent, View view, int position,
   1370             long id) {
   1371         mMasterImage.onHistoryItemClick(position);
   1372         invalidateViews();
   1373     }
   1374 
   1375     public void pickImage() {
   1376         Intent intent = new Intent();
   1377         intent.setType("image/*");
   1378         intent.setAction(Intent.ACTION_GET_CONTENT);
   1379         startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
   1380                 SELECT_PICTURE);
   1381     }
   1382 
   1383     @Override
   1384     public void onActivityResult(int requestCode, int resultCode, Intent data) {
   1385         if (resultCode == RESULT_OK) {
   1386             if (requestCode == SELECT_PICTURE) {
   1387                 Uri selectedImageUri = data.getData();
   1388                 startLoadBitmap(selectedImageUri);
   1389             }
   1390         }
   1391     }
   1392 
   1393 
   1394     public void saveImage() {
   1395         if (mImageShow.hasModifications()) {
   1396             // Get the name of the album, to which the image will be saved
   1397             File saveDir = SaveImage.getFinalSaveDirectory(this, mSelectedImageUri);
   1398             int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
   1399             String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
   1400             showSavingProgress(albumName);
   1401             mImageShow.saveImage(this, null);
   1402         } else {
   1403             done();
   1404         }
   1405     }
   1406 
   1407 
   1408     public void done() {
   1409         hideSavingProgress();
   1410         if (mLoadBitmapTask != null) {
   1411             mLoadBitmapTask.cancel(false);
   1412         }
   1413         finish();
   1414     }
   1415 
   1416     private void extractXMPData() {
   1417         XMresults res = XmpPresets.extractXMPData(
   1418                 getBaseContext(), mMasterImage, getIntent().getData());
   1419         if (res == null)
   1420             return;
   1421 
   1422         mOriginalImageUri = res.originalimage;
   1423         mOriginalPreset = res.preset;
   1424     }
   1425 
   1426     public Uri getSelectedImageUri() {
   1427         return mSelectedImageUri;
   1428     }
   1429 
   1430     public void setHandlesSwipeForView(View view, float startX, float startY) {
   1431         if (view != null) {
   1432             mHandlingSwipeButton = true;
   1433         } else {
   1434             mHandlingSwipeButton = false;
   1435         }
   1436         mHandledSwipeView = view;
   1437         int[] location = new int[2];
   1438         view.getLocationInWindow(location);
   1439         mSwipeStartX = location[0] + startX;
   1440         mSwipeStartY = location[1] + startY;
   1441     }
   1442 
   1443     public boolean dispatchTouchEvent (MotionEvent ev) {
   1444         if (mHandlingSwipeButton) {
   1445             int direction = CategoryView.HORIZONTAL;
   1446             if (mHandledSwipeView instanceof CategoryView) {
   1447                 direction = ((CategoryView) mHandledSwipeView).getOrientation();
   1448             }
   1449             if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
   1450                 float delta = ev.getY() - mSwipeStartY;
   1451                 float distance = mHandledSwipeView.getHeight();
   1452                 if (direction == CategoryView.VERTICAL) {
   1453                     delta = ev.getX() - mSwipeStartX;
   1454                     mHandledSwipeView.setTranslationX(delta);
   1455                     distance = mHandledSwipeView.getWidth();
   1456                 } else {
   1457                     mHandledSwipeView.setTranslationY(delta);
   1458                 }
   1459                 delta = Math.abs(delta);
   1460                 float transparency = Math.min(1, delta / distance);
   1461                 mHandledSwipeView.setAlpha(1.f - transparency);
   1462                 mHandledSwipeViewLastDelta = delta;
   1463             }
   1464             if (ev.getActionMasked() == MotionEvent.ACTION_CANCEL
   1465                     || ev.getActionMasked() == MotionEvent.ACTION_UP) {
   1466                 mHandledSwipeView.setTranslationX(0);
   1467                 mHandledSwipeView.setTranslationY(0);
   1468                 mHandledSwipeView.setAlpha(1.f);
   1469                 mHandlingSwipeButton = false;
   1470                 float distance = mHandledSwipeView.getHeight();
   1471                 if (direction == CategoryView.VERTICAL) {
   1472                     distance = mHandledSwipeView.getWidth();
   1473                 }
   1474                 if (mHandledSwipeViewLastDelta > distance) {
   1475                     ((SwipableView) mHandledSwipeView).delete();
   1476                 }
   1477             }
   1478             return true;
   1479         }
   1480         return super.dispatchTouchEvent(ev);
   1481     }
   1482 
   1483     public Point mHintTouchPoint = new Point();
   1484 
   1485     public Point hintTouchPoint(View view) {
   1486         int location[] = new int[2];
   1487         view.getLocationOnScreen(location);
   1488         int x = mHintTouchPoint.x - location[0];
   1489         int y = mHintTouchPoint.y - location[1];
   1490         return new Point(x, y);
   1491     }
   1492 
   1493     public void startTouchAnimation(View target, float x, float y) {
   1494         final CategorySelected hint =
   1495                 (CategorySelected) findViewById(R.id.categorySelectedIndicator);
   1496         int location[] = new int[2];
   1497         target.getLocationOnScreen(location);
   1498         mHintTouchPoint.x = (int) (location[0] + x);
   1499         mHintTouchPoint.y = (int) (location[1] + y);
   1500         int locationHint[] = new int[2];
   1501         ((View)hint.getParent()).getLocationOnScreen(locationHint);
   1502         int dx = (int) (x - (hint.getWidth())/2);
   1503         int dy = (int) (y - (hint.getHeight())/2);
   1504         hint.setTranslationX(location[0] - locationHint[0] + dx);
   1505         hint.setTranslationY(location[1] - locationHint[1] + dy);
   1506         hint.setVisibility(View.VISIBLE);
   1507         hint.animate().scaleX(2).scaleY(2).alpha(0).withEndAction(new Runnable() {
   1508             @Override
   1509             public void run() {
   1510                 hint.setVisibility(View.INVISIBLE);
   1511                 hint.setScaleX(1);
   1512                 hint.setScaleY(1);
   1513                 hint.setAlpha(1);
   1514             }
   1515         });
   1516     }
   1517 }
   1518