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
17package com.android.music;
18
19import android.content.Context;
20import android.os.SystemClock;
21import android.util.AttributeSet;
22import android.view.KeyEvent;
23import android.view.MotionEvent;
24import android.view.View;
25import android.widget.ImageButton;
26
27/**
28 * A button that will repeatedly call a 'listener' method
29 * as long as the button is pressed.
30 */
31public class RepeatingImageButton extends ImageButton {
32
33    private long mStartTime;
34    private int mRepeatCount;
35    private RepeatListener mListener;
36    private long mInterval = 500;
37
38    public RepeatingImageButton(Context context) {
39        this(context, null);
40    }
41
42    public RepeatingImageButton(Context context, AttributeSet attrs) {
43        this(context, attrs, android.R.attr.imageButtonStyle);
44    }
45
46    public RepeatingImageButton(Context context, AttributeSet attrs, int defStyle) {
47        super(context, attrs, defStyle);
48        setFocusable(true);
49        setLongClickable(true);
50    }
51
52    /**
53     * Sets the listener to be called while the button is pressed and
54     * the interval in milliseconds with which it will be called.
55     * @param l The listener that will be called
56     * @param interval The interval in milliseconds for calls
57     */
58    public void setRepeatListener(RepeatListener l, long interval) {
59        mListener = l;
60        mInterval = interval;
61    }
62
63    @Override
64    public boolean performLongClick() {
65        mStartTime = SystemClock.elapsedRealtime();
66        mRepeatCount = 0;
67        post(mRepeater);
68        return true;
69    }
70
71    @Override
72    public boolean onTouchEvent(MotionEvent event) {
73        if (event.getAction() == MotionEvent.ACTION_UP) {
74            // remove the repeater, but call the hook one more time
75            removeCallbacks(mRepeater);
76            if (mStartTime != 0) {
77                doRepeat(true);
78                mStartTime = 0;
79            }
80        }
81        return super.onTouchEvent(event);
82    }
83
84    @Override
85    public boolean onKeyDown(int keyCode, KeyEvent event) {
86        switch (keyCode) {
87            case KeyEvent.KEYCODE_DPAD_CENTER:
88            case KeyEvent.KEYCODE_ENTER:
89                // need to call super to make long press work, but return
90                // true so that the application doesn't get the down event.
91                super.onKeyDown(keyCode, event);
92                return true;
93        }
94        return super.onKeyDown(keyCode, event);
95    }
96
97    @Override
98    public boolean onKeyUp(int keyCode, KeyEvent event) {
99        switch (keyCode) {
100        case KeyEvent.KEYCODE_DPAD_CENTER:
101        case KeyEvent.KEYCODE_ENTER:
102            // remove the repeater, but call the hook one more time
103            removeCallbacks(mRepeater);
104            if (mStartTime != 0) {
105                doRepeat(true);
106                mStartTime = 0;
107            }
108        }
109        return super.onKeyUp(keyCode, event);
110    }
111
112    private Runnable mRepeater = new Runnable() {
113        public void run() {
114            doRepeat(false);
115            if (isPressed()) {
116                postDelayed(this, mInterval);
117            }
118        }
119    };
120
121    private  void doRepeat(boolean last) {
122        long now = SystemClock.elapsedRealtime();
123        if (mListener != null) {
124            mListener.onRepeat(this, now - mStartTime, last ? -1 : mRepeatCount++);
125        }
126    }
127
128    public interface RepeatListener {
129        /**
130         * This method will be called repeatedly at roughly the interval
131         * specified in setRepeatListener(), for as long as the button
132         * is pressed.
133         * @param v The button as a View.
134         * @param duration The number of milliseconds the button has been pressed so far.
135         * @param repeatcount The number of previous calls in this sequence.
136         * If this is going to be the last call in this sequence (i.e. the user
137         * just stopped pressing the button), the value will be -1.
138         */
139        void onRepeat(View v, long duration, int repeatcount);
140    }
141}
142