当前位置: 移动技术网 > IT编程>移动开发>Android > Android6.0 源码修改之Settings音量调节界面增加通话音量调节

Android6.0 源码修改之Settings音量调节界面增加通话音量调节

2019年04月24日  | 移动技术网IT编程  | 我要评论

郎溪租房网,足球之夜20131114,婆婆也是妈mp4下载

前言

今天客户提了个需求,因为我们的设备在正常情况下无法调节通话音量,只有在打电话过程中,按物理音量加减键才能出现调节通话音量seekbar,很不方便,于是乎需求就来了。需要优化两个地方

1、在正常情况下,按物理音量加减键都显示 通话音量调节seekbar,可方便快速调节通话音量

2、在settings中提示音界面点击设置进入,增加通话音量调节seekbar

在这里插入图片描述
在这里插入图片描述

修改前

在这里插入图片描述
在这里插入图片描述

修改后

实现

第一个功能

先来完成第一个功能,还是通过hierarchy view查看布局结构,查找到布局文件id为volume_dialog,通过在源码中搜索找到位于systemui中,volume_dialog.xml
源码位置 frameworks\base\packages\systemui\res\layout\volume_dialog.xml

对应的java类为 frameworks\base\packages\systemui\src\com\android\systemui\volume\volumedialog.java

修改代码

addrow(audiomanager.stream_voice_call,
            r.drawable.ic_volume_voice, r.drawable.ic_volume_voice, true);

原来的第四个参数为false,修改为true即可显示通话音量seekbar

为了便于说明,我们跟进addrow()中查看

private void addrow(int stream, int iconres, int iconmuteres, boolean important) {
    final volumerow row = initrow(stream, iconres, iconmuteres, important);
    if (!mrows.isempty()) {
        final view v = new view(mcontext);
        v.setid(android.r.id.background);
        final int h = mcontext.getresources()
                .getdimensionpixelsize(r.dimen.volume_slider_interspacing);
        final linearlayout.layoutparams lp =
                new linearlayout.layoutparams(linearlayout.layoutparams.match_parent, h);
        mdialogcontentview.addview(v, mdialogcontentview.getchildcount() - 1, lp);
        row.space = v;
    }
    ...
}

传递的参数对应important,从字面意思理解重要对应显示,继续查看initrow都做了什么

private volumerow initrow(final int stream, int iconres, int iconmuteres, boolean important) {
    final volumerow row = new volumerow();
    row.stream = stream;
    row.iconres = iconres;
    row.iconmuteres = iconmuteres;
    row.important = important;
    row.view = mdialog.getlayoutinflater().inflate(r.layout.volume_dialog_row, null);
    row.view.settag(row);
    row.header = (textview) row.view.findviewbyid(r.id.volume_row_header);
    msptexts.add(row.header);
    row.slider = (seekbar) row.view.findviewbyid(r.id.volume_row_slider);
    row.slider.setonseekbarchangelistener(new volumeseekbarchangelistener(row));

    // forward events above the slider into the slider
    row.view.setontouchlistener(new ontouchlistener() {
        private final rect msliderhitrect = new rect();
        private boolean mdragging;

        @suppresslint("clickableviewaccessibility")
        @override
        public boolean ontouch(view v, motionevent event) {
            row.slider.gethitrect(msliderhitrect);
            if (!mdragging && event.getactionmasked() == motionevent.action_down
                    && event.gety() < msliderhitrect.top) {
                mdragging = true;
            }
            if (mdragging) {
                event.offsetlocation(-msliderhitrect.left, -msliderhitrect.top);
                row.slider.dispatchtouchevent(event);
                if (event.getactionmasked() == motionevent.action_up
                        || event.getactionmasked() == motionevent.action_cancel) {
                    mdragging = false;
                }
                return true;
            }
            return false;
        }
    });
    row.icon = (imagebutton) row.view.findviewbyid(r.id.volume_row_icon);
    row.icon.setimageresource(iconres);
    row.icon.setonclicklistener(new onclicklistener() {
        @override
        public void onclick(view v) {
            events.writeevent(mcontext, events.event_icon_click, row.stream, row.iconstate);
            mcontroller.setactivestream(row.stream);
            if (row.stream == audiomanager.stream_ring) {
                final boolean hasvibrator = mcontroller.hasvibrator();
                if (mstate.ringermodeinternal == audiomanager.ringer_mode_normal) {
                    if (hasvibrator) {
                        mcontroller.setringermode(audiomanager.ringer_mode_vibrate, false);
                    } else {
                        final boolean waszero = row.ss.level == 0;
                        mcontroller.setstreamvolume(stream, waszero ? row.lastaudiblelevel : 0);
                    }
                } else {
                    mcontroller.setringermode(audiomanager.ringer_mode_normal, false);
                    if (row.ss.level == 0) {
                        mcontroller.setstreamvolume(stream, 1);
                    }
                }
            } else {
                final boolean vmute = row.ss.level == 0;
                mcontroller.setstreamvolume(stream, vmute ? row.lastaudiblelevel : 0);
            }
            row.userattempt = 0;  // reset the grace period, slider should update immediately
        }
    });
    row.settingsbutton = (imagebutton) row.view.findviewbyid(r.id.volume_settings_button);
    row.settingsbutton.setonclicklistener(mclicksettings);
    return row;
}

