2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.gallery3d.filtershow;
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 android.support.v4.app.DialogFragment;
46 import android.support.v4.app.Fragment;
47 import android.support.v4.app.FragmentActivity;
48 import android.support.v4.app.FragmentTransaction;
49 import android.util.DisplayMetrics;
50 import android.util.Log;
51 import android.util.TypedValue;
52 import android.view.Menu;
53 import android.view.MenuItem;
54 import android.view.MotionEvent;
55 import android.view.View;
56 import android.view.View.OnClickListener;
57 import android.view.ViewPropertyAnimator;
58 import android.view.WindowManager;
59 import android.widget.AdapterView;
60 import android.widget.AdapterView.OnItemClickListener;
61 import android.widget.FrameLayout;
62 import android.widget.ShareActionProvider;
63 import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
64 import android.widget.Spinner;
65 import android.widget.Toast;
67 import com.android.gallery3d.R;
68 import com.android.gallery3d.app.PhotoPage;
69 import com.android.gallery3d.data.LocalAlbum;
70 import com.android.gallery3d.filtershow.cache.ImageLoader;
71 import com.android.gallery3d.filtershow.category.Action;
72 import com.android.gallery3d.filtershow.category.CategoryAdapter;
73 import com.android.gallery3d.filtershow.category.CategorySelected;
74 import com.android.gallery3d.filtershow.category.CategoryView;
75 import com.android.gallery3d.filtershow.category.MainPanel;
76 import com.android.gallery3d.filtershow.category.SwipableView;
77 import com.android.gallery3d.filtershow.data.UserPresetsManager;
78 import com.android.gallery3d.filtershow.editors.BasicEditor;
79 import com.android.gallery3d.filtershow.editors.Editor;
80 import com.android.gallery3d.filtershow.editors.EditorChanSat;
81 import com.android.gallery3d.filtershow.editors.EditorColorBorder;
82 import com.android.gallery3d.filtershow.editors.EditorCrop;
83 import com.android.gallery3d.filtershow.editors.EditorDraw;
84 import com.android.gallery3d.filtershow.editors.EditorGrad;
85 import com.android.gallery3d.filtershow.editors.EditorManager;
86 import com.android.gallery3d.filtershow.editors.EditorMirror;
87 import com.android.gallery3d.filtershow.editors.EditorPanel;
88 import com.android.gallery3d.filtershow.editors.EditorRedEye;
89 import com.android.gallery3d.filtershow.editors.EditorRotate;
90 import com.android.gallery3d.filtershow.editors.EditorStraighten;
91 import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
92 import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
93 import com.android.gallery3d.filtershow.filters.FilterDrawRepresentation;
94 import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation;
95 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
96 import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation;
97 import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation;
98 import com.android.gallery3d.filtershow.filters.FiltersManager;
99 import com.android.gallery3d.filtershow.filters.ImageFilter;
100 import com.android.gallery3d.filtershow.history.HistoryItem;
101 import com.android.gallery3d.filtershow.history.HistoryManager;
102 import com.android.gallery3d.filtershow.imageshow.ImageShow;
103 import com.android.gallery3d.filtershow.imageshow.MasterImage;
104 import com.android.gallery3d.filtershow.imageshow.Spline;
105 import com.android.gallery3d.filtershow.info.InfoPanel;
106 import com.android.gallery3d.filtershow.pipeline.CachingPipeline;
107 import com.android.gallery3d.filtershow.pipeline.ImagePreset;
108 import com.android.gallery3d.filtershow.pipeline.ProcessingService;
109 import com.android.gallery3d.filtershow.presets.PresetManagementDialog;
110 import com.android.gallery3d.filtershow.presets.UserPresetsAdapter;
111 import com.android.gallery3d.filtershow.provider.SharedImageProvider;
112 import com.android.gallery3d.filtershow.state.StateAdapter;
113 import com.android.gallery3d.filtershow.tools.SaveImage;
114 import com.android.gallery3d.filtershow.tools.XmpPresets;
115 import com.android.gallery3d.filtershow.tools.XmpPresets.XMresults;
116 import com.android.gallery3d.filtershow.ui.ExportDialog;
117 import com.android.gallery3d.filtershow.ui.FramedTextButton;
118 import com.android.gallery3d.util.GalleryUtils;
119 import com.android.gallery3d.util.PrintJob;
120 import com.android.photos.data.GalleryBitmapPool;
123 import java.io.FileDescriptor;
124 import java.io.FileOutputStream;
125 import java.lang.ref.WeakReference;
126 import java.util.ArrayList;
127 import java.util.Vector;
129 public class FilterShowActivity extends FragmentActivity implements OnItemClickListener,
130 OnShareTargetSelectedListener, DialogInterface.OnShowListener,
131 DialogInterface.OnDismissListener{
133 private String mAction = "";
134 MasterImage mMasterImage = null;
136 private static final long LIMIT_SUPPORTS_HIGHRES = 134217728; // 128Mb
138 public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
139 public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
140 private ImageShow mImageShow = null;
142 private View mSaveButton = null;
144 private EditorPlaceHolder mEditorPlaceHolder = new EditorPlaceHolder(this);
145 private Editor mCurrentEditor = null;
147 private static final int SELECT_PICTURE = 1;
148 private static final String LOGTAG = "FilterShowActivity";
150 private boolean mShowingTinyPlanet = false;
151 private boolean mShowingImageStatePanel = false;
152 private boolean mShowingVersionsPanel = false;
154 private final Vector<ImageShow> mImageViews = new Vector<ImageShow>();
156 private ShareActionProvider mShareActionProvider;
157 private File mSharedOutputFile = null;
159 private boolean mSharingImage = false;
161 private WeakReference<ProgressDialog> mSavingProgressDialog;
163 private LoadBitmapTask mLoadBitmapTask;
165 private Uri mOriginalImageUri = null;
166 private ImagePreset mOriginalPreset = null;
168 private Uri mSelectedImageUri = null;
170 private ArrayList<Action> mActions = new ArrayList<Action>();
171 private UserPresetsManager mUserPresetsManager = null;
172 private UserPresetsAdapter mUserPresetsAdapter = null;
173 private CategoryAdapter mCategoryLooksAdapter = null;
174 private CategoryAdapter mCategoryBordersAdapter = null;
175 private CategoryAdapter mCategoryGeometryAdapter = null;
176 private CategoryAdapter mCategoryFiltersAdapter = null;
177 private CategoryAdapter mCategoryVersionsAdapter = null;
178 private int mCurrentPanel = MainPanel.LOOKS;
179 private Vector<FilterUserPresetRepresentation> mVersions =
180 new Vector<FilterUserPresetRepresentation>();
181 private int mVersionsCounter = 0;
183 private boolean mHandlingSwipeButton = false;
184 private View mHandledSwipeView = null;
185 private float mHandledSwipeViewLastDelta = 0;
186 private float mSwipeStartX = 0;
187 private float mSwipeStartY = 0;
189 private ProcessingService mBoundService;
190 private boolean mIsBound = false;
192 private DialogInterface mCurrentDialog = null;
193 private boolean mLoadingVisible = true;
195 public ProcessingService getProcessingService() {
196 return mBoundService;
199 public boolean isSimpleEditAction() {
200 return !PhotoPage.ACTION_NEXTGEN_EDIT.equalsIgnoreCase(mAction);
203 private ServiceConnection mConnection = new ServiceConnection() {
205 public void onServiceConnected(ComponentName className, IBinder service) {
207 * This is called when the connection with the service has been
208 * established, giving us the service object we can use to
209 * interact with the service. Because we have bound to a explicit
210 * service that we know is running in our own process, we can
211 * cast its IBinder to a concrete class and directly access it.
213 mBoundService = ((ProcessingService.LocalBinder)service).getService();
214 mBoundService.setFiltershowActivity(FilterShowActivity.this);
215 mBoundService.onStart();
219 public void onServiceDisconnected(ComponentName className) {
221 * This is called when the connection with the service has been
222 * unexpectedly disconnected -- that is, its process crashed.
223 * Because it is running in our same process, we should never
226 mBoundService = null;
230 void doBindService() {
232 * Establish a connection with the service. We use an explicit
233 * class name because we want a specific service implementation that
234 * we know will be running in our own process (and thus won't be
235 * supporting component replacement by other applications).
237 bindService(new Intent(FilterShowActivity.this, ProcessingService.class),
238 mConnection, Context.BIND_AUTO_CREATE);
242 void doUnbindService() {
244 // Detach our existing connection.
245 unbindService(mConnection);
250 public void updateUIAfterServiceStarted() {
251 MasterImage.setMaster(mMasterImage);
252 ImageFilter.setActivityForMemoryToasts(this);
253 mUserPresetsManager = new UserPresetsManager(this);
254 mUserPresetsAdapter = new UserPresetsAdapter(this);
260 getWindow().setBackgroundDrawable(new ColorDrawable(0));
270 public void onCreate(Bundle savedInstanceState) {
271 super.onCreate(savedInstanceState);
273 boolean onlyUsePortrait = getResources().getBoolean(R.bool.only_use_portrait);
274 if (onlyUsePortrait) {
275 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
278 clearGalleryBitmapPool();
280 getWindow().setBackgroundDrawable(new ColorDrawable(Color.GRAY));
281 setContentView(R.layout.filtershow_splashscreen);
284 public boolean isShowingImageStatePanel() {
285 return mShowingImageStatePanel;
288 public void loadMainPanel() {
289 if (findViewById(R.id.main_panel_container) == null) {
292 MainPanel panel = new MainPanel();
293 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
294 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
295 transaction.commit();
298 public void loadEditorPanel(FilterRepresentation representation,
299 final Editor currentEditor) {
300 if (representation.getEditorId() == ImageOnlyEditor.ID) {
301 currentEditor.reflectCurrentFilter();
304 final int currentId = currentEditor.getID();
305 Runnable showEditor = new Runnable() {
308 EditorPanel panel = new EditorPanel();
309 panel.setEditor(currentId);
310 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
311 transaction.remove(getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG));
312 transaction.replace(R.id.main_panel_container, panel, MainPanel.FRAGMENT_TAG);
313 transaction.commit();
316 Fragment main = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
317 boolean doAnimation = false;
318 if (mShowingImageStatePanel
319 && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
322 if (doAnimation && main != null && main instanceof MainPanel) {
323 MainPanel mainPanel = (MainPanel) main;
324 View container = mainPanel.getView().findViewById(R.id.category_panel_container);
325 View bottom = mainPanel.getView().findViewById(R.id.bottom_panel);
326 int panelHeight = container.getHeight() + bottom.getHeight();
327 ViewPropertyAnimator anim = mainPanel.getView().animate();
328 anim.translationY(panelHeight).start();
329 final Handler handler = new Handler();
330 handler.postDelayed(showEditor, anim.getDuration());
336 public void toggleInformationPanel() {
337 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
338 transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
340 InfoPanel panel = new InfoPanel();
341 panel.show(transaction, InfoPanel.FRAGMENT_TAG);
344 private void loadXML() {
345 setContentView(R.layout.filtershow_activity);
347 ActionBar actionBar = getActionBar();
348 actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
349 actionBar.setCustomView(R.layout.filtershow_actionbar);
350 actionBar.setBackgroundDrawable(new ColorDrawable(
351 getResources().getColor(R.color.background_screen)));
353 mSaveButton = actionBar.getCustomView();
354 mSaveButton.setOnClickListener(new OnClickListener() {
356 public void onClick(View view) {
361 mImageShow = (ImageShow) findViewById(R.id.imageShow);
362 mImageViews.add(mImageShow);
366 mEditorPlaceHolder.hide();
372 public void fillCategories() {
381 public void setupStatePanel() {
382 MasterImage.getImage().setHistoryManager(mMasterImage.getHistory());
385 private void fillVersions() {
386 if (mCategoryVersionsAdapter != null) {
387 mCategoryVersionsAdapter.clear();
389 mCategoryVersionsAdapter = new CategoryAdapter(this);
390 mCategoryVersionsAdapter.setShowAddButton(true);
393 public void registerAction(Action action) {
394 if (mActions.contains(action)) {
397 mActions.add(action);
400 private void loadActions() {
401 for (int i = 0; i < mActions.size(); i++) {
402 Action action = mActions.get(i);
403 action.setImageFrame(new Rect(0, 0, 96, 96), 0);
407 public void updateVersions() {
408 mCategoryVersionsAdapter.clear();
409 FilterUserPresetRepresentation originalRep = new FilterUserPresetRepresentation(
410 getString(R.string.filtershow_version_original), new ImagePreset(), -1);
411 mCategoryVersionsAdapter.add(
412 new Action(this, originalRep, Action.FULL_VIEW));
413 ImagePreset current = new ImagePreset(MasterImage.getImage().getPreset());
414 FilterUserPresetRepresentation currentRep = new FilterUserPresetRepresentation(
415 getString(R.string.filtershow_version_current), current, -1);
416 mCategoryVersionsAdapter.add(
417 new Action(this, currentRep, Action.FULL_VIEW));
418 if (mVersions.size() > 0) {
419 mCategoryVersionsAdapter.add(new Action(this, Action.SPACER));
421 for (FilterUserPresetRepresentation rep : mVersions) {
422 mCategoryVersionsAdapter.add(
423 new Action(this, rep, Action.FULL_VIEW, true));
425 mCategoryVersionsAdapter.notifyDataSetInvalidated();
428 public void addCurrentVersion() {
429 ImagePreset current = new ImagePreset(MasterImage.getImage().getPreset());
431 FilterUserPresetRepresentation rep = new FilterUserPresetRepresentation(
432 "" + mVersionsCounter, current, -1);
437 public void removeVersion(Action action) {
438 mVersions.remove(action.getRepresentation());
442 public void removeLook(Action action) {
443 FilterUserPresetRepresentation rep =
444 (FilterUserPresetRepresentation) action.getRepresentation();
448 mUserPresetsManager.delete(rep.getId());
449 updateUserPresetsFromManager();
452 private void fillEffects() {
453 FiltersManager filtersManager = FiltersManager.getManager();
454 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getEffects();
455 if (mCategoryFiltersAdapter != null) {
456 mCategoryFiltersAdapter.clear();
458 mCategoryFiltersAdapter = new CategoryAdapter(this);
459 for (FilterRepresentation representation : filtersRepresentations) {
460 if (representation.getTextId() != 0) {
461 representation.setName(getString(representation.getTextId()));
463 mCategoryFiltersAdapter.add(new Action(this, representation));
467 private void fillTools() {
468 FiltersManager filtersManager = FiltersManager.getManager();
469 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getTools();
470 if (mCategoryGeometryAdapter != null) {
471 mCategoryGeometryAdapter.clear();
473 mCategoryGeometryAdapter = new CategoryAdapter(this);
474 boolean found = false;
475 for (FilterRepresentation representation : filtersRepresentations) {
476 mCategoryGeometryAdapter.add(new Action(this, representation));
477 if (representation instanceof FilterDrawRepresentation) {
482 FilterRepresentation representation = new FilterDrawRepresentation();
483 Action action = new Action(this, representation);
484 action.setIsDoubleAction(true);
485 mCategoryGeometryAdapter.add(action);
489 private void processIntent() {
490 Intent intent = getIntent();
491 if (intent.getBooleanExtra(LAUNCH_FULLSCREEN, false)) {
492 getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
495 mAction = intent.getAction();
496 mSelectedImageUri = intent.getData();
497 Uri loadUri = mSelectedImageUri;
498 if (mOriginalImageUri != null) {
499 loadUri = mOriginalImageUri;
501 if (loadUri != null) {
502 startLoadBitmap(loadUri);
508 private void setupEditors() {
509 mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer));
510 EditorManager.addEditors(mEditorPlaceHolder);
511 mEditorPlaceHolder.setOldViews(mImageViews);
514 private void fillEditors() {
515 mEditorPlaceHolder.addEditor(new EditorChanSat());
516 mEditorPlaceHolder.addEditor(new EditorGrad());
517 mEditorPlaceHolder.addEditor(new EditorDraw());
518 mEditorPlaceHolder.addEditor(new EditorColorBorder());
519 mEditorPlaceHolder.addEditor(new BasicEditor());
520 mEditorPlaceHolder.addEditor(new ImageOnlyEditor());
521 mEditorPlaceHolder.addEditor(new EditorTinyPlanet());
522 mEditorPlaceHolder.addEditor(new EditorRedEye());
523 mEditorPlaceHolder.addEditor(new EditorCrop());
524 mEditorPlaceHolder.addEditor(new EditorMirror());
525 mEditorPlaceHolder.addEditor(new EditorRotate());
526 mEditorPlaceHolder.addEditor(new EditorStraighten());
529 private void setDefaultValues() {
530 Resources res = getResources();
532 // TODO: get those values from XML.
533 FramedTextButton.setTextSize((int) getPixelsFromDip(14));
534 FramedTextButton.setTrianglePadding((int) getPixelsFromDip(4));
535 FramedTextButton.setTriangleSize((int) getPixelsFromDip(10));
537 Drawable curveHandle = res.getDrawable(R.drawable.camera_crop);
538 int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size);
539 Spline.setCurveHandle(curveHandle, curveHandleSize);
540 Spline.setCurveWidth((int) getPixelsFromDip(3));
542 mOriginalImageUri = null;
545 private void startLoadBitmap(Uri uri) {
546 final View imageShow = findViewById(R.id.imageShow);
547 imageShow.setVisibility(View.INVISIBLE);
548 startLoadingIndicator();
549 mShowingTinyPlanet = false;
550 mLoadBitmapTask = new LoadBitmapTask();
551 mLoadBitmapTask.execute(uri);
554 private void fillBorders() {
555 FiltersManager filtersManager = FiltersManager.getManager();
556 ArrayList<FilterRepresentation> borders = filtersManager.getBorders();
558 for (int i = 0; i < borders.size(); i++) {
559 FilterRepresentation filter = borders.get(i);
560 filter.setName(getString(R.string.borders));
562 filter.setName(getString(R.string.none));
566 if (mCategoryBordersAdapter != null) {
567 mCategoryBordersAdapter.clear();
569 mCategoryBordersAdapter = new CategoryAdapter(this);
570 for (FilterRepresentation representation : borders) {
571 if (representation.getTextId() != 0) {
572 representation.setName(getString(representation.getTextId()));
574 mCategoryBordersAdapter.add(new Action(this, representation, Action.FULL_VIEW));
578 public UserPresetsAdapter getUserPresetsAdapter() {
579 return mUserPresetsAdapter;
582 public CategoryAdapter getCategoryLooksAdapter() {
583 return mCategoryLooksAdapter;
586 public CategoryAdapter getCategoryBordersAdapter() {
587 return mCategoryBordersAdapter;
590 public CategoryAdapter getCategoryGeometryAdapter() {
591 return mCategoryGeometryAdapter;
594 public CategoryAdapter getCategoryFiltersAdapter() {
595 return mCategoryFiltersAdapter;
598 public CategoryAdapter getCategoryVersionsAdapter() {
599 return mCategoryVersionsAdapter;
602 public void removeFilterRepresentation(FilterRepresentation filterRepresentation) {
603 if (filterRepresentation == null) {
606 ImagePreset oldPreset = MasterImage.getImage().getPreset();
607 ImagePreset copy = new ImagePreset(oldPreset);
608 copy.removeFilter(filterRepresentation);
609 MasterImage.getImage().setPreset(copy, copy.getLastRepresentation(), true);
610 if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
611 FilterRepresentation lastRepresentation = copy.getLastRepresentation();
612 MasterImage.getImage().setCurrentFilterRepresentation(lastRepresentation);
616 public void useFilterRepresentation(FilterRepresentation filterRepresentation) {
617 if (filterRepresentation == null) {
620 if (!(filterRepresentation instanceof FilterRotateRepresentation)
621 && !(filterRepresentation instanceof FilterMirrorRepresentation)
622 && MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) {
625 if (filterRepresentation instanceof FilterUserPresetRepresentation
626 || filterRepresentation instanceof FilterRotateRepresentation
627 || filterRepresentation instanceof FilterMirrorRepresentation) {
628 MasterImage.getImage().onNewLook(filterRepresentation);
630 ImagePreset oldPreset = MasterImage.getImage().getPreset();
631 ImagePreset copy = new ImagePreset(oldPreset);
632 FilterRepresentation representation = copy.getRepresentation(filterRepresentation);
633 if (representation == null) {
634 filterRepresentation = filterRepresentation.copy();
635 copy.addFilter(filterRepresentation);
637 if (filterRepresentation.allowsSingleInstanceOnly()) {
638 // Don't just update the filter representation. Centralize the
639 // logic in the addFilter(), such that we can keep "None" as
641 if (!representation.equals(filterRepresentation)) {
642 // Only do this if the filter isn't the same
643 // (state panel clicks can lead us here)
644 copy.removeFilter(representation);
645 copy.addFilter(filterRepresentation);
649 MasterImage.getImage().setPreset(copy, filterRepresentation, true);
650 MasterImage.getImage().setCurrentFilterRepresentation(filterRepresentation);
653 public void showRepresentation(FilterRepresentation representation) {
654 if (representation == null) {
658 if (representation instanceof FilterRotateRepresentation) {
659 FilterRotateRepresentation r = (FilterRotateRepresentation) representation;
662 if (representation instanceof FilterMirrorRepresentation) {
663 FilterMirrorRepresentation r = (FilterMirrorRepresentation) representation;
666 if (representation.isBooleanFilter()) {
667 ImagePreset preset = MasterImage.getImage().getPreset();
668 if (preset.getRepresentation(representation) != null) {
670 ImagePreset copy = new ImagePreset(preset);
671 copy.removeFilter(representation);
672 FilterRepresentation filterRepresentation = representation.copy();
673 MasterImage.getImage().setPreset(copy, filterRepresentation, true);
674 MasterImage.getImage().setCurrentFilterRepresentation(null);
678 useFilterRepresentation(representation);
680 // show representation
681 if (mCurrentEditor != null) {
682 mCurrentEditor.detach();
684 mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId());
685 loadEditorPanel(representation, mCurrentEditor);
688 public Editor getEditor(int editorID) {
689 return mEditorPlaceHolder.getEditor(editorID);
692 public void setCurrentPanel(int currentPanel) {
693 mCurrentPanel = currentPanel;
696 public int getCurrentPanel() {
697 return mCurrentPanel;
700 public void updateCategories() {
701 ImagePreset preset = mMasterImage.getPreset();
702 mCategoryLooksAdapter.reflectImagePreset(preset);
703 mCategoryBordersAdapter.reflectImagePreset(preset);
706 public View getMainStatePanelContainer(int id) {
707 return findViewById(id);
711 public void onShow(DialogInterface dialog) {
712 mCurrentDialog = dialog;
716 public void onDismiss(DialogInterface dialogInterface) {
717 mCurrentDialog = null;
720 private class LoadHighresBitmapTask extends AsyncTask<Void, Void, Boolean> {
722 protected Boolean doInBackground(Void... params) {
723 MasterImage master = MasterImage.getImage();
724 Rect originalBounds = master.getOriginalBounds();
725 if (master.supportsHighRes()) {
726 int highresPreviewSize = master.getOriginalBitmapLarge().getWidth() * 2;
727 if (highresPreviewSize > originalBounds.width()) {
728 highresPreviewSize = originalBounds.width();
730 Rect bounds = new Rect();
731 Bitmap originalHires = ImageLoader.loadOrientedConstrainedBitmap(master.getUri(),
732 master.getActivity(), highresPreviewSize,
733 master.getOrientation(), bounds);
734 master.setOriginalBounds(bounds);
735 master.setOriginalBitmapHighres(originalHires);
736 mBoundService.setOriginalBitmapHighres(originalHires);
737 master.warnListeners();
743 protected void onPostExecute(Boolean result) {
744 Bitmap highresBitmap = MasterImage.getImage().getOriginalBitmapHighres();
745 if (highresBitmap != null) {
746 float highResPreviewScale = (float) highresBitmap.getWidth()
747 / (float) MasterImage.getImage().getOriginalBounds().width();
748 mBoundService.setHighresPreviewScaleFactor(highResPreviewScale);
750 MasterImage.getImage().warnListeners();
754 public boolean isLoadingVisible() {
755 return mLoadingVisible;
758 public void startLoadingIndicator() {
759 final View loading = findViewById(R.id.loading);
760 mLoadingVisible = true;
761 loading.setVisibility(View.VISIBLE);
764 public void stopLoadingIndicator() {
765 final View loading = findViewById(R.id.loading);
766 loading.setVisibility(View.GONE);
767 mLoadingVisible = false;
770 private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
773 public LoadBitmapTask() {
774 mBitmapSize = getScreenImageSize();
778 protected Boolean doInBackground(Uri... params) {
779 if (!MasterImage.getImage().loadBitmap(params[0], mBitmapSize)) {
782 publishProgress(ImageLoader.queryLightCycle360(MasterImage.getImage().getActivity()));
787 protected void onProgressUpdate(Boolean... values) {
788 super.onProgressUpdate(values);
793 mShowingTinyPlanet = true;
798 protected void onPostExecute(Boolean result) {
799 MasterImage.setMaster(mMasterImage);
805 if (mOriginalImageUri != null
806 && !mOriginalImageUri.equals(mSelectedImageUri)) {
807 mOriginalImageUri = mSelectedImageUri;
808 mOriginalPreset = null;
809 Toast.makeText(FilterShowActivity.this,
810 R.string.cannot_edit_original, Toast.LENGTH_SHORT).show();
811 startLoadBitmap(mOriginalImageUri);
818 if (null == CachingPipeline.getRenderScriptContext()){
819 Log.v(LOGTAG,"RenderScript context destroyed during load");
822 final View imageShow = findViewById(R.id.imageShow);
823 imageShow.setVisibility(View.VISIBLE);
826 Bitmap largeBitmap = MasterImage.getImage().getOriginalBitmapLarge();
827 mBoundService.setOriginalBitmap(largeBitmap);
829 float previewScale = (float) largeBitmap.getWidth()
830 / (float) MasterImage.getImage().getOriginalBounds().width();
831 mBoundService.setPreviewScaleFactor(previewScale);
832 if (!mShowingTinyPlanet) {
833 mCategoryFiltersAdapter.removeTinyPlanet();
835 mCategoryLooksAdapter.imageLoaded();
836 mCategoryBordersAdapter.imageLoaded();
837 mCategoryGeometryAdapter.imageLoaded();
838 mCategoryFiltersAdapter.imageLoaded();
839 mLoadBitmapTask = null;
841 MasterImage.getImage().warnListeners();
844 if (mOriginalPreset != null) {
845 MasterImage.getImage().setLoadedPreset(mOriginalPreset);
846 MasterImage.getImage().setPreset(mOriginalPreset,
847 mOriginalPreset.getLastRepresentation(), true);
848 mOriginalPreset = null;
853 MasterImage.getImage().resetGeometryImages(true);
855 if (mAction == TINY_PLANET_ACTION) {
856 showRepresentation(mCategoryFiltersAdapter.getTinyPlanet());
858 LoadHighresBitmapTask highresLoad = new LoadHighresBitmapTask();
859 highresLoad.execute();
860 MasterImage.getImage().warnListeners();
861 super.onPostExecute(result);
866 private void clearGalleryBitmapPool() {
867 (new AsyncTask<Void, Void, Void>() {
869 protected Void doInBackground(Void... params) {
870 // Free memory held in Gallery's Bitmap pool. May be O(n) for n bitmaps.
871 GalleryBitmapPool.getInstance().clear();
878 protected void onDestroy() {
879 if (mLoadBitmapTask != null) {
880 mLoadBitmapTask.cancel(false);
882 mUserPresetsManager.close();
887 // TODO: find a more robust way of handling image size selection
888 // for high screen densities.
889 private int getScreenImageSize() {
890 DisplayMetrics outMetrics = new DisplayMetrics();
891 getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
892 return Math.max(outMetrics.heightPixels, outMetrics.widthPixels);
895 private void showSavingProgress(String albumName) {
896 ProgressDialog progress;
897 if (mSavingProgressDialog != null) {
898 progress = mSavingProgressDialog.get();
899 if (progress != null) {
904 // TODO: Allow cancellation of the saving process
906 if (albumName == null) {
907 progressText = getString(R.string.saving_image);
909 progressText = getString(R.string.filtershow_saving_image, albumName);
911 progress = ProgressDialog.show(this, "", progressText, true, false);
912 mSavingProgressDialog = new WeakReference<ProgressDialog>(progress);
915 private void hideSavingProgress() {
916 if (mSavingProgressDialog != null) {
917 ProgressDialog progress = mSavingProgressDialog.get();
918 if (progress != null)
923 public void completeSaveImage(Uri saveUri) {
924 if (mSharingImage && mSharedOutputFile != null) {
925 // Image saved, we unblock the content provider
926 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
927 Uri.encode(mSharedOutputFile.getAbsolutePath()));
928 ContentValues values = new ContentValues();
929 values.put(SharedImageProvider.PREPARE, false);
930 getContentResolver().insert(uri, values);
932 setResult(RESULT_OK, new Intent().setData(saveUri));
933 hideSavingProgress();
938 public boolean onShareTargetSelected(ShareActionProvider arg0, Intent arg1) {
939 // First, let's tell the SharedImageProvider that it will need to wait
941 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
942 Uri.encode(mSharedOutputFile.getAbsolutePath()));
943 ContentValues values = new ContentValues();
944 values.put(SharedImageProvider.PREPARE, true);
945 getContentResolver().insert(uri, values);
946 mSharingImage = true;
948 // Process and save the image in the background.
949 showSavingProgress(null);
950 mImageShow.saveImage(this, mSharedOutputFile);
954 private Intent getDefaultShareIntent() {
955 Intent intent = new Intent(Intent.ACTION_SEND);
956 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
957 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
958 intent.setType(SharedImageProvider.MIME_TYPE);
959 mSharedOutputFile = SaveImage.getNewFile(this, MasterImage.getImage().getUri());
960 Uri uri = Uri.withAppendedPath(SharedImageProvider.CONTENT_URI,
961 Uri.encode(mSharedOutputFile.getAbsolutePath()));
962 intent.putExtra(Intent.EXTRA_STREAM, uri);
967 public boolean onCreateOptionsMenu(Menu menu) {
968 getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu);
969 MenuItem showState = menu.findItem(R.id.showImageStateButton);
970 if (mShowingImageStatePanel) {
971 showState.setTitle(R.string.hide_imagestate_panel);
973 showState.setTitle(R.string.show_imagestate_panel);
975 mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share)
976 .getActionProvider();
977 mShareActionProvider.setShareIntent(getDefaultShareIntent());
978 mShareActionProvider.setOnShareTargetSelectedListener(this);
984 private void setupMenu(){
985 if (mMenu == null || mMasterImage == null) {
988 MenuItem undoItem = mMenu.findItem(R.id.undoButton);
989 MenuItem redoItem = mMenu.findItem(R.id.redoButton);
990 MenuItem resetItem = mMenu.findItem(R.id.resetHistoryButton);
991 mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
995 public void onPause() {
997 if (mShareActionProvider != null) {
998 mShareActionProvider.setOnShareTargetSelectedListener(null);
1003 public void onResume() {
1005 if (mShareActionProvider != null) {
1006 mShareActionProvider.setOnShareTargetSelectedListener(this);
1011 public boolean onOptionsItemSelected(MenuItem item) {
1012 switch (item.getItemId()) {
1013 case R.id.undoButton: {
1014 HistoryManager adapter = mMasterImage.getHistory();
1015 int position = adapter.undo();
1016 mMasterImage.onHistoryItemClick(position);
1021 case R.id.redoButton: {
1022 HistoryManager adapter = mMasterImage.getHistory();
1023 int position = adapter.redo();
1024 mMasterImage.onHistoryItemClick(position);
1028 case R.id.resetHistoryButton: {
1032 case R.id.showImageStateButton: {
1033 toggleImageStatePanel();
1036 case R.id.exportFlattenButton: {
1037 showExportOptionsDialog();
1040 case android.R.id.home: {
1044 case R.id.manageUserPresets: {
1045 manageUserPresets();
1048 case R.id.showInfoPanel: {
1049 toggleInformationPanel();
1052 case R.id.printButton: {
1060 public void print() {
1061 Bitmap bitmap = MasterImage.getImage().getHighresImage();
1062 PrintJob.printBitmap(this, "ImagePrint", bitmap);
1065 public void addNewPreset() {
1066 DialogFragment dialog = new PresetManagementDialog();
1067 dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
1070 private void manageUserPresets() {
1071 DialogFragment dialog = new PresetManagementDialog();
1072 dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
1075 private void showExportOptionsDialog() {
1076 DialogFragment dialog = new ExportDialog();
1077 dialog.show(getSupportFragmentManager(), "ExportDialogFragment");
1080 public void updateUserPresetsFromAdapter(UserPresetsAdapter adapter) {
1081 ArrayList<FilterUserPresetRepresentation> representations =
1082 adapter.getDeletedRepresentations();
1083 for (FilterUserPresetRepresentation representation : representations) {
1084 deletePreset(representation.getId());
1086 ArrayList<FilterUserPresetRepresentation> changedRepresentations =
1087 adapter.getChangedRepresentations();
1088 for (FilterUserPresetRepresentation representation : changedRepresentations) {
1089 updatePreset(representation);
1091 adapter.clearDeletedRepresentations();
1092 adapter.clearChangedRepresentations();
1096 public void loadUserPresets() {
1097 mUserPresetsManager.load();
1098 updateUserPresetsFromManager();
1101 public void updateUserPresetsFromManager() {
1102 ArrayList<FilterUserPresetRepresentation> presets = mUserPresetsManager.getRepresentations();
1103 if (presets == null) {
1106 if (mCategoryLooksAdapter != null) {
1109 if (presets.size() > 0) {
1110 mCategoryLooksAdapter.add(new Action(this, Action.SPACER));
1112 mUserPresetsAdapter.clear();
1113 for (int i = 0; i < presets.size(); i++) {
1114 FilterUserPresetRepresentation representation = presets.get(i);
1115 mCategoryLooksAdapter.add(
1116 new Action(this, representation, Action.FULL_VIEW, true));
1117 mUserPresetsAdapter.add(new Action(this, representation, Action.FULL_VIEW));
1119 if (presets.size() > 0) {
1120 mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
1122 mCategoryLooksAdapter.notifyDataSetChanged();
1123 mCategoryLooksAdapter.notifyDataSetInvalidated();
1126 public void saveCurrentImagePreset(String name) {
1127 mUserPresetsManager.save(MasterImage.getImage().getPreset(), name);
1130 private void deletePreset(int id) {
1131 mUserPresetsManager.delete(id);
1134 private void updatePreset(FilterUserPresetRepresentation representation) {
1135 mUserPresetsManager.update(representation);
1138 public void enableSave(boolean enable) {
1139 if (mSaveButton != null) {
1140 mSaveButton.setEnabled(enable);
1144 private void fillLooks() {
1145 FiltersManager filtersManager = FiltersManager.getManager();
1146 ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getLooks();
1148 if (mCategoryLooksAdapter != null) {
1149 mCategoryLooksAdapter.clear();
1151 mCategoryLooksAdapter = new CategoryAdapter(this);
1152 int verticalItemHeight = (int) getResources().getDimension(R.dimen.action_item_height);
1153 mCategoryLooksAdapter.setItemHeight(verticalItemHeight);
1154 for (FilterRepresentation representation : filtersRepresentations) {
1155 mCategoryLooksAdapter.add(new Action(this, representation, Action.FULL_VIEW));
1157 if (mUserPresetsManager.getRepresentations() == null
1158 || mUserPresetsManager.getRepresentations().size() == 0) {
1159 mCategoryLooksAdapter.add(new Action(this, Action.ADD_ACTION));
1162 Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1163 if (panel != null) {
1164 if (panel instanceof MainPanel) {
1165 MainPanel mainPanel = (MainPanel) panel;
1166 mainPanel.loadCategoryLookPanel(true);
1171 public void setDefaultPreset() {
1172 // Default preset (original)
1173 ImagePreset preset = new ImagePreset(); // empty
1174 mMasterImage.setPreset(preset, preset.getLastRepresentation(), true);
1177 // //////////////////////////////////////////////////////////////////////////////
1178 // Some utility functions
1179 // TODO: finish the cleanup.
1181 public void invalidateViews() {
1182 for (ImageShow views : mImageViews) {
1183 views.updateImage();
1187 public void hideImageViews() {
1188 for (View view : mImageViews) {
1189 view.setVisibility(View.GONE);
1191 mEditorPlaceHolder.hide();
1194 // //////////////////////////////////////////////////////////////////////////////
1195 // imageState panel...
1197 public void toggleImageStatePanel() {
1198 invalidateOptionsMenu();
1199 mShowingImageStatePanel = !mShowingImageStatePanel;
1200 Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1201 if (panel != null) {
1202 if (panel instanceof EditorPanel) {
1203 EditorPanel editorPanel = (EditorPanel) panel;
1204 editorPanel.showImageStatePanel(mShowingImageStatePanel);
1205 } else if (panel instanceof MainPanel) {
1206 MainPanel mainPanel = (MainPanel) panel;
1207 mainPanel.showImageStatePanel(mShowingImageStatePanel);
1212 public void toggleVersionsPanel() {
1213 mShowingVersionsPanel = !mShowingVersionsPanel;
1214 Fragment panel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1215 if (panel != null && panel instanceof MainPanel) {
1216 MainPanel mainPanel = (MainPanel) panel;
1217 mainPanel.loadCategoryVersionsPanel();
1222 public void onConfigurationChanged(Configuration newConfig)
1224 super.onConfigurationChanged(newConfig);
1227 if (mMasterImage == null) {
1234 if (mCurrentDialog != null) {
1235 mCurrentDialog.dismiss();
1236 mCurrentDialog = null;
1238 // mLoadBitmapTask==null implies you have looked at the intent
1239 if (!mShowingTinyPlanet && (mLoadBitmapTask == null)) {
1240 mCategoryFiltersAdapter.removeTinyPlanet();
1242 stopLoadingIndicator();
1245 public void setupMasterImage() {
1247 HistoryManager historyManager = new HistoryManager();
1248 StateAdapter imageStateAdapter = new StateAdapter(this, 0);
1249 MasterImage.reset();
1250 mMasterImage = MasterImage.getImage();
1251 mMasterImage.setHistoryManager(historyManager);
1252 mMasterImage.setStateAdapter(imageStateAdapter);
1253 mMasterImage.setActivity(this);
1255 if (Runtime.getRuntime().maxMemory() > LIMIT_SUPPORTS_HIGHRES) {
1256 mMasterImage.setSupportsHighRes(true);
1258 mMasterImage.setSupportsHighRes(false);
1262 void resetHistory() {
1263 HistoryManager adapter = mMasterImage.getHistory();
1265 HistoryItem historyItem = adapter.getItem(0);
1266 ImagePreset original = new ImagePreset(historyItem.getImagePreset());
1267 mMasterImage.setPreset(original, historyItem.getFilterRepresentation(), true);
1272 public void showDefaultImageView() {
1273 mEditorPlaceHolder.hide();
1274 mImageShow.setVisibility(View.VISIBLE);
1275 MasterImage.getImage().setCurrentFilter(null);
1276 MasterImage.getImage().setCurrentFilterRepresentation(null);
1279 public void backToMain() {
1280 Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1281 if (currentPanel instanceof MainPanel) {
1285 showDefaultImageView();
1289 public void onBackPressed() {
1290 Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG);
1291 if (currentPanel instanceof MainPanel) {
1292 if (!mImageShow.hasModifications()) {
1295 AlertDialog.Builder builder = new AlertDialog.Builder(this);
1296 builder.setMessage(R.string.unsaved).setTitle(R.string.save_before_exit);
1297 builder.setPositiveButton(R.string.save_and_exit, new DialogInterface.OnClickListener() {
1299 public void onClick(DialogInterface dialog, int id) {
1303 builder.setNegativeButton(R.string.exit, new DialogInterface.OnClickListener() {
1305 public void onClick(DialogInterface dialog, int id) {
1316 public void cannotLoadImage() {
1317 Toast.makeText(this, R.string.cannot_load_image, Toast.LENGTH_SHORT).show();
1321 // //////////////////////////////////////////////////////////////////////////////
1323 public float getPixelsFromDip(float value) {
1324 Resources r = getResources();
1325 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
1326 r.getDisplayMetrics());
1330 public void onItemClick(AdapterView<?> parent, View view, int position,
1332 mMasterImage.onHistoryItemClick(position);
1336 public void pickImage() {
1337 Intent intent = new Intent();
1338 intent.setType("image/*");
1339 intent.setAction(Intent.ACTION_GET_CONTENT);
1340 startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
1345 public void onActivityResult(int requestCode, int resultCode, Intent data) {
1346 if (resultCode == RESULT_OK) {
1347 if (requestCode == SELECT_PICTURE) {
1348 Uri selectedImageUri = data.getData();
1349 startLoadBitmap(selectedImageUri);
1355 public void saveImage() {
1356 if (mImageShow.hasModifications()) {
1357 // Get the name of the album, to which the image will be saved
1358 File saveDir = SaveImage.getFinalSaveDirectory(this, mSelectedImageUri);
1359 int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
1360 String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
1361 showSavingProgress(albumName);
1362 mImageShow.saveImage(this, null);
1369 public void done() {
1370 hideSavingProgress();
1371 if (mLoadBitmapTask != null) {
1372 mLoadBitmapTask.cancel(false);
1377 private void extractXMPData() {
1378 XMresults res = XmpPresets.extractXMPData(
1379 getBaseContext(), mMasterImage, getIntent().getData());
1383 mOriginalImageUri = res.originalimage;
1384 mOriginalPreset = res.preset;
1387 public Uri getSelectedImageUri() {
1388 return mSelectedImageUri;
1391 public void setHandlesSwipeForView(View view, float startX, float startY) {
1393 mHandlingSwipeButton = true;
1395 mHandlingSwipeButton = false;
1397 mHandledSwipeView = view;
1398 int[] location = new int[2];
1399 view.getLocationInWindow(location);
1400 mSwipeStartX = location[0] + startX;
1401 mSwipeStartY = location[1] + startY;
1404 public boolean dispatchTouchEvent (MotionEvent ev) {
1405 if (mHandlingSwipeButton) {
1406 int direction = CategoryView.HORIZONTAL;
1407 if (mHandledSwipeView instanceof CategoryView) {
1408 direction = ((CategoryView) mHandledSwipeView).getOrientation();
1410 if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
1411 float delta = ev.getY() - mSwipeStartY;
1412 float distance = mHandledSwipeView.getHeight();
1413 if (direction == CategoryView.VERTICAL) {
1414 delta = ev.getX() - mSwipeStartX;
1415 mHandledSwipeView.setTranslationX(delta);
1416 distance = mHandledSwipeView.getWidth();
1418 mHandledSwipeView.setTranslationY(delta);
1420 delta = Math.abs(delta);
1421 float transparency = Math.min(1, delta / distance);
1422 mHandledSwipeView.setAlpha(1.f - transparency);
1423 mHandledSwipeViewLastDelta = delta;
1425 if (ev.getActionMasked() == MotionEvent.ACTION_CANCEL
1426 || ev.getActionMasked() == MotionEvent.ACTION_UP) {
1427 mHandledSwipeView.setTranslationX(0);
1428 mHandledSwipeView.setTranslationY(0);
1429 mHandledSwipeView.setAlpha(1.f);
1430 mHandlingSwipeButton = false;
1431 float distance = mHandledSwipeView.getHeight();
1432 if (direction == CategoryView.VERTICAL) {
1433 distance = mHandledSwipeView.getWidth();
1435 if (mHandledSwipeViewLastDelta > distance) {
1436 ((SwipableView) mHandledSwipeView).delete();
1441 return super.dispatchTouchEvent(ev);
1444 public Point mHintTouchPoint = new Point();
1446 public Point hintTouchPoint(View view) {
1447 int location[] = new int[2];
1448 view.getLocationOnScreen(location);
1449 int x = mHintTouchPoint.x - location[0];
1450 int y = mHintTouchPoint.y - location[1];
1451 return new Point(x, y);
1454 public void startTouchAnimation(View target, float x, float y) {
1455 final CategorySelected hint =
1456 (CategorySelected) findViewById(R.id.categorySelectedIndicator);
1457 int location[] = new int[2];
1458 target.getLocationOnScreen(location);
1459 mHintTouchPoint.x = (int) (location[0] + x);
1460 mHintTouchPoint.y = (int) (location[1] + y);
1461 int locationHint[] = new int[2];
1462 ((View)hint.getParent()).getLocationOnScreen(locationHint);
1463 int dx = (int) (x - (hint.getWidth())/2);
1464 int dy = (int) (y - (hint.getHeight())/2);
1465 hint.setTranslationX(location[0] - locationHint[0] + dx);
1466 hint.setTranslationY(location[1] - locationHint[1] + dy);
1467 hint.setVisibility(View.VISIBLE);
1468 hint.animate().scaleX(2).scaleY(2).alpha(0).withEndAction(new Runnable() {
1471 hint.setVisibility(View.INVISIBLE);