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);
- }
- }
- }
-}