telecomadapter.java_Android-mvp模式

Android应用开发中,mvp模式是目前比较流行的设计模式。

三层依赖关系如下图:

91089173a0b0656ce76a1572c4fc918f.png

本文记录一下通话界面的对于MVP设计模式的使用。

1.V层即view层的接口定义

(1)接口Ui定义

package com.android.incallui.baseui;

/** Base class for all presenter ui. */

public interface Ui {}

(2)BaseFragment定义,这也是在V层,是所有使用Presenter 和Ui的父亲fragment

package com.android.incallui.baseui;

import android.os.Bundle;

import android.support.v4.app.Fragment;

/** Parent for all fragments that use Presenters and Ui design. */

public abstract class BaseFragment, U extends Ui> extends Fragment {

private static final String KEY_FRAGMENT_HIDDEN = "key_fragment_hidden";

private T presenter;

protected BaseFragment() {

presenter = createPresenter();

}

public abstract T createPresenter();

public abstract U getUi();

/**

* Presenter will be available after onActivityCreated().

*

* @return The presenter associated with this fragment.

*/

public T getPresenter() {

return presenter;

}

@Override

public void onActivityCreated(Bundle savedInstanceState) {

super.onActivityCreated(savedInstanceState);

presenter.onUiReady(getUi());

}

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

if (savedInstanceState != null) {

presenter.onRestoreInstanceState(savedInstanceState);

if (savedInstanceState.getBoolean(KEY_FRAGMENT_HIDDEN)) {

getFragmentManager().beginTransaction().hide(this).commit();

}

}

}

@Override

public void onDestroyView() {

super.onDestroyView();

presenter.onUiDestroy(getUi());

}

@Override

public void onSaveInstanceState(Bundle outState) {

super.onSaveInstanceState(outState);

presenter.onSaveInstanceState(outState);

outState.putBoolean(KEY_FRAGMENT_HIDDEN, isHidden());

}

}

(3)V层接口实现类定义

DialpadFragment 类定义

package com.android.incallui;

import android.content.Context;

import android.os.Bundle;

import android.telephony.PhoneNumberUtils;

import android.util.ArrayMap;

import android.util.AttributeSet;

import android.view.KeyEvent;

import android.view.LayoutInflater;

import android.view.View;

import android.view.View.OnClickListener;

import android.view.View.OnKeyListener;

import android.view.ViewGroup;

import android.widget.EditText;

import android.widget.LinearLayout;

import android.widget.TextView;

import com.android.dialer.common.LogUtil;

import com.android.dialer.dialpadview.DialpadKeyButton;

import com.android.dialer.dialpadview.DialpadKeyButton.OnPressedListener;

import com.android.dialer.dialpadview.DialpadView;

import com.android.dialer.logging.DialerImpression;

import com.android.dialer.logging.Logger;

import com.android.incallui.DialpadPresenter.DialpadUi;

import com.android.incallui.baseui.BaseFragment;

import java.util.Map;

/** Fragment for call control buttons */

public class DialpadFragment extends BaseFragment

