diff --git a/res/drawable-hdpi/jog_tab_bar_left_end_confirm_green.9.png b/res/drawable-hdpi/jog_tab_bar_left_end_confirm_green.9.png deleted file mode 100644 index 7c4f40ea9..000000000 Binary files a/res/drawable-hdpi/jog_tab_bar_left_end_confirm_green.9.png and /dev/null differ diff --git a/res/drawable-hdpi/jog_tab_bar_left_end_normal.9.png b/res/drawable-hdpi/jog_tab_bar_left_end_normal.9.png deleted file mode 100644 index b9ec2374a..000000000 Binary files a/res/drawable-hdpi/jog_tab_bar_left_end_normal.9.png and /dev/null differ diff --git a/res/drawable-hdpi/jog_tab_bar_left_end_pressed.9.png b/res/drawable-hdpi/jog_tab_bar_left_end_pressed.9.png deleted file mode 100644 index 2800cabeb..000000000 Binary files a/res/drawable-hdpi/jog_tab_bar_left_end_pressed.9.png and /dev/null differ diff --git a/res/drawable-hdpi/jog_tab_bar_right_end_confirm_red.9.png b/res/drawable-hdpi/jog_tab_bar_right_end_confirm_red.9.png deleted file mode 100644 index fd98571d3..000000000 Binary files a/res/drawable-hdpi/jog_tab_bar_right_end_confirm_red.9.png and /dev/null differ diff --git a/res/drawable-hdpi/jog_tab_bar_right_end_normal.9.png b/res/drawable-hdpi/jog_tab_bar_right_end_normal.9.png deleted file mode 100644 index 49ec1184f..000000000 Binary files a/res/drawable-hdpi/jog_tab_bar_right_end_normal.9.png and /dev/null differ diff --git a/res/drawable-hdpi/jog_tab_bar_right_end_pressed.9.png b/res/drawable-hdpi/jog_tab_bar_right_end_pressed.9.png deleted file mode 100644 index ffc54339d..000000000 Binary files a/res/drawable-hdpi/jog_tab_bar_right_end_pressed.9.png and /dev/null differ diff --git a/res/drawable-hdpi/jog_tab_left_confirm_green.png b/res/drawable-hdpi/jog_tab_left_confirm_green.png deleted file mode 100644 index 23acc95f1..000000000 Binary files a/res/drawable-hdpi/jog_tab_left_confirm_green.png and /dev/null differ diff --git a/res/drawable-hdpi/jog_tab_left_normal.png b/res/drawable-hdpi/jog_tab_left_normal.png deleted file mode 100644 index 18216f946..000000000 Binary files a/res/drawable-hdpi/jog_tab_left_normal.png and /dev/null differ diff --git a/res/drawable-hdpi/jog_tab_left_pressed.png b/res/drawable-hdpi/jog_tab_left_pressed.png deleted file mode 100644 index 86f49c685..000000000 Binary files a/res/drawable-hdpi/jog_tab_left_pressed.png and /dev/null differ diff --git a/res/drawable-hdpi/jog_tab_right_confirm_red.png b/res/drawable-hdpi/jog_tab_right_confirm_red.png deleted file mode 100644 index bb0fa4754..000000000 Binary files a/res/drawable-hdpi/jog_tab_right_confirm_red.png and /dev/null differ diff --git a/res/drawable-hdpi/jog_tab_right_normal.png b/res/drawable-hdpi/jog_tab_right_normal.png deleted file mode 100644 index 68545ef51..000000000 Binary files a/res/drawable-hdpi/jog_tab_right_normal.png and /dev/null differ diff --git a/res/drawable-hdpi/jog_tab_right_pressed.png b/res/drawable-hdpi/jog_tab_right_pressed.png deleted file mode 100644 index 3d75c0d9b..000000000 Binary files a/res/drawable-hdpi/jog_tab_right_pressed.png and /dev/null differ diff --git a/res/drawable-hdpi/jog_tab_target_green.png b/res/drawable-hdpi/jog_tab_target_green.png deleted file mode 100644 index 17f6b101e..000000000 Binary files a/res/drawable-hdpi/jog_tab_target_green.png and /dev/null differ diff --git a/res/drawable-hdpi/jog_tab_target_red.png b/res/drawable-hdpi/jog_tab_target_red.png deleted file mode 100644 index 8db20bb63..000000000 Binary files a/res/drawable-hdpi/jog_tab_target_red.png and /dev/null differ diff --git a/res/drawable/jog_tab_bar_left_answer.xml b/res/drawable/jog_tab_bar_left_answer.xml deleted file mode 100644 index 32ce3dcda..000000000 --- a/res/drawable/jog_tab_bar_left_answer.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - diff --git a/res/drawable/jog_tab_bar_left_end_confirm_green.9.png b/res/drawable/jog_tab_bar_left_end_confirm_green.9.png deleted file mode 100644 index e8be7bf37..000000000 Binary files a/res/drawable/jog_tab_bar_left_end_confirm_green.9.png and /dev/null differ diff --git a/res/drawable/jog_tab_bar_left_end_normal.9.png b/res/drawable/jog_tab_bar_left_end_normal.9.png deleted file mode 100644 index 747745378..000000000 Binary files a/res/drawable/jog_tab_bar_left_end_normal.9.png and /dev/null differ diff --git a/res/drawable/jog_tab_bar_left_end_pressed.9.png b/res/drawable/jog_tab_bar_left_end_pressed.9.png deleted file mode 100644 index c79a35cf3..000000000 Binary files a/res/drawable/jog_tab_bar_left_end_pressed.9.png and /dev/null differ diff --git a/res/drawable/jog_tab_bar_right_decline.xml b/res/drawable/jog_tab_bar_right_decline.xml deleted file mode 100644 index 83183ac1b..000000000 --- a/res/drawable/jog_tab_bar_right_decline.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - diff --git a/res/drawable/jog_tab_bar_right_end_confirm_red.9.png b/res/drawable/jog_tab_bar_right_end_confirm_red.9.png deleted file mode 100644 index 0242a42b5..000000000 Binary files a/res/drawable/jog_tab_bar_right_end_confirm_red.9.png and /dev/null differ diff --git a/res/drawable/jog_tab_bar_right_end_normal.9.png b/res/drawable/jog_tab_bar_right_end_normal.9.png deleted file mode 100644 index 2e6ca2ebf..000000000 Binary files a/res/drawable/jog_tab_bar_right_end_normal.9.png and /dev/null differ diff --git a/res/drawable/jog_tab_bar_right_end_pressed.9.png b/res/drawable/jog_tab_bar_right_end_pressed.9.png deleted file mode 100644 index 8bdfd8406..000000000 Binary files a/res/drawable/jog_tab_bar_right_end_pressed.9.png and /dev/null differ diff --git a/res/drawable/jog_tab_left_answer.xml b/res/drawable/jog_tab_left_answer.xml deleted file mode 100644 index 18ec7fa15..000000000 --- a/res/drawable/jog_tab_left_answer.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - diff --git a/res/drawable/jog_tab_left_confirm_green.png b/res/drawable/jog_tab_left_confirm_green.png deleted file mode 100755 index 829b1462d..000000000 Binary files a/res/drawable/jog_tab_left_confirm_green.png and /dev/null differ diff --git a/res/drawable/jog_tab_left_normal.png b/res/drawable/jog_tab_left_normal.png deleted file mode 100755 index eb91e97ea..000000000 Binary files a/res/drawable/jog_tab_left_normal.png and /dev/null differ diff --git a/res/drawable/jog_tab_left_pressed.png b/res/drawable/jog_tab_left_pressed.png deleted file mode 100755 index 995199292..000000000 Binary files a/res/drawable/jog_tab_left_pressed.png and /dev/null differ diff --git a/res/drawable/jog_tab_right_confirm_red.png b/res/drawable/jog_tab_right_confirm_red.png deleted file mode 100644 index 2e1e105c6..000000000 Binary files a/res/drawable/jog_tab_right_confirm_red.png and /dev/null differ diff --git a/res/drawable/jog_tab_right_decline.xml b/res/drawable/jog_tab_right_decline.xml deleted file mode 100644 index a3bca5e92..000000000 --- a/res/drawable/jog_tab_right_decline.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - diff --git a/res/drawable/jog_tab_right_normal.png b/res/drawable/jog_tab_right_normal.png deleted file mode 100755 index f2113f26e..000000000 Binary files a/res/drawable/jog_tab_right_normal.png and /dev/null differ diff --git a/res/drawable/jog_tab_right_pressed.png b/res/drawable/jog_tab_right_pressed.png deleted file mode 100755 index 65cd51ea6..000000000 Binary files a/res/drawable/jog_tab_right_pressed.png and /dev/null differ diff --git a/res/drawable/jog_tab_target_green.png b/res/drawable/jog_tab_target_green.png deleted file mode 100644 index 188f3cc83..000000000 Binary files a/res/drawable/jog_tab_target_green.png and /dev/null differ diff --git a/res/drawable/jog_tab_target_red.png b/res/drawable/jog_tab_target_red.png deleted file mode 100644 index a36394dc5..000000000 Binary files a/res/drawable/jog_tab_target_red.png and /dev/null differ diff --git a/res/drawable/slider_left.9.png b/res/drawable/slider_left.9.png new file mode 100644 index 000000000..4cb9421d9 Binary files /dev/null and b/res/drawable/slider_left.9.png differ diff --git a/res/drawable/slider_right.9.png b/res/drawable/slider_right.9.png new file mode 100644 index 000000000..d0a8f1670 Binary files /dev/null and b/res/drawable/slider_right.9.png differ diff --git a/res/layout/chat.xml b/res/layout/chat.xml index 06b77a237..d387c12a8 100644 --- a/res/layout/chat.xml +++ b/res/layout/chat.xml @@ -62,7 +62,7 @@ android:textColor="@android:color/black" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_weight="0.3" + android:layout_weight="0.21" android:background="@drawable/chat_message_background" android:padding="15dp"/> @@ -71,7 +71,7 @@ android:id="@+id/sendMessage" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_weight="0.7" + android:layout_weight="0.79" android:scaleType="fitXY" android:src="@drawable/chat_send_message" /> diff --git a/res/layout/incoming.xml b/res/layout/incoming.xml index 354b05863..80d2d347f 100644 --- a/res/layout/incoming.xml +++ b/res/layout/incoming.xml @@ -49,12 +49,11 @@ - + android:layout_marginBottom="80dip" /> diff --git a/src/org/linphone/IncomingCallActivity.java b/src/org/linphone/IncomingCallActivity.java index aadb04bec..932057d91 100644 --- a/src/org/linphone/IncomingCallActivity.java +++ b/src/org/linphone/IncomingCallActivity.java @@ -25,14 +25,13 @@ import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCall; import org.linphone.core.LinphoneCall.State; import org.linphone.core.Log; -import org.linphone.ui.SlidingTab; -import org.linphone.ui.SlidingTab.OnTriggerListener; +import org.linphone.ui.LinphoneSliders; +import org.linphone.ui.LinphoneSliders.LinphoneSliderTriggered; import android.app.Activity; import android.net.Uri; import android.os.Bundle; import android.view.KeyEvent; -import android.view.View; import android.view.WindowManager; import android.widget.ImageView; import android.widget.TextView; @@ -44,13 +43,13 @@ import android.widget.Toast; * * @author Guillaume Beraudo */ -public class IncomingCallActivity extends Activity implements LinphoneOnCallStateChangedListener, OnTriggerListener { +public class IncomingCallActivity extends Activity implements LinphoneOnCallStateChangedListener, LinphoneSliderTriggered { private TextView mNameView; private TextView mNumberView; private ImageView mPictureView; private LinphoneCall mCall; - private SlidingTab mIncomingCallWidget; + private LinphoneSliders mIncomingCallWidget; @Override protected void onCreate(Bundle savedInstanceState) { @@ -67,13 +66,7 @@ public class IncomingCallActivity extends Activity implements LinphoneOnCallStat // "Dial-to-answer" widget for incoming calls. - mIncomingCallWidget = (SlidingTab) findViewById(R.id.sliding_widget); - - // For now, we only need to show two states: answer and decline. - // TODO: make left hint work -// mIncomingCallWidget.setLeftHintText(R.string.slide_to_answer_hint); -// mIncomingCallWidget.setRightHintText(R.string.slide_to_decline_hint); - + mIncomingCallWidget = (LinphoneSliders) findViewById(R.id.sliding_widget); mIncomingCallWidget.setOnTriggerListener(this); @@ -147,24 +140,16 @@ public class IncomingCallActivity extends Activity implements LinphoneOnCallStat LinphoneActivity.instance().startIncallActivity(mCall); } } + @Override - public void onGrabbedStateChange(View v, int grabbedState) { + public void onLeftHandleTriggered() { + answer(); + finish(); } @Override - public void onTrigger(View v, int whichHandle) { - switch (whichHandle) { - case OnTriggerListener.LEFT_HANDLE: - answer(); - finish(); - break; - case OnTriggerListener.RIGHT_HANDLE: - decline(); - finish(); - break; - default: - break; - } + public void onRightHandleTriggered() { + decline(); + finish(); } - } diff --git a/src/org/linphone/ui/LinphoneSliders.java b/src/org/linphone/ui/LinphoneSliders.java new file mode 100644 index 000000000..b890516e0 --- /dev/null +++ b/src/org/linphone/ui/LinphoneSliders.java @@ -0,0 +1,140 @@ +package org.linphone.ui; + +import org.linphone.R; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.GestureDetector; +import android.view.GestureDetector.OnGestureListener; +import android.view.MotionEvent; +import android.view.View; + +/* +LinphoneSliders.java +Copyright (C) 2012 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/** + * @author Sylvain Berfini + */ +public class LinphoneSliders extends View implements OnGestureListener { + private Drawable leftSliderImg, rightSliderImg; + private int leftSliderX, rightSliderX; + private int slidersHeight, slidersWidth; + private GestureDetector mGestures; + private LinphoneSliderTriggered mTriggerListener; + private boolean slidingLeftHandle, slidingRightHandle; + private static final double mCoeff = 0.5; + + public LinphoneSliders(Context context, AttributeSet attrs) { + super(context, attrs); + mGestures = new GestureDetector(getContext(), this); + leftSliderImg = getResources().getDrawable(R.drawable.slider_left); + rightSliderImg = getResources().getDrawable(R.drawable.slider_right); + + slidersHeight = leftSliderImg.getIntrinsicHeight(); + slidersWidth = leftSliderImg.getIntrinsicWidth(); + + leftSliderX = 0; + rightSliderX = 0; + slidingLeftHandle = slidingRightHandle = false; + } + + @Override + protected void onDraw(Canvas canvas) { + rightSliderImg.setBounds(getWidth() - slidersWidth - rightSliderX, getHeight() - slidersHeight, getWidth(), getHeight()); + rightSliderImg.draw(canvas); + + leftSliderImg.setBounds(0, getHeight() - slidersHeight, slidersWidth + leftSliderX, getHeight()); + leftSliderImg.draw(canvas); + + if (slidingLeftHandle && Math.abs(leftSliderX) >= mCoeff * getWidth()) { + mTriggerListener.onLeftHandleTriggered(); + } else if (slidingRightHandle && rightSliderX >= mCoeff * getWidth()) { + mTriggerListener.onRightHandleTriggered(); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_UP) { + leftSliderX = 0; + rightSliderX = 0; + slidingLeftHandle = slidingRightHandle = false; + invalidate(); + } + + return mGestures.onTouchEvent(event); + } + + @Override + public boolean onDown(MotionEvent e) { + return true; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { + return false; + } + + @Override + public void onLongPress(MotionEvent e) { + + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, + float distanceY) { + if (e1.getY() < getHeight() - slidersHeight) { + return false; + } + + if (e1.getX() < getWidth() / 2) { + leftSliderX -= distanceX; + slidingLeftHandle = true; + } else { + rightSliderX += distanceX; + slidingRightHandle = true; + } + invalidate(); + + return true; + } + + @Override + public void onShowPress(MotionEvent e) { + + } + + @Override + public boolean onSingleTapUp(MotionEvent e) { + return false; + } + + public void setOnTriggerListener(LinphoneSliderTriggered listener) { + mTriggerListener = listener; + } + + public interface LinphoneSliderTriggered { + public void onLeftHandleTriggered(); + public void onRightHandleTriggered(); + } +} + diff --git a/src/org/linphone/ui/SlidingTab.java b/src/org/linphone/ui/SlidingTab.java deleted file mode 100644 index 3409e1df1..000000000 --- a/src/org/linphone/ui/SlidingTab.java +++ /dev/null @@ -1,843 +0,0 @@ -/* - * Derived from Android "SlidingTab" source. - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linphone.ui; - - -import org.linphone.R; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.Vibrator; -import android.util.AttributeSet; -import android.util.Log; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.view.animation.Animation.AnimationListener; -import android.view.animation.LinearInterpolator; -import android.view.animation.TranslateAnimation; -import android.widget.ImageView; -import android.widget.ImageView.ScaleType; -import android.widget.TextView; - -/** - * A special widget containing two Sliders and a threshold for each. Moving either slider beyond - * the threshold will cause the registered OnTriggerListener.onTrigger() to be called with - * whichHandle being {@link OnTriggerListener#LEFT_HANDLE} or {@link OnTriggerListener#RIGHT_HANDLE} - * Equivalently, selecting a tab will result in a call to - * {@link OnTriggerListener#onGrabbedStateChange(View, int)} with one of these two states. Releasing - * the tab will result in whichHandle being {@link OnTriggerListener#NO_HANDLE}. - * - */ -public class SlidingTab extends ViewGroup { - private static final String LOG_TAG = "SlidingTab"; - private static final int HORIZONTAL = 0; // as defined in attrs.xml - - // TODO: Make these configurable - private static final float THRESHOLD = 2.0f / 3.0f; - private static final long VIBRATE_SHORT = 30; - private static final long VIBRATE_LONG = 40; - private static final int TRACKING_MARGIN = 50; - private static final int ANIM_DURATION = 250; // Time for most animations (in ms) - private static final int ANIM_TARGET_TIME = 500; // Time to show targets (in ms) - private boolean mHoldLeftOnTransition = false; - private boolean mHoldRightOnTransition = false; - - private OnTriggerListener mOnTriggerListener; - private int mGrabbedState = OnTriggerListener.NO_HANDLE; - private boolean mTriggered = false; - private Vibrator mVibrator; - - /** - * Either {@link #HORIZONTAL} or {@link #VERTICAL}. - */ - private int mOrientation; - - private Slider mLeftSlider; - private Slider mRightSlider; - private Slider mCurrentSlider; - private boolean mTracking; - private float mThreshold; - private Slider mOtherSlider; - private boolean mAnimating; - private Rect mTmpRect; - - /** - * Listener used to reset the view when the current animation completes. - */ - private final AnimationListener mAnimationDoneListener = new AnimationListener() { - public void onAnimationStart(Animation animation) { - - } - - public void onAnimationRepeat(Animation animation) { - - } - - public void onAnimationEnd(Animation animation) { - onAnimationDone(); - } - }; - - /** - * Interface definition for a callback to be invoked when a tab is triggered - * by moving it beyond a threshold. - */ - public interface OnTriggerListener { - /** - * The interface was triggered because the user let go of the handle without reaching the - * threshold. - */ - public static final int NO_HANDLE = 0; - - /** - * The interface was triggered because the user grabbed the left handle and moved it past - * the threshold. - */ - public static final int LEFT_HANDLE = 1; - - /** - * The interface was triggered because the user grabbed the right handle and moved it past - * the threshold. - */ - public static final int RIGHT_HANDLE = 2; - - /** - * Called when the user moves a handle beyond the threshold. - * - * @param v The view that was triggered. - * @param whichHandle Which "dial handle" the user grabbed, - * either {@link #LEFT_HANDLE}, {@link #RIGHT_HANDLE}. - */ - void onTrigger(View v, int whichHandle); - - /** - * Called when the "grabbed state" changes (i.e. when the user either grabs or releases - * one of the handles.) - * - * @param v the view that was triggered - * @param grabbedState the new state: {@link #NO_HANDLE}, {@link #LEFT_HANDLE}, - * or {@link #RIGHT_HANDLE}. - */ - void onGrabbedStateChange(View v, int grabbedState); - } - - /** - * Simple container class for all things pertinent to a slider. - * A slider consists of 3 Views: - * - * {@link #tab} is the tab shown on the screen in the default state. - * {@link #text} is the view revealed as the user slides the tab out. - * {@link #target} is the target the user must drag the slider past to trigger the slider. - * - */ - private static class Slider { - /** - * Tab alignment - determines which side the tab should be drawn on - */ - public static final int ALIGN_LEFT = 0; - public static final int ALIGN_RIGHT = 1; - public static final int ALIGN_TOP = 2; - public static final int ALIGN_BOTTOM = 3; - public static final int ALIGN_UNKNOWN = 4; - - /** - * States for the view. - */ - private static final int STATE_NORMAL = 0; - private static final int STATE_PRESSED = 1; - private static final int STATE_ACTIVE = 2; - - private final ImageView tab; - private final TextView text; - private final ImageView target; - private int currentState = STATE_NORMAL; - private int alignment = ALIGN_UNKNOWN; - private int alignment_value; - - /** - * Constructor - * - * @param parent the container view of this one - * @param tabId drawable for the tab - * @param barId drawable for the bar - * @param targetId drawable for the target - */ - Slider(ViewGroup parent, int iconId, int tabId, int barId, int targetId) { - // Create tab - tab = new ImageView(parent.getContext()); - tab.setImageResource(iconId); - tab.setBackgroundResource(tabId); - tab.setScaleType(ScaleType.FIT_CENTER); - tab.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT)); - - // Create hint TextView - text = new TextView(parent.getContext()); - text.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.FILL_PARENT)); - text.setBackgroundResource(barId); - text.setTextAppearance(parent.getContext(), R.style.TextAppearance_SlidingTabNormal); - // hint.setSingleLine(); // Hmm.. this causes the text to disappear off-screen - - // Create target - target = new ImageView(parent.getContext()); - target.setImageResource(targetId); - target.setScaleType(ScaleType.CENTER); - target.setLayoutParams( - new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); - target.setVisibility(View.INVISIBLE); - - parent.addView(target); // this needs to be first - relies on painter's algorithm - parent.addView(tab); - parent.addView(text); - } - - void setIcon(int iconId) { - tab.setImageResource(iconId); - } - - void setTabBackgroundResource(int tabId) { - tab.setBackgroundResource(tabId); - } - - void setBarBackgroundResource(int barId) { - text.setBackgroundResource(barId); - } - - void setHintText(int resId) { - text.setText(resId); - } - - void hide() { - boolean horiz = alignment == ALIGN_LEFT || alignment == ALIGN_RIGHT; - int dx = horiz ? (alignment == ALIGN_LEFT ? alignment_value - tab.getRight() - : alignment_value - tab.getLeft()) : 0; - int dy = horiz ? 0 : (alignment == ALIGN_TOP ? alignment_value - tab.getBottom() - : alignment_value - tab.getTop()); - - Animation trans = new TranslateAnimation(0, dx, 0, dy); - trans.setDuration(ANIM_DURATION); - trans.setFillAfter(true); - tab.startAnimation(trans); - text.startAnimation(trans); - target.setVisibility(View.INVISIBLE); - } - - void show(boolean animate) { - text.setVisibility(View.VISIBLE); - tab.setVisibility(View.VISIBLE); - //target.setVisibility(View.INVISIBLE); - if (animate) { - boolean horiz = alignment == ALIGN_LEFT || alignment == ALIGN_RIGHT; - int dx = horiz ? (alignment == ALIGN_LEFT ? tab.getWidth() : -tab.getWidth()) : 0; - int dy = horiz ? 0: (alignment == ALIGN_TOP ? tab.getHeight() : -tab.getHeight()); - - Animation trans = new TranslateAnimation(-dx, 0, -dy, 0); - trans.setDuration(ANIM_DURATION); - tab.startAnimation(trans); - text.startAnimation(trans); - } - } - - void setState(int state) { - text.setPressed(state == STATE_PRESSED); - tab.setPressed(state == STATE_PRESSED); - if (state == STATE_ACTIVE) { - final int[] activeState = new int[] {android.R.attr.state_active}; - if (text.getBackground().isStateful()) { - text.getBackground().setState(activeState); - } - if (tab.getBackground().isStateful()) { - tab.getBackground().setState(activeState); - } - text.setTextAppearance(text.getContext(), R.style.TextAppearance_SlidingTabActive); - } else { - text.setTextAppearance(text.getContext(), R.style.TextAppearance_SlidingTabNormal); - } - currentState = state; - } - - void showTarget() { - AlphaAnimation alphaAnim = new AlphaAnimation(0.0f, 1.0f); - alphaAnim.setDuration(ANIM_TARGET_TIME); - target.startAnimation(alphaAnim); - target.setVisibility(View.VISIBLE); - } - - void reset(boolean animate) { - setState(STATE_NORMAL); - text.setVisibility(View.VISIBLE); - text.setTextAppearance(text.getContext(), R.style.TextAppearance_SlidingTabNormal); - tab.setVisibility(View.VISIBLE); - target.setVisibility(View.INVISIBLE); - final boolean horiz = alignment == ALIGN_LEFT || alignment == ALIGN_RIGHT; - int dx = horiz ? (alignment == ALIGN_LEFT ? alignment_value - tab.getLeft() - : alignment_value - tab.getRight()) : 0; - int dy = horiz ? 0 : (alignment == ALIGN_TOP ? alignment_value - tab.getTop() - : alignment_value - tab.getBottom()); - if (animate) { - TranslateAnimation trans = new TranslateAnimation(0, dx, 0, dy); - trans.setDuration(ANIM_DURATION); - trans.setFillAfter(false); - text.startAnimation(trans); - tab.startAnimation(trans); - } else { - if (horiz) { - text.offsetLeftAndRight(dx); - tab.offsetLeftAndRight(dx); - } else { - text.offsetTopAndBottom(dy); - tab.offsetTopAndBottom(dy); - } - text.clearAnimation(); - tab.clearAnimation(); - target.clearAnimation(); - } - } - - void setTarget(int targetId) { - target.setImageResource(targetId); - } - - /** - * Layout the given widgets within the parent. - * - * @param l the parent's left border - * @param t the parent's top border - * @param r the parent's right border - * @param b the parent's bottom border - * @param alignment which side to align the widget to - */ - void layout(int l, int t, int r, int b, int alignment) { - this.alignment = alignment; - final Drawable tabBackground = tab.getBackground(); - final int handleWidth = tabBackground.getIntrinsicWidth(); - final int handleHeight = tabBackground.getIntrinsicHeight(); - final Drawable targetDrawable = target.getDrawable(); - final int targetWidth = targetDrawable.getIntrinsicWidth(); - final int targetHeight = targetDrawable.getIntrinsicHeight(); - final int parentWidth = r - l; - final int parentHeight = b - t; - - final int leftTarget = (int) (THRESHOLD * parentWidth) - targetWidth + handleWidth / 2; - final int rightTarget = (int) ((1.0f - THRESHOLD) * parentWidth) - handleWidth / 2; - final int left = (parentWidth - handleWidth) / 2; - final int right = left + handleWidth; - - if (alignment == ALIGN_LEFT || alignment == ALIGN_RIGHT) { - // horizontal - final int targetTop = (parentHeight - targetHeight) / 2; - final int targetBottom = targetTop + targetHeight; - final int top = (parentHeight - handleHeight) / 2; - final int bottom = (parentHeight + handleHeight) / 2; - if (alignment == ALIGN_LEFT) { - tab.layout(0, top, handleWidth, bottom); - text.layout(0 - parentWidth, top, 0, bottom); - text.setGravity(Gravity.RIGHT); - target.layout(leftTarget, targetTop, leftTarget + targetWidth, targetBottom); - alignment_value = l; - } else { - tab.layout(parentWidth - handleWidth, top, parentWidth, bottom); - text.layout(parentWidth, top, parentWidth + parentWidth, bottom); - target.layout(rightTarget, targetTop, rightTarget + targetWidth, targetBottom); - text.setGravity(Gravity.TOP); - alignment_value = r; - } - } else { - // vertical - final int targetLeft = (parentWidth - targetWidth) / 2; - final int targetRight = (parentWidth + targetWidth) / 2; - final int top = (int) (THRESHOLD * parentHeight) + handleHeight / 2 - targetHeight; - final int bottom = (int) ((1.0f - THRESHOLD) * parentHeight) - handleHeight / 2; - if (alignment == ALIGN_TOP) { - tab.layout(left, 0, right, handleHeight); - text.layout(left, 0 - parentHeight, right, 0); - target.layout(targetLeft, top, targetRight, top + targetHeight); - alignment_value = t; - } else { - tab.layout(left, parentHeight - handleHeight, right, parentHeight); - text.layout(left, parentHeight, right, parentHeight + parentHeight); - target.layout(targetLeft, bottom, targetRight, bottom + targetHeight); - alignment_value = b; - } - } - } - - public void updateDrawableStates() { - setState(currentState); - } - - /** - * Ensure all the dependent widgets are measured. - */ - public void measure() { - tab.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), - View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); - text.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), - View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); - } - - /** - * Get the measured tab width. Must be called after {@link Slider#measure()}. - * @return - */ - public int getTabWidth() { - return tab.getMeasuredWidth(); - } - - /** - * Get the measured tab width. Must be called after {@link Slider#measure()}. - * @return - */ - public int getTabHeight() { - return tab.getMeasuredHeight(); - } - - /** - * Start animating the slider. Note we need two animations since an Animator - * keeps internal state of the invalidation region which is just the view being animated. - * - * @param anim1 - * @param anim2 - */ - public void startAnimation(Animation anim1, Animation anim2) { - tab.startAnimation(anim1); - text.startAnimation(anim2); - } - - public void hideTarget() { - target.clearAnimation(); - target.setVisibility(View.INVISIBLE); - } - } - - public SlidingTab(Context context) { - this(context, null); - } - - /** - * Constructor used when this widget is created from a layout file. - */ - public SlidingTab(Context context, AttributeSet attrs) { - super(context, attrs); - - // Allocate a temporary once that can be used everywhere. - mTmpRect = new Rect(); - - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlidingTab); - mOrientation = a.getInt(org.linphone.R.styleable.SlidingTab_orientation, HORIZONTAL); - a.recycle(); - mLeftSlider = new Slider(this, - R.drawable.call_answer_default, - R.drawable.jog_tab_left_answer, - R.drawable.jog_tab_bar_left_answer, - R.drawable.jog_tab_target_green - ); - mRightSlider = new Slider(this, - R.drawable.call_refused_icon, - R.drawable.jog_tab_right_decline, - R.drawable.jog_tab_bar_right_decline, - R.drawable.jog_tab_target_red - ); - - // setBackgroundColor(0x80808080); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); - int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); - - int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); - int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); - - if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) { - Log.e("SlidingTab", "SlidingTab cannot have UNSPECIFIED MeasureSpec" - +"(wspec=" + widthSpecMode + ", hspec=" + heightSpecMode + ")", - new RuntimeException(LOG_TAG + "stack:")); - } - - mLeftSlider.measure(); - mRightSlider.measure(); - final int leftTabWidth = mLeftSlider.getTabWidth(); - final int rightTabWidth = mRightSlider.getTabWidth(); - final int leftTabHeight = mLeftSlider.getTabHeight(); - final int rightTabHeight = mRightSlider.getTabHeight(); - final int width; - final int height; - if (isHorizontal()) { - width = Math.max(widthSpecSize, leftTabWidth + rightTabWidth); - height = Math.max(leftTabHeight, rightTabHeight); - } else { - width = Math.max(leftTabWidth, rightTabHeight); - height = Math.max(heightSpecSize, leftTabHeight + rightTabHeight); - } - setMeasuredDimension(width, height); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent event) { - final int action = event.getAction(); - final float x = event.getX(); - final float y = event.getY(); - - if (mAnimating) { - return false; - } - - View leftHandle = mLeftSlider.tab; - leftHandle.getHitRect(mTmpRect); - boolean leftHit = mTmpRect.contains((int) x, (int) y); - - View rightHandle = mRightSlider.tab; - rightHandle.getHitRect(mTmpRect); - boolean rightHit = mTmpRect.contains((int)x, (int) y); - - if (!mTracking && !(leftHit || rightHit)) { - return false; - } - - switch (action) { - case MotionEvent.ACTION_DOWN: { - mTracking = true; - mTriggered = false; - vibrate(VIBRATE_SHORT); - if (leftHit) { - mCurrentSlider = mLeftSlider; - mOtherSlider = mRightSlider; - mThreshold = isHorizontal() ? THRESHOLD : 1.0f - THRESHOLD; - setGrabbedState(OnTriggerListener.LEFT_HANDLE); - } else { - mCurrentSlider = mRightSlider; - mOtherSlider = mLeftSlider; - mThreshold = isHorizontal() ? 1.0f - THRESHOLD : THRESHOLD; - setGrabbedState(OnTriggerListener.RIGHT_HANDLE); - } - mCurrentSlider.setState(Slider.STATE_PRESSED); - mCurrentSlider.showTarget(); - mOtherSlider.hide(); - break; - } - } - - return true; - } - - /** - * Reset the tabs to their original state and stop any existing animation. - * Animate them back into place if animate is true. - * - * @param animate - */ - public void reset(boolean animate) { - mLeftSlider.reset(animate); - mRightSlider.reset(animate); - if (!animate) { - mAnimating = false; - } - } - - @Override - public void setVisibility(int visibility) { - // Clear animations so sliders don't continue to animate when we show the widget again. - if (visibility != getVisibility() && visibility == View.INVISIBLE) { - reset(false); - } - super.setVisibility(visibility); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (mTracking) { - final int action = event.getAction(); - final float x = event.getX(); - final float y = event.getY(); - - switch (action) { - case MotionEvent.ACTION_MOVE: - if (withinView(x, y, this) ) { - moveHandle(x, y); - float position = isHorizontal() ? x : y; - float target = mThreshold * (isHorizontal() ? getWidth() : getHeight()); - boolean thresholdReached; - if (isHorizontal()) { - thresholdReached = mCurrentSlider == mLeftSlider ? - position > target : position < target; - } else { - thresholdReached = mCurrentSlider == mLeftSlider ? - position < target : position > target; - } - if (!mTriggered && thresholdReached) { - mTriggered = true; - mTracking = false; - mCurrentSlider.setState(Slider.STATE_ACTIVE); - boolean isLeft = mCurrentSlider == mLeftSlider; - dispatchTriggerEvent(isLeft ? - OnTriggerListener.LEFT_HANDLE : OnTriggerListener.RIGHT_HANDLE); - - startAnimating(isLeft ? mHoldLeftOnTransition : mHoldRightOnTransition); - setGrabbedState(OnTriggerListener.NO_HANDLE); - } - break; - } - // Intentionally fall through - we're outside tracking rectangle - - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mTracking = false; - mTriggered = false; - mOtherSlider.show(true); - mCurrentSlider.reset(false); - mCurrentSlider.hideTarget(); - mCurrentSlider = null; - mOtherSlider = null; - setGrabbedState(OnTriggerListener.NO_HANDLE); - break; - } - } - - return mTracking || super.onTouchEvent(event); - } - - void startAnimating(final boolean holdAfter) { - mAnimating = true; - final Animation trans1; - final Animation trans2; - final Slider slider = mCurrentSlider; - final int dx; - final int dy; - if (isHorizontal()) { - int right = slider.tab.getRight(); - int width = slider.tab.getWidth(); - int left = slider.tab.getLeft(); - int viewWidth = getWidth(); - int holdOffset = holdAfter ? 0 : width; // how much of tab to show at the end of anim - dx = slider == mRightSlider ? - (right + viewWidth - holdOffset) - : (viewWidth - left) + viewWidth - holdOffset; - dy = 0; - } else { - int top = slider.tab.getTop(); - int bottom = slider.tab.getBottom(); - int height = slider.tab.getHeight(); - int viewHeight = getHeight(); - int holdOffset = holdAfter ? 0 : height; // how much of tab to show at end of anim - dx = 0; - dy = slider == mRightSlider ? (top + viewHeight - holdOffset) - : - ((viewHeight - bottom) + viewHeight - holdOffset); - } - trans1 = new TranslateAnimation(0, dx, 0, dy); - trans1.setDuration(ANIM_DURATION); - trans1.setInterpolator(new LinearInterpolator()); - trans1.setFillAfter(true); - trans2 = new TranslateAnimation(0, dx, 0, dy); - trans2.setDuration(ANIM_DURATION); - trans2.setInterpolator(new LinearInterpolator()); - trans2.setFillAfter(true); - - trans1.setAnimationListener(new AnimationListener() { - public void onAnimationEnd(Animation animation) { - Animation anim; - if (holdAfter) { - anim = new TranslateAnimation(dx, dx, dy, dy); - anim.setDuration(1000); // plenty of time for transitions - mAnimating = false; - } else { - anim = new AlphaAnimation(0.5f, 1.0f); - anim.setDuration(ANIM_DURATION); - resetView(); - } - anim.setAnimationListener(mAnimationDoneListener); - - /* Animation can be the same for these since the animation just holds */ - mLeftSlider.startAnimation(anim, anim); - mRightSlider.startAnimation(anim, anim); - } - - public void onAnimationRepeat(Animation animation) { - - } - - public void onAnimationStart(Animation animation) { - - } - - }); - - slider.hideTarget(); - slider.startAnimation(trans1, trans2); - } - - private void onAnimationDone() { - resetView(); - mAnimating = false; - } - - private boolean withinView(final float x, final float y, final View view) { - return isHorizontal() && y > - TRACKING_MARGIN && y < TRACKING_MARGIN + view.getHeight() - || !isHorizontal() && x > -TRACKING_MARGIN && x < TRACKING_MARGIN + view.getWidth(); - } - - private boolean isHorizontal() { - return mOrientation == HORIZONTAL; - } - - private void resetView() { - mLeftSlider.reset(false); - mRightSlider.reset(false); - // onLayout(true, getLeft(), getTop(), getLeft() + getWidth(), getTop() + getHeight()); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - if (!changed) return; - - // Center the widgets in the view - mLeftSlider.layout(l, t, r, b, isHorizontal() ? Slider.ALIGN_LEFT : Slider.ALIGN_BOTTOM); - mRightSlider.layout(l, t, r, b, isHorizontal() ? Slider.ALIGN_RIGHT : Slider.ALIGN_TOP); - } - - private void moveHandle(float x, float y) { - final View handle = mCurrentSlider.tab; - final View content = mCurrentSlider.text; - if (isHorizontal()) { - int deltaX = (int) x - handle.getLeft() - (handle.getWidth() / 2); - handle.offsetLeftAndRight(deltaX); - content.offsetLeftAndRight(deltaX); - } else { - int deltaY = (int) y - handle.getTop() - (handle.getHeight() / 2); - handle.offsetTopAndBottom(deltaY); - content.offsetTopAndBottom(deltaY); - } - invalidate(); // TODO: be more conservative about what we're invalidating - } - - /** - * Sets the left handle icon to a given resource. - * - * The resource should refer to a Drawable object, or use 0 to remove - * the icon. - * - * @param iconId the resource ID of the icon drawable - * @param targetId the resource of the target drawable - * @param barId the resource of the bar drawable (stateful) - * @param tabId the resource of the - */ - public void setLeftTabResources(int iconId, int targetId, int barId, int tabId) { - mLeftSlider.setIcon(iconId); - mLeftSlider.setTarget(targetId); - mLeftSlider.setBarBackgroundResource(barId); - mLeftSlider.setTabBackgroundResource(tabId); - mLeftSlider.updateDrawableStates(); - } - - /** - * Sets the left handle hint text to a given resource string. - * - * @param resId - */ - public void setLeftHintText(int resId) { - if (isHorizontal()) { - mLeftSlider.setHintText(resId); - } - } - - /** - * Sets the right handle icon to a given resource. - * - * The resource should refer to a Drawable object, or use 0 to remove - * the icon. - * - * @param iconId the resource ID of the icon drawable - * @param targetId the resource of the target drawable - * @param barId the resource of the bar drawable (stateful) - * @param tabId the resource of the - */ - public void setRightTabResources(int iconId, int targetId, int barId, int tabId) { - mRightSlider.setIcon(iconId); - mRightSlider.setTarget(targetId); - mRightSlider.setBarBackgroundResource(barId); - mRightSlider.setTabBackgroundResource(tabId); - mRightSlider.updateDrawableStates(); - } - - /** - * Sets the left handle hint text to a given resource string. - * - * @param resId - */ - public void setRightHintText(int resId) { - if (isHorizontal()) { - mRightSlider.setHintText(resId); - } - } - - public void setHoldAfterTrigger(boolean holdLeft, boolean holdRight) { - mHoldLeftOnTransition = holdLeft; - mHoldRightOnTransition = holdRight; - } - - /** - * Triggers haptic feedback. - */ - private synchronized void vibrate(long duration) { - if (mVibrator == null) { - mVibrator = (android.os.Vibrator) - getContext().getSystemService(Context.VIBRATOR_SERVICE); - } - mVibrator.vibrate(duration); - } - - /** - * Registers a callback to be invoked when the user triggers an event. - * - * @param listener the OnDialTriggerListener to attach to this view - */ - public void setOnTriggerListener(OnTriggerListener listener) { - mOnTriggerListener = listener; - } - - /** - * Dispatches a trigger event to listener. Ignored if a listener is not set. - * @param whichHandle the handle that triggered the event. - */ - private void dispatchTriggerEvent(int whichHandle) { - vibrate(VIBRATE_LONG); - if (mOnTriggerListener != null) { - mOnTriggerListener.onTrigger(this, whichHandle); - } - } - - /** - * Sets the current grabbed state, and dispatches a grabbed state change - * event to our listener. - */ - private void setGrabbedState(int newState) { - if (newState != mGrabbedState) { - mGrabbedState = newState; - if (mOnTriggerListener != null) { - mOnTriggerListener.onGrabbedStateChange(this, mGrabbedState); - } - } - } -}