Home | History | Annotate | Download | only in camera
      1 /*
      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 
     17 package com.android.camera;
     18 
     19 import android.content.Context;
     20 import android.util.AttributeSet;
     21 import android.view.GestureDetector;
     22 import android.view.GestureDetector.SimpleOnGestureListener;
     23 import android.view.MotionEvent;
     24 import android.view.View;
     25 import android.widget.ImageView;
     26 
     27 import com.android.camera.debug.Log;
     28 import com.android.camera.ui.TouchCoordinate;
     29 
     30 import java.util.ArrayList;
     31 import java.util.List;
     32 
     33 /**
     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  */
     38 public class ShutterButton extends ImageView {
     39     private static final Log.Tag TAG = new Log.Tag("ShutterButton");
     40     public static final float ALPHA_WHEN_ENABLED = 1f;
     41     public static final float ALPHA_WHEN_DISABLED = 0.2f;
     42     private boolean mTouchEnabled = true;
     43     private TouchCoordinate mTouchCoordinate;
     44     private final GestureDetector mGestureDetector;
     45 
     46     /**
     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);
     56         void onShutterCoordinate(TouchCoordinate coord);
     57         void onShutterButtonClick();
     58 
     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         }
     75     }
     76 
     77     private List<OnShutterButtonListener> mListeners
     78         = new ArrayList<OnShutterButtonListener>();
     79     private boolean mOldPressed;
     80 
     81     public ShutterButton(Context context, AttributeSet attrs) {
     82         super(context, attrs);
     83         mGestureDetector = new GestureDetector(context, new LongPressGestureListener());
     84         mGestureDetector.setIsLongpressEnabled(true);
     85     }
     86 
     87     /**
     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         }
    103     }
    104 
    105     @Override
    106     public boolean dispatchTouchEvent(MotionEvent m) {
    107         if (mTouchEnabled) {
    108             // Don't send ACTION_MOVE messages to gesture detector unless event motion is out of
    109             // shutter button view. A small motion resets the long tap status. A long tap should
    110             // be interpreted as the duration the finger is held down on the shutter button,
    111             // regardless of any small motions. If motion moves out of shutter button view, the
    112             // gesture detector needs to be notified to reset the long tap status.
    113             if (m.getActionMasked() != MotionEvent.ACTION_MOVE
    114                 || m.getX() < 0 || m.getY() < 0
    115                 || m.getX() >= getWidth() || m.getY() >= getHeight()) {
    116                 mGestureDetector.onTouchEvent(m);
    117             }
    118             if (m.getActionMasked() == MotionEvent.ACTION_UP) {
    119                 mTouchCoordinate = new TouchCoordinate(m.getX(), m.getY(), this.getMeasuredWidth(),
    120                         this.getMeasuredHeight());
    121             }
    122             return super.dispatchTouchEvent(m);
    123         } else {
    124             return false;
    125         }
    126     }
    127 
    128     public void enableTouch(boolean enable) {
    129         mTouchEnabled = enable;
    130     }
    131 
    132     /**
    133      * Hook into the drawable state changing to get changes to isPressed -- the
    134      * onPressed listener doesn't always get called when the pressed state
    135      * changes.
    136      */
    137     @Override
    138     protected void drawableStateChanged() {
    139         super.drawableStateChanged();
    140         final boolean pressed = isPressed();
    141         if (pressed != mOldPressed) {
    142             if (!pressed) {
    143                 // When pressing the physical camera button the sequence of
    144                 // events is:
    145                 //    focus pressed, optional camera pressed, focus released.
    146                 // We want to emulate this sequence of events with the shutter
    147                 // button. When clicking using a trackball button, the view
    148                 // system changes the drawable state before posting click
    149                 // notification, so the sequence of events is:
    150                 //    pressed(true), optional click, pressed(false)
    151                 // When clicking using touch events, the view system changes the
    152                 // drawable state after posting click notification, so the
    153                 // sequence of events is:
    154                 //    pressed(true), pressed(false), optional click
    155                 // Since we're emulating the physical camera button, we want to
    156                 // have the same order of events. So we want the optional click
    157                 // callback to be delivered before the pressed(false) callback.
    158                 //
    159                 // To do this, we delay the posting of the pressed(false) event
    160                 // slightly by pushing it on the event queue. This moves it
    161                 // after the optional click notification, so our client always
    162                 // sees events in this sequence:
    163                 //     pressed(true), optional click, pressed(false)
    164                 post(new Runnable() {
    165                     @Override
    166                     public void run() {
    167                         callShutterButtonFocus(pressed);
    168                     }
    169                 });
    170             } else {
    171                 callShutterButtonFocus(pressed);
    172             }
    173             mOldPressed = pressed;
    174         }
    175     }
    176 
    177     private void callShutterButtonFocus(boolean pressed) {
    178         for (OnShutterButtonListener listener : mListeners) {
    179             listener.onShutterButtonFocus(pressed);
    180         }
    181     }
    182 
    183     @Override
    184     public boolean performClick() {
    185         boolean result = super.performClick();
    186         if (getVisibility() == View.VISIBLE) {
    187             for (OnShutterButtonListener listener : mListeners) {
    188                 listener.onShutterCoordinate(mTouchCoordinate);
    189                 mTouchCoordinate = null;
    190                 listener.onShutterButtonClick();
    191             }
    192         }
    193         return result;
    194     }
    195 }
    196