implements DialpadUi, OnKeyListener, OnClickListener, OnPressedListener {

/** Hash Map to map a view id to a character */

private static final Map displayMap = new ArrayMap<>();

/** Set up the static maps */

static {

// Map the buttons to the display characters

displayMap.put(R.id.one, '1');

displayMap.put(R.id.two, '2');

displayMap.put(R.id.three, '3');

displayMap.put(R.id.four, '4');

displayMap.put(R.id.five, '5');

displayMap.put(R.id.six, '6');

displayMap.put(R.id.seven, '7');

displayMap.put(R.id.eight, '8');

displayMap.put(R.id.nine, '9');

displayMap.put(R.id.zero, '0');

displayMap.put(R.id.pound, '#');

displayMap.put(R.id.star, '*');

}

private final int[] buttonIds =

new int[] {

R.id.zero,

R.id.one,

R.id.two,

R.id.three,

R.id.four,

R.id.five,

R.id.six,

R.id.seven,

R.id.eight,

R.id.nine,

R.id.star,

R.id.pound

};

private EditText dtmfDialerField;

// KeyListener used with the "dialpad digits" EditText widget.

private DtmfKeyListener dtmfKeyListener;

private DialpadView dialpadView;

private int currentTextColor;

@Override

public void onClick(View v) {

if (v.getId() == R.id.dialpad_back) {

Logger.get(getContext())

.logImpression(DialerImpression.Type.IN_CALL_DIALPAD_CLOSE_BUTTON_PRESSED);

getActivity().onBackPressed();

}

}

@Override

public boolean onKey(View v, int keyCode, KeyEvent event) {

Log.d(this, "onKey: keyCode " + keyCode + ", view " + v);

if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {

int viewId = v.getId();

if (displayMap.containsKey(viewId)) {

switch (event.getAction()) {

case KeyEvent.ACTION_DOWN:

if (event.getRepeatCount() == 0) {

getPresenter().processDtmf(displayMap.get(viewId));

}

break;

case KeyEvent.ACTION_UP:

getPresenter().stopDtmf();

break;

default: // fall out

}

// do not return true [handled] here, since we want the

// press / click animation to be handled by the framework.

}

}

return false;

}

@Override

public DialpadPresenter createPresenter() {

return new DialpadPresenter();

}

@Override

public DialpadPresenter.DialpadUi getUi() {

return this;

}

// TODO(klp) Adds hardware keyboard listener

@Override

public View onCreateView(

LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

final View parent = inflater.inflate(R.layout.incall_dialpad_fragment, container, false);

dialpadView = (DialpadView) parent.findViewById(R.id.dialpad_view);

dialpadView.setCanDigitsBeEdited(false);

dialpadView.setBackgroundResource(R.color.incall_dialpad_background);

dtmfDialerField = (EditText) parent.findViewById(R.id.digits);

if (dtmfDialerField != null) {

LogUtil.i("DialpadFragment.onCreateView", "creating dtmfKeyListener");

dtmfKeyListener = new DtmfKeyListener(getPresenter());

dtmfDialerField.setKeyListener(dtmfKeyListener);

// remove the long-press context menus that support

// the edit (copy / paste / select) functions.

dtmfDialerField.setLongClickable(false);

dtmfDialerField.setElegantTextHeight(false);

configureKeypadListeners();

}

View backButton = dialpadView.findViewById(R.id.dialpad_back);

backButton.setVisibility(View.VISIBLE);

backButton.setOnClickListener(this);

return parent;

}

@Override

public void onResume() {

super.onResume();

updateColors();

}

public void updateColors() {

int textColor = InCallPresenter.getInstance().getThemeColorManager().getPrimaryColor();

if (currentTextColor == textColor) {

return;

}

DialpadKeyButton dialpadKey;

for (int i = 0; i < buttonIds.length; i++) {

dialpadKey = (DialpadKeyButton) dialpadView.findViewById(buttonIds[i]);

((TextView) dialpadKey.findViewById(R.id.dialpad_key_number)).setTextColor(textColor);

}

currentTextColor = textColor;

}

@Override

public void onDestroyView() {

dtmfKeyListener = null;

super.onDestroyView();

}

/**

* Getter for Dialpad text.

*

* @return String containing current Dialpad EditText text.

*/

public String getDtmfText() {

return dtmfDialerField.getText().toString();

}

/**

* Sets the Dialpad text field with some text.

*

* @param text Text to set Dialpad EditText to.

*/

public void setDtmfText(String text) {

dtmfDialerField.setText(PhoneNumberUtils.createTtsSpannable(text));

/// M: ALPS03175530 set the focus to the end of the text

dtmfDialerField.setSelection(dtmfDialerField.length());

}

/** Starts the slide up animation for the Dialpad keys when the Dialpad is revealed. */

public void animateShowDialpad() {

final DialpadView dialpadView = (DialpadView) getView().findViewById(R.id.dialpad_view);

dialpadView.animateShow();

}

@Override

public void appendDigitsToField(char digit) {

if (dtmfDialerField != null) {

// TODO: maybe *don't* manually append this digit if

// mDialpadDigits is focused and this key came from the HW

// keyboard, since in that case the EditText field will

// get the key event directly and automatically appends

// whetever the user types.

// (Or, a cleaner fix would be to just make mDialpadDigits

// *not* handle HW key presses. That seems to be more

// complicated than just setting focusable="false" on it,

// though.)

dtmfDialerField.getText().append(digit);

}

}

/** Called externally (from InCallScreen) to play a DTMF Tone. */

/* package */ boolean onDialerKeyDown(KeyEvent event) {

Log.d(this, "Notifying dtmf key down.");

if (dtmfKeyListener != null) {

return dtmfKeyListener.onKeyDown(event);

} else {

return false;

}

}

/** Called externally (from InCallScreen) to cancel the last DTMF Tone played. */

public boolean onDialerKeyUp(KeyEvent event) {

Log.d(this, "Notifying dtmf key up.");

if (dtmfKeyListener != null) {

return dtmfKeyListener.onKeyUp(event);

} else {

return false;

}

}

private void configureKeypadListeners() {

DialpadKeyButton dialpadKey;

for (int i = 0; i < buttonIds.length; i++) {

dialpadKey = (DialpadKeyButton) dialpadView.findViewById(buttonIds[i]);

dialpadKey.setOnKeyListener(this);

dialpadKey.setOnClickListener(this);

dialpadKey.setOnPressedListener(this);

}

}

@Override

public void onPressed(View view, boolean pressed) {

if (pressed && displayMap.containsKey(view.getId())) {

Logger.get(getContext())

.logImpression(DialerImpression.Type.IN_CALL_DIALPAD_NUMBER_BUTTON_PRESSED);

Log.d(this, "onPressed: " + pressed + " " + displayMap.get(view.getId()));

getPresenter().processDtmf(displayMap.get(view.getId()));

}

if (!pressed) {

Log.d(this, "onPressed: " + pressed);

getPresenter().stopDtmf();

}

}

/**

* LinearLayout with getter and setter methods for the translationY property using floats, for

* animation purposes.

*/

public static class DialpadSlidingLinearLayout extends LinearLayout {

public DialpadSlidingLinearLayout(Context context) {

super(context);

}

public DialpadSlidingLinearLayout(Context context, AttributeSet attrs) {

super(context, attrs);

}

public DialpadSlidingLinearLayout(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

}

public float getYFraction() {

final int height = getHeight();

if (height == 0) {

return 0;

}

return getTranslationY() / height;

}

public void setYFraction(float yFraction) {

setTranslationY(yFraction * getHeight());

}

}

/// M: ------------------------------- MediaTek feature ---------------------------

/**

* M: Used for making the fragment's state is latest.

* @param hidden Dialpad fragment is hidden or show

*/

@Override

public void onHiddenChanged(boolean hidden) {

super.onHiddenChanged(hidden);

if (!hidden) {

updateColors();

}

}

}

