blob: e80bfcb57d66b9e558cddcb1c27d81f08e03f768 [file] [log] [blame]
Michael Kolb8872c232013-01-29 10:33:22 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera;
18
19import android.content.Context;
20import android.util.AttributeSet;
Shashi Shekhar82d592f2014-11-12 09:52:19 -080021import android.view.GestureDetector;
22import android.view.GestureDetector.SimpleOnGestureListener;
Michael Kolb8872c232013-01-29 10:33:22 -080023import android.view.MotionEvent;
24import android.view.View;
25import android.widget.ImageView;
26
Andy Huibersb7c7d9a2014-06-18 22:26:14 -070027import com.android.camera.debug.Log;
28import com.android.camera.ui.TouchCoordinate;
29
Erin Dahlgren5d187692014-02-25 19:16:12 -080030import java.util.ArrayList;
Shashi Shekhar82d592f2014-11-12 09:52:19 -080031import java.util.List;
Erin Dahlgren5d187692014-02-25 19:16:12 -080032
Michael Kolb8872c232013-01-29 10:33:22 -080033/**
34 * A button designed to be used for the on-screen shutter button.
35 * It's currently an {@code ImageView} that can call a delegate when the
36 * pressed state changes.
37 */
38public class ShutterButton extends ImageView {
Andy Huibersb7c7d9a2014-06-18 22:26:14 -070039 private static final Log.Tag TAG = new Log.Tag("ShutterButton");
Doris Liu948de992014-02-19 18:06:00 -080040 public static final float ALPHA_WHEN_ENABLED = 1f;
41 public static final float ALPHA_WHEN_DISABLED = 0.2f;
Michael Kolb8872c232013-01-29 10:33:22 -080042 private boolean mTouchEnabled = true;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -070043 private TouchCoordinate mTouchCoordinate;
Shashi Shekhar82d592f2014-11-12 09:52:19 -080044 private final GestureDetector mGestureDetector;
45
Michael Kolb8872c232013-01-29 10:33:22 -080046 /**
47 * A callback to be invoked when a ShutterButton's pressed state changes.
48 */
49 public interface OnShutterButtonListener {
50 /**
51 * Called when a ShutterButton has been pressed.
52 *
53 * @param pressed The ShutterButton that was pressed.
54 */
55 void onShutterButtonFocus(boolean pressed);
Andy Huibersb7c7d9a2014-06-18 22:26:14 -070056 void onShutterCoordinate(TouchCoordinate coord);
Michael Kolb8872c232013-01-29 10:33:22 -080057 void onShutterButtonClick();
Shashi Shekhar82d592f2014-11-12 09:52:19 -080058
59 /**
60 * Called when shutter button is held down for a long press.
61 */
62 void onShutterButtonLongPressed();
63 }
64
65 /**
66 * A gesture listener to detect long presses.
67 */
68 private class LongPressGestureListener extends SimpleOnGestureListener {
69 @Override
70 public void onLongPress(MotionEvent event) {
71 for (OnShutterButtonListener listener : mListeners) {
72 listener.onShutterButtonLongPressed();
73 }
74 }
Michael Kolb8872c232013-01-29 10:33:22 -080075 }
76
Erin Dahlgren5d187692014-02-25 19:16:12 -080077 private List<OnShutterButtonListener> mListeners
78 = new ArrayList<OnShutterButtonListener>();
Michael Kolb8872c232013-01-29 10:33:22 -080079 private boolean mOldPressed;
80
81 public ShutterButton(Context context, AttributeSet attrs) {
82 super(context, attrs);
Shashi Shekhar82d592f2014-11-12 09:52:19 -080083 mGestureDetector = new GestureDetector(context, new LongPressGestureListener());
84 mGestureDetector.setIsLongpressEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -080085 }
86
Erin Dahlgren5d187692014-02-25 19:16:12 -080087 /**
88 * Add an {@link OnShutterButtonListener} to a set of listeners.
89 */
90 public void addOnShutterButtonListener(OnShutterButtonListener listener) {
91 if (!mListeners.contains(listener)) {
92 mListeners.add(listener);
93 }
94 }
95
96 /**
97 * Remove an {@link OnShutterButtonListener} from a set of listeners.
98 */
99 public void removeOnShutterButtonListener(OnShutterButtonListener listener) {
100 if (mListeners.contains(listener)) {
101 mListeners.remove(listener);
102 }
Michael Kolb8872c232013-01-29 10:33:22 -0800103 }
104
105 @Override
106 public boolean dispatchTouchEvent(MotionEvent m) {
107 if (mTouchEnabled) {
Shashi Shekhar82d592f2014-11-12 09:52:19 -0800108 mGestureDetector.onTouchEvent(m);
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700109 if (m.getActionMasked() == MotionEvent.ACTION_UP) {
110 mTouchCoordinate = new TouchCoordinate(m.getX(), m.getY(), this.getMeasuredWidth(),
111 this.getMeasuredHeight());
112 }
Michael Kolb8872c232013-01-29 10:33:22 -0800113 return super.dispatchTouchEvent(m);
114 } else {
115 return false;
116 }
117 }
118
119 public void enableTouch(boolean enable) {
120 mTouchEnabled = enable;
121 }
122
123 /**
124 * Hook into the drawable state changing to get changes to isPressed -- the
125 * onPressed listener doesn't always get called when the pressed state
126 * changes.
127 */
128 @Override
129 protected void drawableStateChanged() {
130 super.drawableStateChanged();
131 final boolean pressed = isPressed();
132 if (pressed != mOldPressed) {
133 if (!pressed) {
134 // When pressing the physical camera button the sequence of
135 // events is:
136 // focus pressed, optional camera pressed, focus released.
137 // We want to emulate this sequence of events with the shutter
138 // button. When clicking using a trackball button, the view
139 // system changes the drawable state before posting click
140 // notification, so the sequence of events is:
141 // pressed(true), optional click, pressed(false)
142 // When clicking using touch events, the view system changes the
143 // drawable state after posting click notification, so the
144 // sequence of events is:
145 // pressed(true), pressed(false), optional click
146 // Since we're emulating the physical camera button, we want to
147 // have the same order of events. So we want the optional click
148 // callback to be delivered before the pressed(false) callback.
149 //
150 // To do this, we delay the posting of the pressed(false) event
151 // slightly by pushing it on the event queue. This moves it
152 // after the optional click notification, so our client always
153 // sees events in this sequence:
154 // pressed(true), optional click, pressed(false)
155 post(new Runnable() {
156 @Override
157 public void run() {
158 callShutterButtonFocus(pressed);
159 }
160 });
161 } else {
162 callShutterButtonFocus(pressed);
163 }
164 mOldPressed = pressed;
165 }
166 }
167
168 private void callShutterButtonFocus(boolean pressed) {
Erin Dahlgren5d187692014-02-25 19:16:12 -0800169 for (OnShutterButtonListener listener : mListeners) {
170 listener.onShutterButtonFocus(pressed);
Michael Kolb8872c232013-01-29 10:33:22 -0800171 }
172 }
173
174 @Override
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700175 public boolean performClick() {
Michael Kolb8872c232013-01-29 10:33:22 -0800176 boolean result = super.performClick();
Erin Dahlgren5d187692014-02-25 19:16:12 -0800177 if (getVisibility() == View.VISIBLE) {
178 for (OnShutterButtonListener listener : mListeners) {
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700179 listener.onShutterCoordinate(mTouchCoordinate);
180 mTouchCoordinate = null;
Erin Dahlgren5d187692014-02-25 19:16:12 -0800181 listener.onShutterButtonClick();
182 }
Michael Kolb8872c232013-01-29 10:33:22 -0800183 }
184 return result;
185 }
Michael Kolb8872c232013-01-29 10:33:22 -0800186}