18872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb/*
28872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * Copyright (C) 2008 The Android Open Source Project
38872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb *
48872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * Licensed under the Apache License, Version 2.0 (the "License");
58872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * you may not use this file except in compliance with the License.
68872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * You may obtain a copy of the License at
78872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb *
88872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb *      http://www.apache.org/licenses/LICENSE-2.0
98872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb *
108872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * Unless required by applicable law or agreed to in writing, software
118872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * distributed under the License is distributed on an "AS IS" BASIS,
128872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * See the License for the specific language governing permissions and
148872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * limitations under the License.
158872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb */
168872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
178872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbpackage com.android.camera;
188872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
198872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.content.Context;
208872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.util.AttributeSet;
218872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.view.MotionEvent;
228872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.view.View;
238872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbimport android.widget.ImageView;
248872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
258872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb/**
268872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * A button designed to be used for the on-screen shutter button.
278872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * It's currently an {@code ImageView} that can call a delegate when the
288872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb * pressed state changes.
298872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb */
308872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolbpublic class ShutterButton extends ImageView {
318872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
328872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private boolean mTouchEnabled = true;
338872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
348872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    /**
358872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     * A callback to be invoked when a ShutterButton's pressed state changes.
368872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     */
378872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public interface OnShutterButtonListener {
388872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        /**
398872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb         * Called when a ShutterButton has been pressed.
408872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb         *
418872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb         * @param pressed The ShutterButton that was pressed.
428872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb         */
438872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        void onShutterButtonFocus(boolean pressed);
448872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        void onShutterButtonClick();
458872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
468872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
478872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private OnShutterButtonListener mListener;
488872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private boolean mOldPressed;
498872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
508872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public ShutterButton(Context context, AttributeSet attrs) {
518872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        super(context, attrs);
528872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
538872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
548872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void setOnShutterButtonListener(OnShutterButtonListener listener) {
558872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mListener = listener;
568872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
578872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
588872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    @Override
598872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public boolean dispatchTouchEvent(MotionEvent m) {
608872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mTouchEnabled) {
618872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            return super.dispatchTouchEvent(m);
628872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        } else {
638872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            return false;
648872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
658872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
668872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
678872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public void enableTouch(boolean enable) {
688872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        mTouchEnabled = enable;
698872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
708872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
718872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    /**
728872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     * Hook into the drawable state changing to get changes to isPressed -- the
738872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     * onPressed listener doesn't always get called when the pressed state
748872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     * changes.
758872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb     */
768872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    @Override
778872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    protected void drawableStateChanged() {
788872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        super.drawableStateChanged();
798872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        final boolean pressed = isPressed();
808872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (pressed != mOldPressed) {
818872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            if (!pressed) {
828872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // When pressing the physical camera button the sequence of
838872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // events is:
848872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                //    focus pressed, optional camera pressed, focus released.
858872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // We want to emulate this sequence of events with the shutter
868872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // button. When clicking using a trackball button, the view
878872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // system changes the drawable state before posting click
888872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // notification, so the sequence of events is:
898872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                //    pressed(true), optional click, pressed(false)
908872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // When clicking using touch events, the view system changes the
918872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // drawable state after posting click notification, so the
928872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // sequence of events is:
938872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                //    pressed(true), pressed(false), optional click
948872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // Since we're emulating the physical camera button, we want to
958872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // have the same order of events. So we want the optional click
968872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // callback to be delivered before the pressed(false) callback.
978872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                //
988872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // To do this, we delay the posting of the pressed(false) event
998872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // slightly by pushing it on the event queue. This moves it
1008872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // after the optional click notification, so our client always
1018872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                // sees events in this sequence:
1028872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                //     pressed(true), optional click, pressed(false)
1038872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                post(new Runnable() {
1048872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    @Override
1058872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    public void run() {
1068872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                        callShutterButtonFocus(pressed);
1078872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                    }
1088872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                });
1098872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            } else {
1108872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb                callShutterButtonFocus(pressed);
1118872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            }
1128872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mOldPressed = pressed;
1138872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
1148872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
1158872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1168872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    private void callShutterButtonFocus(boolean pressed) {
1178872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mListener != null) {
1188872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mListener.onShutterButtonFocus(pressed);
1198872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
1208872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
1218872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb
1228872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    @Override
1238872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    public boolean performClick() {
1248872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        boolean result = super.performClick();
1258872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        if (mListener != null && getVisibility() == View.VISIBLE) {
1268872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb            mListener.onShutterButtonClick();
1278872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        }
1288872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb        return result;
1298872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb    }
1308872c23e739de38d74f04a8c852ebb5199c905f6Michael Kolb}
131