2.P层

(1)Presenter抽象类定义

package com.android.incallui.baseui;

import android.os.Bundle;

/** Base class for Presenters. */

public abstract class Presenter {

private U ui;

/**

* Called after the UI view has been created. That is when fragment.onViewCreated() is called.

*

* @param ui The Ui implementation that is now ready to be used.

*/

public void onUiReady(U ui) {

this.ui = ui;

}

/** Called when the UI view is destroyed in Fragment.onDestroyView(). */

public final void onUiDestroy(U ui) {

onUiUnready(ui);

this.ui = null;

}

/**

* To be overriden by Presenter implementations. Called when the fragment is being destroyed but

* before ui is set to null.

*/

public void onUiUnready(U ui) {}

public void onSaveInstanceState(Bundle outState) {}

public void onRestoreInstanceState(Bundle savedInstanceState) {}

public U getUi() {

return ui;

}

}

(2)Presenter类的接口实现类DialpadPresenter定义

package com.android.incallui;

import android.telephony.PhoneNumberUtils;

import com.android.incallui.DialpadPresenter.DialpadUi;

import com.android.incallui.baseui.Presenter;

import com.android.incallui.baseui.Ui;

import com.android.incallui.call.CallList;

import com.android.incallui.call.DialerCall;

import com.android.incallui.call.TelecomAdapter;

