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