从上面可看出,将一些变量都保存到了volumerow中,设置了icon的点击事件,将当前对应的音量类型设置为最低(禁音), 设置seekbar的改变事件。通过过滤日志,查找到控制音量类型的显示和隐藏的代码块updaterowsh()

private boolean isvisibleh(volumerow row, boolean isactive) {
    return mexpanded && row.view.getvisibility() == view.visible
            || (mexpanded && (row.important || isactive))
            || !mexpanded && isactive;
}

private void updaterowsh() {
    if (d.bug) log.d(tag, "updaterowsh");
    final volumerow activerow = getactiverow();
    updatefooterh();
    updateexpandbuttonh();
    if (!mshowing) {
        trimobsoleteh();
    }
    // apply changes to all rows
    for (volumerow row : mrows) {
        final boolean isactive = row == activerow;
        final boolean visible = isvisibleh(row, isactive);
        log.e(tag, "row==" + row.stream + " isactive=="+isactive + " visible="+visible);
        util.setvisorgone(row.view, visible);
        util.setvisorgone(row.space, visible && mexpanded);
        final int expandbuttonres = mexpanded ? r.drawable.ic_volume_settings : 0;
        if (expandbuttonres != row.cachedexpandbuttonres) {
            row.cachedexpandbuttonres = expandbuttonres;
            if (expandbuttonres == 0) {
                row.settingsbutton.setimagedrawable(null);
            } else {
                row.settingsbutton.setimageresource(expandbuttonres);
            }
        }
        util.setvisorinvis(row.settingsbutton, false);
        updatevolumerowheadervisibleh(row);
        row.header.setalpha(mexpanded && isactive ? 1 : 0.5f);
        updatevolumerowslidertinth(row, isactive);
    }
}

遍历已经添加的音量类型集合mrows,依次判断是否处于活动状态,再和开始设置的important属性比较。mexpanded是否展开,默认只显示铃声音量控制,点击下拉的按钮,才完全显示其它的音量控制

mexpanded && row.view.getvisibility() == view.visible || (mexpanded && (row.important || isactive)) || !mexpanded && isactive

true && false || (true && (true || false)) || false && true --->true

好了,至此分析完毕,重新mmm push systemui.apk 查看效果

第二个功能

源码位置

settings\res_ext\xml\edit_profile_prefs.xml
settings\src\com\mediatek\audioprofile\editprofile.java
settings\src\com\mediatek\audioprofile\volumeseekbarpreference.java

在edit_profile_prefs.xml中仿照原来的alarm volume和ring volume,新增加一个call volume

<!-- media volume -->
<com.mediatek.audioprofile.volumeseekbarpreference
        android:key="media_volume"
        android:icon="@*android:drawable/ic_audio_vol"
        android:title="@string/media_volume_option_title" />

<!-- alarm volume -->
<com.mediatek.audioprofile.volumeseekbarpreference
        android:key="alarm_volume"
        android:icon="@*android:drawable/ic_audio_alarm"
        android:title="@string/alarm_volume_option_title" />

<!-- ring volume -->
<com.mediatek.audioprofile.volumeseekbarpreference
        android:key="ring_volume"
        android:icon="@*android:drawable/ic_audio_ring_notif"
        android:title="@string/ring_volume_option_title" />

<!-- call volume -->
<com.mediatek.audioprofile.volumeseekbarpreference
        android:key="call_volume"
        android:icon="@drawable/ic_volume_voice"
        android:title="@string/call_volume_option_title" />

对应的drawable文件时从systemui中拷贝过来的,ic_volume_voice.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24.0dp"
android:viewportheight="48.0"
android:viewportwidth="48.0"
android:width="24.0dp" >

<path
    android:fillcolor="#ff727272"
    android:pathdata="m13.25,21.59c2.88,5.66 7.51,10.29 13.18,13.17l4.4,-4.41c0.55,-0.55 1.34,-0.71 2.03,-0.49c35.1,30.6 37.51,31.0 40.0,31.0c1.11,0.0 2.0,0.89 2.0,2.0l0.0,7.0c0.0,1.11 -0.89,2.0 -2.0,2.0c21.22,42.0 6.0,26.78 6.0,8.0c0.0,-1.1 0.9,-2.0 2.0,-2.0l7.0,0.0c1.11,0.0 2.0,0.89 2.0,2.0 0.0,2.4 0.4,4.9 1.14,7.1 0.2,0.6 0.06,1.48 -0.49,2.03l-4.4,4.42z" />