/** Logic for call buttons. */

public class DialpadPresenter extends Presenter

implements InCallPresenter.InCallStateListener {

private DialerCall call;

@Override

public void onUiReady(DialpadUi ui) {

super.onUiReady(ui);

InCallPresenter.getInstance().addListener(this);

call = CallList.getInstance().getOutgoingOrActive();

}

@Override

public void onUiUnready(DialpadUi ui) {

super.onUiUnready(ui);

InCallPresenter.getInstance().removeListener(this);

}

@Override

public void onStateChange(

InCallPresenter.InCallState oldState,

InCallPresenter.InCallState newState,

CallList callList) {

call = callList.getOutgoingOrActive();

Log.d(this, "DialpadPresenter mCall = " + call);

}

/**

* Processes the specified digit as a DTMF key, by playing the appropriate DTMF tone, and

* appending the digit to the EditText field that displays the DTMF digits sent so far.

*/

public final void processDtmf(char c) {

Log.d(this, "Processing dtmf key " + c);

// if it is a valid key, then update the display and send the dtmf tone.

if (PhoneNumberUtils.is12Key(c) && call != null) {

Log.d(this, "updating display and sending dtmf tone for '" + c + "'");

// Append this key to the "digits" widget.

DialpadUi dialpadUi = getUi();

if (dialpadUi != null) {

dialpadUi.appendDigitsToField(c);

}

// Plays the tone through Telecom.

TelecomAdapter.getInstance().playDtmfTone(call.getId(), c);

} else {

Log.d(this, "ignoring dtmf request for '" + c + "'");

}

}

/** Stops the local tone based on the phone type. */

public void stopDtmf() {

if (call != null) {

Log.d(this, "stopping remote tone");

TelecomAdapter.getInstance().stopDtmfTone(call.getId());

}

}

public interface DialpadUi extends Ui {

void appendDigitsToField(char digit);

}

}

3.M层

TelecomAdapter类就是model层的实现

package com.android.incallui.call;

import android.app.Notification;

import android.content.ActivityNotFoundException;

import android.content.Intent;

import android.os.Looper;

import android.support.annotation.MainThread;

import android.support.annotation.VisibleForTesting;

import android.telecom.InCallService;

import com.android.dialer.common.Assert;

import com.android.dialer.common.LogUtil;

import java.util.List;

/** Wrapper around Telecom APIs. */

