Selaa lähdekoodia

通话界面增加音量调节,修改通话背景

weizhengliang 1 vuosi sitten
vanhempi
commit
0553bb4336

+ 37 - 2
android_bed/src/main/h7_3128/java/com/wdkl/app/ncs/callingbed/fragment/SkyCallFragment.kt

@@ -6,6 +6,7 @@ import android.os.SystemClock
 import android.util.Log
 import android.view.View
 import android.view.ViewGroup
+import android.widget.SeekBar
 import com.google.gson.Gson
 import com.wdkl.app.ncs.callingbed.R
 import com.wdkl.app.ncs.callingbed.activity.CallingbedActivity
@@ -61,6 +62,8 @@ class SkyCallFragment: BaseCallFragment(), CallSessionCallback {
 
     private var janusInit = false
 
+    private var volume = 60
+
     override fun getLayId(): Int {
         return R.layout.sky_voice_call_layout
     }
@@ -97,7 +100,14 @@ class SkyCallFragment: BaseCallFragment(), CallSessionCallback {
             WebRTCEngine.getInstance().init(true, this.context)
         }
 
-        VoiceManagerUtil.setCallVoice(activity, SettingConfig.getExtensionCallVolume(activity))
+        volume = SettingConfig.getExtensionCallVolume(activity)
+        if (volume < 0 || volume > 100) {
+            volume = 60
+        }
+        call_volume_bar.progress = volume/10
+        tv_volume.text = "" + volume/10
+
+        VoiceManagerUtil.setCallVoice(activity, volume)
         SerialPortHelper.setHandsMIC(true)
 
         //初始化 janusClient
@@ -198,6 +208,29 @@ class SkyCallFragment: BaseCallFragment(), CallSessionCallback {
             acceptCall()
             janusClient!!.connect(-1, false)
         }
+
+        call_volume_bar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
+            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
+                tv_volume.text = "" + progress
+                if (seekBar!!.progress <= 1) {
+                    tv_volume.text = "1"
+                } else {
+                    tv_volume.text = "" + progress
+                }
+            }
+
+            override fun onStartTrackingTouch(seekBar: SeekBar?) {
+                //
+            }
+
+            override fun onStopTrackingTouch(seekBar: SeekBar?) {
+                if (seekBar!!.progress <= 2) {
+                    VoiceManagerUtil.setCallVoice(activity, 20)
+                } else {
+                    VoiceManagerUtil.setCallVoice(activity, seekBar.progress*10)
+                }
+            }
+        })
     }
 
     override fun destroy() {
@@ -313,6 +346,7 @@ class SkyCallFragment: BaseCallFragment(), CallSessionCallback {
         sky_voice_call_timer.visibility = View.VISIBLE
         sky_voice_call_timer.base = SystemClock.elapsedRealtime()
         sky_voice_call_timer.start()
+        ll_voice_volume_bar.visibility = View.VISIBLE
 
         //通话时关闭指示灯,防止热量太高导致设备重启
         SerialPortHelper.setCallStatus("0")
@@ -364,8 +398,9 @@ class SkyCallFragment: BaseCallFragment(), CallSessionCallback {
     override fun didChangeState(var1: EnumType.CallState?) {
         Log.e(TAG, "didChangeState: $var1")
         if (var1 == EnumType.CallState.Connected) {
+            RingPlayHelper.stopRingTone()
+
             handler.post {
-                RingPlayHelper.stopRingTone()
                 //更新界面显示
                 showCalling(audioCall)
 

+ 39 - 2
android_bed/src/main/h7_3128/res/layout/sky_voice_call_layout.xml

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
-<layout>
+<layout xmlns:app="http://schemas.android.com/apk/res-auto">
     <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="@color/gray_deep">
+        android:background="@mipmap/call_bg">
         <!--全屏视频画面-->
         <FrameLayout
             android:id="@+id/fullscreen_video_frame"
@@ -130,6 +130,43 @@
                     android:layout_height="1dp"
                     android:layout_weight="2" />
             </LinearLayout>
+
+            <LinearLayout
+                android:id="@+id/ll_voice_volume_bar"
+                android:layout_width="48dp"
+                android:layout_height="match_parent"
+                android:layout_alignParentRight="true"
+                android:layout_marginRight="120dp"
+                android:layout_marginTop="100dp"
+                android:layout_marginBottom="100dp"
+                android:clipChildren="true"
+                android:orientation="vertical"
+                android:gravity="center"
+                android:visibility="gone">
+                <TextView
+                    android:id="@+id/tv_volume"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:gravity="center"
+                    android:textSize="24sp"
+                    android:textColor="@color/title_text"/>
+
+                <com.wdkl.ncs.android.lib.widget.VerticalSeekBarWrapper
+                    android:layout_width="24dp"
+                    android:layout_height="match_parent">
+                    <com.wdkl.ncs.android.lib.widget.VerticalSeekBar
+                        android:id="@+id/call_volume_bar"
+                        android:layout_width="0dp"
+                        android:layout_height="0dp"
+                        android:padding="8dp"
+                        android:max="10"
+                        android:progress="6"
+                        android:splitTrack="false"
+                        android:progressDrawable="@drawable/seek_bar_bg"
+                        android:thumb="@drawable/seek_bar_thumb"
+                        app:seekBarRotation="CW270" /> <!-- Rotation: CW90 or CW270 -->
+                </com.wdkl.ncs.android.lib.widget.VerticalSeekBarWrapper>
+            </LinearLayout>
         </RelativeLayout>
     </FrameLayout>
 </layout>

+ 403 - 0
common/src/main/code/com/wdkl/ncs/android/lib/widget/VerticalSeekBar.java

@@ -0,0 +1,403 @@
+/*
+ *    Copyright (C) 2015 Haruki Hasegawa
+ *
+ *    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.
+ */
+
+/* This file contains AOSP code copied from /frameworks/base/core/java/android/widget/AbsSeekBar.java */
+/*============================================================================*/
+/*
+ * Copyright (C) 2007 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 com.wdkl.ncs.android.lib.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.support.v4.view.ViewCompat;
+import android.support.v7.widget.AppCompatSeekBar;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.ProgressBar;
+
+import com.wdkl.ncs.android.lib.R;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class VerticalSeekBar extends AppCompatSeekBar {
+    public static final int ROTATION_ANGLE_CW_90 = 90;
+    public static final int ROTATION_ANGLE_CW_270 = 270;
+
+    private boolean mIsDragging;
+    private Drawable mThumb_;
+    private Method mMethodSetProgressFromUser;
+    private int mRotationAngle = ROTATION_ANGLE_CW_90;
+
+    public VerticalSeekBar(Context context) {
+        super(context);
+        initialize(context, null, 0, 0);
+    }
+
+    public VerticalSeekBar(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initialize(context, attrs, 0, 0);
+    }
+
+    public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        initialize(context, attrs, defStyle, 0);
+    }
+
+    private void initialize(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        ViewCompat.setLayoutDirection(this, ViewCompat.LAYOUT_DIRECTION_LTR);
+
+        if (attrs != null) {
+            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VerticalSeekBar, defStyleAttr, defStyleRes);
+            final int rotationAngle = a.getInteger(R.styleable.VerticalSeekBar_seekBarRotation, 0);
+            if (isValidRotationAngle(rotationAngle)) {
+                mRotationAngle = rotationAngle;
+            }
+            a.recycle();
+        }
+    }
+
+    @Override
+    public void setThumb(Drawable thumb) {
+        mThumb_ = thumb;
+        super.setThumb(thumb);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        if (useViewRotation()) {
+            return onTouchEventUseViewRotation(event);
+        } else {
+            return onTouchEventTraditionalRotation(event);
+        }
+    }
+
+    private boolean onTouchEventTraditionalRotation(MotionEvent event) {
+        if (!isEnabled()) {
+            return false;
+        }
+
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                setPressed(true);
+                onStartTrackingTouch();
+                trackTouchEvent(event);
+                attemptClaimDrag(true);
+                invalidate();
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                if (mIsDragging) {
+                    trackTouchEvent(event);
+                }
+                break;
+
+            case MotionEvent.ACTION_UP:
+                if (mIsDragging) {
+                    trackTouchEvent(event);
+                    onStopTrackingTouch();
+                    setPressed(false);
+                } else {
+                    // Touch up when we never crossed the touch slop threshold
+                    // should
+                    // be interpreted as a tap-seek to that location.
+                    onStartTrackingTouch();
+                    trackTouchEvent(event);
+                    onStopTrackingTouch();
+                    attemptClaimDrag(false);
+                }
+                // ProgressBar doesn't know to repaint the thumb drawable
+                // in its inactive state when the touch stops (because the
+                // value has not apparently changed)
+                invalidate();
+                break;
+
+            case MotionEvent.ACTION_CANCEL:
+                if (mIsDragging) {
+                    onStopTrackingTouch();
+                    setPressed(false);
+                }
+                invalidate(); // see above explanation
+                break;
+        }
+        return true;
+    }
+
+    private boolean onTouchEventUseViewRotation(MotionEvent event) {
+        boolean handled = super.onTouchEvent(event);
+
+        if (handled) {
+            int action = event.getAction();
+            switch (action) {
+                case MotionEvent.ACTION_DOWN:
+                    attemptClaimDrag(true);
+                    break;
+
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    attemptClaimDrag(false);
+                    break;
+            }
+        }
+
+        return handled;
+    }
+
+    private void trackTouchEvent(MotionEvent event) {
+        final int paddingLeft = super.getPaddingLeft();
+        final int paddingRight = super.getPaddingRight();
+        final int height = getHeight();
+
+        final int available = height - paddingLeft - paddingRight;
+        int y = (int) event.getY();
+
+        final float scale;
+        float value = 0;
+
+        switch (mRotationAngle) {
+            case ROTATION_ANGLE_CW_90:
+                value = y - paddingLeft;
+                break;
+            case ROTATION_ANGLE_CW_270:
+                value = (height - paddingLeft) - y;
+                break;
+        }
+
+        if (value < 0 || available == 0) {
+            scale = 0.0f;
+        } else if (value > available) {
+            scale = 1.0f;
+        } else {
+            scale = value / (float) available;
+        }
+
+        final int max = getMax();
+        final float progress = scale * max;
+
+        _setProgressFromUser((int) progress, true);
+    }
+
+    /**
+     * Tries to claim the user's drag motion, and requests disallowing any
+     * ancestors from stealing events in the drag.
+     */
+    private void attemptClaimDrag(boolean active) {
+        final ViewParent parent = getParent();
+        if (parent != null) {
+            parent.requestDisallowInterceptTouchEvent(active);
+        }
+    }
+
+    /**
+     * This is called when the user has started touching this widget.
+     */
+    private void onStartTrackingTouch() {
+        mIsDragging = true;
+    }
+
+    /**
+     * This is called when the user either releases his touch or the touch is
+     * canceled.
+     */
+    private void onStopTrackingTouch() {
+        mIsDragging = false;
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (isEnabled()) {
+            final boolean handled;
+            int direction = 0;
+
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_DPAD_DOWN:
+                    direction = (mRotationAngle == ROTATION_ANGLE_CW_90) ? 1 : -1;
+                    handled = true;
+                    break;
+                case KeyEvent.KEYCODE_DPAD_UP:
+                    direction = (mRotationAngle == ROTATION_ANGLE_CW_270) ? 1 : -1;
+                    handled = true;
+                    break;
+                case KeyEvent.KEYCODE_DPAD_LEFT:
+                case KeyEvent.KEYCODE_DPAD_RIGHT:
+                    // move view focus to previous/next view
+                    return false;
+                default:
+                    handled = false;
+                    break;
+            }
+
+            if (handled) {
+                final int keyProgressIncrement = getKeyProgressIncrement();
+                int progress = getProgress();
+
+                progress += (direction * keyProgressIncrement);
+
+                if (progress >= 0 && progress <= getMax()) {
+                    _setProgressFromUser(progress, true);
+                }
+
+                return true;
+            }
+        }
+
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public synchronized void setProgress(int progress) {
+        super.setProgress(progress);
+        if (!useViewRotation()) {
+            refreshThumb();
+        }
+    }
+
+    private synchronized void _setProgressFromUser(int progress, boolean fromUser) {
+        if (mMethodSetProgressFromUser == null) {
+            try {
+                Method m;
+                m = ProgressBar.class.getDeclaredMethod("setProgress", int.class, boolean.class);
+                m.setAccessible(true);
+                mMethodSetProgressFromUser = m;
+            } catch (NoSuchMethodException e) {
+            }
+        }
+
+        if (mMethodSetProgressFromUser != null) {
+            try {
+                mMethodSetProgressFromUser.invoke(this, progress, fromUser);
+            } catch (IllegalArgumentException e) {
+            } catch (IllegalAccessException e) {
+            } catch (InvocationTargetException e) {
+            }
+        } else {
+            super.setProgress(progress);
+        }
+        refreshThumb();
+    }
+
+    @Override
+    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (useViewRotation()) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        } else {
+            super.onMeasure(heightMeasureSpec, widthMeasureSpec);
+
+            final ViewGroup.LayoutParams lp = getLayoutParams();
+
+            if (isInEditMode() && (lp != null) && (lp.height >= 0)) {
+                setMeasuredDimension(super.getMeasuredHeight(), lp.height);
+            } else {
+                setMeasuredDimension(super.getMeasuredHeight(), super.getMeasuredWidth());
+            }
+        }
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        if (useViewRotation()) {
+            super.onSizeChanged(w, h, oldw, oldh);
+        } else {
+            super.onSizeChanged(h, w, oldh, oldw);
+        }
+    }
+
+    @Override
+    protected synchronized void onDraw(Canvas canvas) {
+        if (!useViewRotation()) {
+            switch (mRotationAngle) {
+                case ROTATION_ANGLE_CW_90:
+                    canvas.rotate(90);
+                    canvas.translate(0, -super.getWidth());
+                    break;
+                case ROTATION_ANGLE_CW_270:
+                    canvas.rotate(-90);
+                    canvas.translate(-super.getHeight(), 0);
+                    break;
+            }
+        }
+
+        super.onDraw(canvas);
+    }
+
+    public int getRotationAngle() {
+        return mRotationAngle;
+    }
+
+    public void setRotationAngle(int angle) {
+        if (!isValidRotationAngle(angle)) {
+            throw new IllegalArgumentException("Invalid angle specified :" + angle);
+        }
+
+        if (mRotationAngle == angle) {
+            return;
+        }
+
+        mRotationAngle = angle;
+
+        if (useViewRotation()) {
+            VerticalSeekBarWrapper wrapper = getWrapper();
+            if (wrapper != null) {
+                wrapper.applyViewRotation();
+            }
+        } else {
+            requestLayout();
+        }
+    }
+
+    // refresh thumb position
+    private void refreshThumb() {
+        onSizeChanged(super.getWidth(), super.getHeight(), 0, 0);
+    }
+
+    /*package*/ boolean useViewRotation() {
+        return !isInEditMode();
+    }
+
+    private VerticalSeekBarWrapper getWrapper() {
+        final ViewParent parent = getParent();
+
+        if (parent instanceof VerticalSeekBarWrapper) {
+            return (VerticalSeekBarWrapper) parent;
+        } else {
+            return null;
+        }
+    }
+
+    private static boolean isValidRotationAngle(int angle) {
+        return (angle == ROTATION_ANGLE_CW_90 || angle == ROTATION_ANGLE_CW_270);
+    }
+}

+ 189 - 0
common/src/main/code/com/wdkl/ncs/android/lib/widget/VerticalSeekBarWrapper.java

@@ -0,0 +1,189 @@
+/*
+ *    Copyright (C) 2015 Haruki Hasegawa
+ *
+ *    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 com.wdkl.ncs.android.lib.widget;
+
+import android.content.Context;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+public class VerticalSeekBarWrapper extends FrameLayout {
+    public VerticalSeekBarWrapper(Context context) {
+        this(context, null, 0);
+    }
+
+    public VerticalSeekBarWrapper(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public VerticalSeekBarWrapper(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        if (useViewRotation()) {
+            onSizeChangedUseViewRotation(w, h, oldw, oldh);
+        } else {
+            onSizeChangedTraditionalRotation(w, h, oldw, oldh);
+        }
+    }
+
+    private void onSizeChangedTraditionalRotation(int w, int h, int oldw, int oldh) {
+        final VerticalSeekBar seekBar = getChildSeekBar();
+
+        if (seekBar != null) {
+            final int hPadding = getPaddingLeft() + getPaddingRight();
+            final int vPadding = getPaddingTop() + getPaddingBottom();
+            final LayoutParams lp = (LayoutParams) seekBar.getLayoutParams();
+
+            lp.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+            lp.height = Math.max(0, h - vPadding);
+            seekBar.setLayoutParams(lp);
+
+            seekBar.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+            final int seekBarMeasuredWidth = seekBar.getMeasuredWidth();
+            seekBar.measure(
+                    MeasureSpec.makeMeasureSpec(Math.max(0, w - hPadding), MeasureSpec.AT_MOST),
+                    MeasureSpec.makeMeasureSpec(Math.max(0, h - vPadding), MeasureSpec.EXACTLY));
+
+            lp.gravity = Gravity.TOP | Gravity.LEFT;
+            lp.leftMargin = (Math.max(0, w - hPadding) - seekBarMeasuredWidth) / 2;
+            seekBar.setLayoutParams(lp);
+        }
+
+        super.onSizeChanged(w, h, oldw, oldh);
+    }
+
+    private void onSizeChangedUseViewRotation(int w, int h, int oldw, int oldh) {
+        final VerticalSeekBar seekBar = getChildSeekBar();
+
+        if (seekBar != null) {
+            final int hPadding = getPaddingLeft() + getPaddingRight();
+            final int vPadding = getPaddingTop() + getPaddingBottom();
+            seekBar.measure(
+                    MeasureSpec.makeMeasureSpec(Math.max(0, h - vPadding), MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(Math.max(0, w - hPadding), MeasureSpec.AT_MOST));
+        }
+
+        applyViewRotation(w, h);
+        super.onSizeChanged(w, h, oldw, oldh);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final VerticalSeekBar seekBar = getChildSeekBar();
+        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+        if ((seekBar != null) && (widthMode != MeasureSpec.EXACTLY)) {
+            final int seekBarWidth;
+            final int seekBarHeight;
+            final int hPadding = getPaddingLeft() + getPaddingRight();
+            final int vPadding = getPaddingTop() + getPaddingBottom();
+            final int innerContentWidthMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(0, widthSize - hPadding), widthMode);
+            final int innerContentHeightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(0, heightSize - vPadding), heightMode);
+
+            if (useViewRotation()) {
+                seekBar.measure(innerContentHeightMeasureSpec, innerContentWidthMeasureSpec);
+                seekBarWidth = seekBar.getMeasuredHeight();
+                seekBarHeight = seekBar.getMeasuredWidth();
+            } else {
+                seekBar.measure(innerContentWidthMeasureSpec, innerContentHeightMeasureSpec);
+                seekBarWidth = seekBar.getMeasuredWidth();
+                seekBarHeight = seekBar.getMeasuredHeight();
+            }
+
+            final int measuredWidth = resolveSizeAndState(seekBarWidth + hPadding, widthMeasureSpec, 0);
+            final int measuredHeight = resolveSizeAndState(seekBarHeight + vPadding, heightMeasureSpec, 0);
+
+            setMeasuredDimension(measuredWidth, measuredHeight);
+        } else {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    /*package*/ void applyViewRotation() {
+        applyViewRotation(getWidth(), getHeight());
+    }
+
+    private void applyViewRotation(int w, int h) {
+        final VerticalSeekBar seekBar = getChildSeekBar();
+
+        if (seekBar != null) {
+            final boolean isLTR = ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_LTR;
+            final int rotationAngle = seekBar.getRotationAngle();
+            final int seekBarMeasuredWidth = seekBar.getMeasuredWidth();
+            final int seekBarMeasuredHeight = seekBar.getMeasuredHeight();
+            final int hPadding = getPaddingLeft() + getPaddingRight();
+            final int vPadding = getPaddingTop() + getPaddingBottom();
+            final float hOffset = (Math.max(0, w - hPadding) - seekBarMeasuredHeight) * 0.5f;
+            final ViewGroup.LayoutParams lp = seekBar.getLayoutParams();
+
+            lp.width = Math.max(0, h - vPadding);
+            lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+
+            seekBar.setLayoutParams(lp);
+
+            seekBar.setPivotX((isLTR) ? 0 : Math.max(0, h - vPadding));
+            seekBar.setPivotY(0);
+
+            switch (rotationAngle) {
+                case VerticalSeekBar.ROTATION_ANGLE_CW_90:
+                    seekBar.setRotation(90);
+                    if (isLTR) {
+                        seekBar.setTranslationX(seekBarMeasuredHeight + hOffset);
+                        seekBar.setTranslationY(0);
+                    } else {
+                        seekBar.setTranslationX(-hOffset);
+                        seekBar.setTranslationY(seekBarMeasuredWidth);
+                    }
+                    break;
+                case VerticalSeekBar.ROTATION_ANGLE_CW_270:
+                    seekBar.setRotation( 270);
+                    if (isLTR) {
+                        seekBar.setTranslationX(hOffset);
+                        seekBar.setTranslationY(seekBarMeasuredWidth);
+                    } else {
+                        seekBar.setTranslationX(-(seekBarMeasuredHeight + hOffset));
+                        seekBar.setTranslationY(0);
+                    }
+                    break;
+            }
+        }
+    }
+
+    private VerticalSeekBar getChildSeekBar() {
+        final View child = (getChildCount() > 0) ? getChildAt(0) : null;
+        return (child instanceof VerticalSeekBar) ? (VerticalSeekBar) child : null;
+    }
+
+    private boolean useViewRotation() {
+        final VerticalSeekBar seekBar = getChildSeekBar();
+        if (seekBar != null) {
+            return seekBar.useViewRotation();
+        } else {
+            return false;
+        }
+    }
+}

+ 10 - 0
common/src/main/res/values/styles.xml

@@ -8,6 +8,16 @@
         <item name="android:windowIsTranslucent">true</item>
         <item name="windowNoTitle">true</item>
     </style>
+
     <style name="AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
     </style>
+
+    <declare-styleable name="VerticalSeekBar">
+        <attr name="seekBarRotation">
+            <!-- Clock wise - 90 deg; top = min, bottom = max -->
+            <enum name="CW90" value="90" />
+            <!-- Clock wise - 270 deg; bottom = min, top = max -->
+            <enum name="CW270" value="270" />
+        </attr>
+    </declare-styleable>
 </resources>

+ 25 - 0
resource/src/main/res/drawable/seek_bar_bg.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@android:id/background">
+        <shape>
+            <corners android:radius="4dp" />
+            <solid android:color="#ECF0F1" />
+        </shape>
+    </item>
+    <item android:id="@android:id/secondaryProgress">
+        <clip>
+            <shape>
+                <corners android:radius="4dp" />
+                <solid android:color="#C6CACE" />
+            </shape>
+        </clip>
+    </item>
+    <item android:id="@android:id/progress">
+        <clip>
+            <shape>
+                <corners android:radius="4dp" />
+                <solid android:color="#a1eafb" />
+            </shape>
+        </clip>
+    </item>
+</layer-list>

+ 14 - 0
resource/src/main/res/drawable/seek_bar_thumb.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <!-- solid表示远的填充色 -->
+    <solid android:color="#ffcef3" />
+    <!-- stroke则代表远的边框线 -->
+    <stroke
+        android:width="1dp"
+        android:color="#a1eafb" />
+    <!-- size控制高宽 -->
+    <size
+        android:width="24dp"
+        android:height="24dp" />
+</shape>

BIN
resource/src/main/res/mipmap-mdpi/call_bg.png