</vector>

接下来对应到 editprofile.java 文件中,可以看到 key_alarm_volume 对应的preference初始化,依旧照葫芦画瓢,添加 key_call_volume

private void initvolume(preferencescreen parent) {
    initvolumepreference(key_media_volume, audiomanager.stream_music);
    initvolumepreference(key_alarm_volume, audiomanager.stream_alarm);
    initvolumepreference(key_call_volume, audiomanager.stream_voice_call);
    if (mvoicecapable) {
        mvolume = initvolumepreference(key_ring_volume, audiomanager.stream_ring);
        parent.removepreference(parent.findpreference(key_notification_volume));
    } else {
        mvolume = initvolumepreference(key_notification_volume,
                audiomanager.stream_notification);
        parent.removepreference(parent.findpreference(key_ring_volume));
    }
}

重新编译,push替换后发现,ui倒是出来了,但是无法滑动,事情果然没那么简单,继续查看 initvolumepreference()

private volumeseekbarpreference initvolumepreference(string key, int stream) {
    log.d("@m_" + tag, "init volume preference, key = " + key + ",stream = " + stream);
    final volumeseekbarpreference volumepref = (volumeseekbarpreference) findpreference(key);
    volumepref.setstream(stream);
    volumepref.setcallback(mvolumecallback);
    volumepref.setprofile(mkey);

    return volumepref;
}

保存了当前的音量调节类型,设置seekbar回调事件,接下来看看回调处理了什么

private final class volumepreferencecallback implements volumeseekbarpreference.callback {
    private seekbarvolumizer mcurrent;

    @override
    public void onsamplestarting(seekbarvolumizer sbv) {
        if (mcurrent != null && mcurrent != sbv) {
            mcurrent.stopsample();
        }
        mcurrent = sbv;
        if (mcurrent != null) {
            mhandler.removemessages(h.stop_sample);
            mhandler.sendemptymessagedelayed(h.stop_sample, sample_cutoff);
        }
    }

    public void onstreamvaluechanged(int stream, int progress) {
        if (stream == audiomanager.stream_ring) {
            mhandler.removemessages(h.update_ringer_icon);
            mhandler.obtainmessage(h.update_ringer_icon, progress, 0).sendtotarget();
        }
    }

    public void stopsample() {
        if (mcurrent != null) {
            mcurrent.stopsample();
        }
    }

    public void ringtonechanged() {
        if (mcurrent != null) {
            mcurrent.ringtonechanged();
        } else {
            mvolume.getseekbar().ringtonechanged();
        }
    }
};

当我们点击或者是滑动seekbar时,会根据当前设置的音量大小播放一段短暂的默认铃音,当铃音未播放完成时,再次点击将不进行播放。继续跟进 volumeseekbarpreference.java 中

@override
protected void onbindview(view view) {
    super.onbindview(view);
    if (mstream == 0) {
        log.w(tag, "no stream found, not binding volumizer  ");
        return;
    }
    getpreferencemanager().registeronactivitystoplistener(this);
    final seekbar seekbar = (seekbar) view.findviewbyid(com.android.internal.r.id.seekbar);
    if (seekbar == mseekbar) {
        return;
    }
    mseekbar = seekbar;
    final seekbarvolumizer.callback sbvc = new seekbarvolumizer.callback() {
        @override
        public void onsamplestarting(seekbarvolumizer sbv) {
            if (mcallback != null) {
                mcallback.onsamplestarting(sbv);
            }
        }
    };
    final uri sampleuri = mstream == audiomanager.stream_music ? getmediavolumeuri() : null;
    if (mvolumizer == null) {
        mvolumizer = new seekbarvolumizer(getcontext(), mstream, sampleuri, sbvc, mkey);
    }
    //mvolumizer.setprofile(mkey);
    mvolumizer.setseekbar(mseekbar);
}

mstream == 0, 直接return,会不会和这有关系呢,来看下各个音量调节类型对应的int值

/** used to identify the volume of audio streams for phone calls */
public static final int stream_voice_call = 0;
/** used to identify the volume of audio streams for the phone ring and message alerts */
public static final int stream_ring = 2;
/** used to identify the volume of audio streams for music playback */
public static final int stream_music = 3;
/** used to identify the volume of audio streams for alarms */
public static final int stream_alarm = 4;

我们新增的 stream_voice_call对应的mstream正好为0,直接给return掉了,所以修改为 mstream < 0 即可,重新编译 push 发现成功了。

具体的音量调节逻辑在 packages\apps\settings\src\com\mediatek\audioprofile\seekbarvolumizer.java 中,感兴趣的可继续深究,肯定离不开调用 .setstreamvolume()方法,大概看了一眼,主要是onprogresschanged()回调,通过postsetvolume(progress)方法,发送msg_set_stream_volume消息,最终调用savevolume()

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网