public class TelecomAdapter implements InCallServiceListener {

private static final String ADD_CALL_MODE_KEY = "add_call_mode";

private static TelecomAdapter instance;

private InCallService inCallService;

private TelecomAdapter() {}

@MainThread

public static TelecomAdapter getInstance() {

if (!Looper.getMainLooper().isCurrentThread()) {

throw new IllegalStateException();

}

if (instance == null) {

instance = new TelecomAdapter();

}

return instance;

}

@VisibleForTesting(otherwise = VisibleForTesting.NONE)

public static void setInstanceForTesting(TelecomAdapter telecomAdapter) {

instance = telecomAdapter;

}

@Override

public void setInCallService(InCallService inCallService) {

this.inCallService = inCallService;

}

@Override

public void clearInCallService() {

inCallService = null;

}

private android.telecom.Call getTelecomCallById(String callId) {

DialerCall call = CallList.getInstance().getCallById(callId);

return call == null ? null : call.getTelecomCall();

}

public void mute(boolean shouldMute) {

if (inCallService != null) {

inCallService.setMuted(shouldMute);

} else {

LogUtil.e("TelecomAdapter.mute", "mInCallService is null");

}

}

public void setAudioRoute(int route) {

if (inCallService != null) {

inCallService.setAudioRoute(route);

} else {

LogUtil.e("TelecomAdapter.setAudioRoute", "mInCallService is null");

}

}

public void merge(String callId) {

android.telecom.Call call = getTelecomCallById(callId);

if (call != null) {

List conferenceable = call.getConferenceableCalls();

if (!conferenceable.isEmpty()) {

call.conference(conferenceable.get(0));

// It's safe to clear restrict count for merge action.

DialerCall.clearRestrictedCount();

} else {

if (call.getDetails().can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE)) {

call.mergeConference();

// It's safe to clear restrict count for merge action.

DialerCall.clearRestrictedCount();

}

}

} else {

LogUtil.e("TelecomAdapter.merge", "call not in call list " + callId);

}

}

public void swap(String callId) {

android.telecom.Call call = getTelecomCallById(callId);

if (call != null) {

if (call.getDetails().can(android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE)) {

call.swapConference();

}

} else {

LogUtil.e("TelecomAdapter.swap", "call not in call list " + callId);

}

}

public void addCall() {

if (inCallService != null) {

Intent intent = new Intent(Intent.ACTION_DIAL);

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

// when we request the dialer come up, we also want to inform

// it that we're going through the "add call" option from the

// InCallScreen / PhoneUtils.

intent.putExtra(ADD_CALL_MODE_KEY, true);

try {

LogUtil.d("TelecomAdapter.addCall", "Sending the add DialerCall intent");

inCallService.startActivity(intent);

} catch (ActivityNotFoundException e) {

// This is rather rare but possible.

// Note: this method is used even when the phone is encrypted. At that moment

// the system may not find any Activity which can accept this Intent.

LogUtil.e("TelecomAdapter.addCall", "Activity for adding calls isn't found.", e);

}

}

}

public void playDtmfTone(String callId, char digit) {

android.telecom.Call call = getTelecomCallById(callId);

if (call != null) {

call.playDtmfTone(digit);

} else {

LogUtil.e("TelecomAdapter.playDtmfTone", "call not in call list " + callId);

}

}

public void stopDtmfTone(String callId) {

android.telecom.Call call = getTelecomCallById(callId);

if (call != null) {

call.stopDtmfTone();

} else {

LogUtil.e("TelecomAdapter.stopDtmfTone", "call not in call list " + callId);

}

}

public void postDialContinue(String callId, boolean proceed) {

android.telecom.Call call = getTelecomCallById(callId);

if (call != null) {

call.postDialContinue(proceed);

} else {

LogUtil.e("TelecomAdapter.postDialContinue", "call not in call list " + callId);

}

}

public boolean canAddCall() {

if (inCallService != null) {

return inCallService.canAddCall();

}

return false;

}

/**

* Start a foreground notification. Calling it multiple times with the same id only updates the

* existing notification. Whoever called this function are responsible for calling {@link

* #stopForegroundNotification()} to remove the notification.

*/

public void startForegroundNotification(int id, Notification notification) {

Assert.isNotNull(

inCallService, "No inCallService available for starting foreground notification");

inCallService.startForeground(id, notification);

}

/**

* Stop a started foreground notification. This does not stop {@code mInCallService} from running.

*/

public void stopForegroundNotification() {

if (inCallService != null) {

inCallService.stopForeground(true /*removeNotification*/);

} else {

LogUtil.e(

"TelecomAdapter.stopForegroundNotification",

"no inCallService available for stopping foreground notification");

}

}

}

model层就是完全的数据逻辑层。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值