• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.incallui;
18 
19 import android.content.Context;
20 import android.os.Bundle;
21 import android.os.Trace;
22 import android.support.v4.app.Fragment;
23 import android.support.v4.os.UserManagerCompat;
24 import android.telecom.CallAudioState;
25 import android.telecom.PhoneAccountHandle;
26 import com.android.contacts.common.compat.CallCompat;
27 import com.android.dialer.common.Assert;
28 import com.android.dialer.common.LogUtil;
29 import com.android.dialer.common.concurrent.DialerExecutorComponent;
30 import com.android.dialer.logging.DialerImpression;
31 import com.android.dialer.logging.DialerImpression.Type;
32 import com.android.dialer.logging.Logger;
33 import com.android.dialer.telecom.TelecomUtil;
34 import com.android.incallui.InCallCameraManager.Listener;
35 import com.android.incallui.InCallPresenter.CanAddCallListener;
36 import com.android.incallui.InCallPresenter.InCallDetailsListener;
37 import com.android.incallui.InCallPresenter.InCallState;
38 import com.android.incallui.InCallPresenter.InCallStateListener;
39 import com.android.incallui.InCallPresenter.IncomingCallListener;
40 import com.android.incallui.audiomode.AudioModeProvider;
41 import com.android.incallui.audiomode.AudioModeProvider.AudioModeListener;
42 import com.android.incallui.call.CallList;
43 import com.android.incallui.call.DialerCall;
44 import com.android.incallui.call.DialerCall.CameraDirection;
45 import com.android.incallui.call.TelecomAdapter;
46 import com.android.incallui.incall.protocol.InCallButtonIds;
47 import com.android.incallui.incall.protocol.InCallButtonUi;
48 import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
49 import com.android.incallui.multisim.SwapSimWorker;
50 import com.android.incallui.videotech.utils.VideoUtils;
51 
52 /** Logic for call buttons. */
53 public class CallButtonPresenter
54     implements InCallStateListener,
55         AudioModeListener,
56         IncomingCallListener,
57         InCallDetailsListener,
58         CanAddCallListener,
59         Listener,
60         InCallButtonUiDelegate {
61 
62   private static final String KEY_AUTOMATICALLY_MUTED = "incall_key_automatically_muted";
63   private static final String KEY_PREVIOUS_MUTE_STATE = "incall_key_previous_mute_state";
64 
65   private final Context context;
66   private InCallButtonUi inCallButtonUi;
67   private DialerCall call;
68   private boolean automaticallyMuted = false;
69   private boolean previousMuteState = false;
70   private boolean isInCallButtonUiReady;
71   private PhoneAccountHandle otherAccount;
72 
CallButtonPresenter(Context context)73   public CallButtonPresenter(Context context) {
74     this.context = context.getApplicationContext();
75   }
76 
77   @Override
onInCallButtonUiReady(InCallButtonUi ui)78   public void onInCallButtonUiReady(InCallButtonUi ui) {
79     Assert.checkState(!isInCallButtonUiReady);
80     inCallButtonUi = ui;
81     AudioModeProvider.getInstance().addListener(this);
82 
83     // register for call state changes last
84     final InCallPresenter inCallPresenter = InCallPresenter.getInstance();
85     inCallPresenter.addListener(this);
86     inCallPresenter.addIncomingCallListener(this);
87     inCallPresenter.addDetailsListener(this);
88     inCallPresenter.addCanAddCallListener(this);
89     inCallPresenter.getInCallCameraManager().addCameraSelectionListener(this);
90 
91     // Update the buttons state immediately for the current call
92     onStateChange(InCallState.NO_CALLS, inCallPresenter.getInCallState(), CallList.getInstance());
93     isInCallButtonUiReady = true;
94   }
95 
96   @Override
onInCallButtonUiUnready()97   public void onInCallButtonUiUnready() {
98     Assert.checkState(isInCallButtonUiReady);
99     inCallButtonUi = null;
100     InCallPresenter.getInstance().removeListener(this);
101     AudioModeProvider.getInstance().removeListener(this);
102     InCallPresenter.getInstance().removeIncomingCallListener(this);
103     InCallPresenter.getInstance().removeDetailsListener(this);
104     InCallPresenter.getInstance().getInCallCameraManager().removeCameraSelectionListener(this);
105     InCallPresenter.getInstance().removeCanAddCallListener(this);
106     isInCallButtonUiReady = false;
107   }
108 
109   @Override
onStateChange(InCallState oldState, InCallState newState, CallList callList)110   public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
111     Trace.beginSection("CallButtonPresenter.onStateChange");
112     if (newState == InCallState.OUTGOING) {
113       call = callList.getOutgoingCall();
114     } else if (newState == InCallState.INCALL) {
115       call = callList.getActiveOrBackgroundCall();
116 
117       // When connected to voice mail, automatically shows the dialpad.
118       // (On previous releases we showed it when in-call shows up, before waiting for
119       // OUTGOING.  We may want to do that once we start showing "Voice mail" label on
120       // the dialpad too.)
121       if (oldState == InCallState.OUTGOING && call != null) {
122         if (call.isVoiceMailNumber() && getActivity() != null) {
123           getActivity().showDialpadFragment(true /* show */, true /* animate */);
124         }
125       }
126     } else if (newState == InCallState.INCOMING) {
127       if (getActivity() != null) {
128         getActivity().showDialpadFragment(false /* show */, true /* animate */);
129       }
130       call = callList.getIncomingCall();
131     } else {
132       call = null;
133     }
134     updateUi(newState, call);
135     Trace.endSection();
136   }
137 
138   /**
139    * Updates the user interface in response to a change in the details of a call. Currently handles
140    * changes to the call buttons in response to a change in the details for a call. This is
141    * important to ensure changes to the active call are reflected in the available buttons.
142    *
143    * @param call The active call.
144    * @param details The call details.
145    */
146   @Override
onDetailsChanged(DialerCall call, android.telecom.Call.Details details)147   public void onDetailsChanged(DialerCall call, android.telecom.Call.Details details) {
148     // Only update if the changes are for the currently active call
149     if (inCallButtonUi != null && call != null && call.equals(this.call)) {
150       updateButtonsState(call);
151     }
152   }
153 
154   @Override
onIncomingCall(InCallState oldState, InCallState newState, DialerCall call)155   public void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call) {
156     onStateChange(oldState, newState, CallList.getInstance());
157   }
158 
159   @Override
onCanAddCallChanged(boolean canAddCall)160   public void onCanAddCallChanged(boolean canAddCall) {
161     if (inCallButtonUi != null && call != null) {
162       updateButtonsState(call);
163     }
164   }
165 
166   @Override
onAudioStateChanged(CallAudioState audioState)167   public void onAudioStateChanged(CallAudioState audioState) {
168     if (inCallButtonUi != null) {
169       inCallButtonUi.setAudioState(audioState);
170     }
171   }
172 
173   @Override
getCurrentAudioState()174   public CallAudioState getCurrentAudioState() {
175     return AudioModeProvider.getInstance().getAudioState();
176   }
177 
178   @Override
setAudioRoute(int route)179   public void setAudioRoute(int route) {
180     LogUtil.i(
181         "CallButtonPresenter.setAudioRoute",
182         "sending new audio route: " + CallAudioState.audioRouteToString(route));
183     TelecomAdapter.getInstance().setAudioRoute(route);
184   }
185 
186   /** Function assumes that bluetooth is not supported. */
187   @Override
toggleSpeakerphone()188   public void toggleSpeakerphone() {
189     // This function should not be called if bluetooth is available.
190     CallAudioState audioState = getCurrentAudioState();
191     if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) {
192       // It's clear the UI is wrong, so update the supported mode once again.
193       LogUtil.e(
194           "CallButtonPresenter", "toggling speakerphone not allowed when bluetooth supported.");
195       inCallButtonUi.setAudioState(audioState);
196       return;
197     }
198 
199     int newRoute;
200     if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
201       newRoute = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
202       Logger.get(context)
203           .logCallImpression(
204               DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_WIRED_OR_EARPIECE,
205               call.getUniqueCallId(),
206               call.getTimeAddedMs());
207     } else {
208       newRoute = CallAudioState.ROUTE_SPEAKER;
209       Logger.get(context)
210           .logCallImpression(
211               DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_SPEAKERPHONE,
212               call.getUniqueCallId(),
213               call.getTimeAddedMs());
214     }
215 
216     setAudioRoute(newRoute);
217   }
218 
219   @Override
muteClicked(boolean checked, boolean clickedByUser)220   public void muteClicked(boolean checked, boolean clickedByUser) {
221     LogUtil.i(
222         "CallButtonPresenter", "turning on mute: %s, clicked by user: %s", checked, clickedByUser);
223     if (clickedByUser) {
224       Logger.get(context)
225           .logCallImpression(
226               checked
227                   ? DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_MUTE
228                   : DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_MUTE,
229               call.getUniqueCallId(),
230               call.getTimeAddedMs());
231     }
232     TelecomAdapter.getInstance().mute(checked);
233   }
234 
235   @Override
holdClicked(boolean checked)236   public void holdClicked(boolean checked) {
237     if (call == null) {
238       return;
239     }
240     if (checked) {
241       LogUtil.i("CallButtonPresenter", "putting the call on hold: " + call);
242       call.hold();
243     } else {
244       LogUtil.i("CallButtonPresenter", "removing the call from hold: " + call);
245       call.unhold();
246     }
247   }
248 
249   @Override
swapClicked()250   public void swapClicked() {
251     if (call == null) {
252       return;
253     }
254 
255     LogUtil.i("CallButtonPresenter", "swapping the call: " + call);
256     TelecomAdapter.getInstance().swap(call.getId());
257   }
258 
259   @Override
mergeClicked()260   public void mergeClicked() {
261     Logger.get(context)
262         .logCallImpression(
263             DialerImpression.Type.IN_CALL_MERGE_BUTTON_PRESSED,
264             call.getUniqueCallId(),
265             call.getTimeAddedMs());
266     TelecomAdapter.getInstance().merge(call.getId());
267   }
268 
269   @Override
addCallClicked()270   public void addCallClicked() {
271     Logger.get(context)
272         .logCallImpression(
273             DialerImpression.Type.IN_CALL_ADD_CALL_BUTTON_PRESSED,
274             call.getUniqueCallId(),
275             call.getTimeAddedMs());
276     // Automatically mute the current call
277     automaticallyMuted = true;
278     previousMuteState = AudioModeProvider.getInstance().getAudioState().isMuted();
279     // Simulate a click on the mute button
280     muteClicked(true /* checked */, false /* clickedByUser */);
281     TelecomAdapter.getInstance().addCall();
282   }
283 
284   @Override
showDialpadClicked(boolean checked)285   public void showDialpadClicked(boolean checked) {
286     Logger.get(context)
287         .logCallImpression(
288             DialerImpression.Type.IN_CALL_SHOW_DIALPAD_BUTTON_PRESSED,
289             call.getUniqueCallId(),
290             call.getTimeAddedMs());
291     LogUtil.v("CallButtonPresenter", "show dialpad " + String.valueOf(checked));
292     getActivity().showDialpadFragment(checked /* show */, true /* animate */);
293   }
294 
295   @Override
changeToVideoClicked()296   public void changeToVideoClicked() {
297     LogUtil.enterBlock("CallButtonPresenter.changeToVideoClicked");
298     Logger.get(context)
299         .logCallImpression(
300             DialerImpression.Type.VIDEO_CALL_UPGRADE_REQUESTED,
301             call.getUniqueCallId(),
302             call.getTimeAddedMs());
303     call.getVideoTech().upgradeToVideo(context);
304   }
305 
306   @Override
onEndCallClicked()307   public void onEndCallClicked() {
308     LogUtil.i("CallButtonPresenter.onEndCallClicked", "call: " + call);
309     if (call != null) {
310       call.disconnect();
311     }
312   }
313 
314   @Override
showAudioRouteSelector()315   public void showAudioRouteSelector() {
316     inCallButtonUi.showAudioRouteSelector();
317   }
318 
319   @Override
swapSimClicked()320   public void swapSimClicked() {
321     LogUtil.enterBlock("CallButtonPresenter.swapSimClicked");
322     Logger.get(getContext()).logImpression(Type.DUAL_SIM_CHANGE_SIM_PRESSED);
323     SwapSimWorker worker =
324         new SwapSimWorker(
325             getContext(),
326             call,
327             InCallPresenter.getInstance().getCallList(),
328             otherAccount,
329             InCallPresenter.getInstance().acquireInCallUiLock("swapSim"));
330     DialerExecutorComponent.get(getContext())
331         .dialerExecutorFactory()
332         .createNonUiTaskBuilder(worker)
333         .build()
334         .executeParallel(null);
335   }
336 
337   /**
338    * Switches the camera between the front-facing and back-facing camera.
339    *
340    * @param useFrontFacingCamera True if we should switch to using the front-facing camera, or false
341    *     if we should switch to using the back-facing camera.
342    */
343   @Override
switchCameraClicked(boolean useFrontFacingCamera)344   public void switchCameraClicked(boolean useFrontFacingCamera) {
345     updateCamera(useFrontFacingCamera);
346   }
347 
348   @Override
toggleCameraClicked()349   public void toggleCameraClicked() {
350     LogUtil.i("CallButtonPresenter.toggleCameraClicked", "");
351     if (call == null) {
352       return;
353     }
354     Logger.get(context)
355         .logCallImpression(
356             DialerImpression.Type.IN_CALL_SCREEN_SWAP_CAMERA,
357             call.getUniqueCallId(),
358             call.getTimeAddedMs());
359     switchCameraClicked(
360         !InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
361   }
362 
363   /**
364    * Stop or start client's video transmission.
365    *
366    * @param pause True if pausing the local user's video, or false if starting the local user's
367    *     video.
368    */
369   @Override
pauseVideoClicked(boolean pause)370   public void pauseVideoClicked(boolean pause) {
371     LogUtil.i("CallButtonPresenter.pauseVideoClicked", "%s", pause ? "pause" : "unpause");
372 
373     Logger.get(context)
374         .logCallImpression(
375             pause
376                 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_VIDEO
377                 : DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_VIDEO,
378             call.getUniqueCallId(),
379             call.getTimeAddedMs());
380 
381     if (pause) {
382       call.getVideoTech().setCamera(null);
383       call.getVideoTech().stopTransmission();
384     } else {
385       updateCamera(
386           InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera());
387       call.getVideoTech().resumeTransmission(context);
388     }
389 
390     inCallButtonUi.setVideoPaused(pause);
391     inCallButtonUi.enableButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, false);
392   }
393 
updateCamera(boolean useFrontFacingCamera)394   private void updateCamera(boolean useFrontFacingCamera) {
395     InCallCameraManager cameraManager = InCallPresenter.getInstance().getInCallCameraManager();
396     cameraManager.setUseFrontFacingCamera(useFrontFacingCamera);
397 
398     String cameraId = cameraManager.getActiveCameraId();
399     if (cameraId != null) {
400       final int cameraDir =
401           cameraManager.isUsingFrontFacingCamera()
402               ? CameraDirection.CAMERA_DIRECTION_FRONT_FACING
403               : CameraDirection.CAMERA_DIRECTION_BACK_FACING;
404       call.setCameraDir(cameraDir);
405       call.getVideoTech().setCamera(cameraId);
406     }
407   }
408 
updateUi(InCallState state, DialerCall call)409   private void updateUi(InCallState state, DialerCall call) {
410     LogUtil.v("CallButtonPresenter", "updating call UI for call: %s", call);
411 
412     if (inCallButtonUi == null) {
413       return;
414     }
415 
416     if (call != null) {
417       inCallButtonUi.updateInCallButtonUiColors(
418           InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor());
419     }
420 
421     final boolean isEnabled =
422         state.isConnectingOrConnected() && !state.isIncoming() && call != null;
423     inCallButtonUi.setEnabled(isEnabled);
424 
425     if (call == null) {
426       return;
427     }
428 
429     updateButtonsState(call);
430   }
431 
432   /**
433    * Updates the buttons applicable for the UI.
434    *
435    * @param call The active call.
436    */
437   @SuppressWarnings("MissingPermission")
updateButtonsState(DialerCall call)438   private void updateButtonsState(DialerCall call) {
439     LogUtil.v("CallButtonPresenter.updateButtonsState", "");
440     final boolean isVideo = call.isVideoCall();
441 
442     // Common functionality (audio, hold, etc).
443     // Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available:
444     //     (1) If the device normally can hold, show HOLD in a disabled state.
445     //     (2) If the device doesn't have the concept of hold/swap, remove the button.
446     final boolean showSwap = call.can(android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
447     final boolean showHold =
448         !showSwap
449             && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD)
450             && call.can(android.telecom.Call.Details.CAPABILITY_HOLD);
451     final boolean isCallOnHold = call.getState() == DialerCall.State.ONHOLD;
452 
453     final boolean showAddCall =
454         TelecomAdapter.getInstance().canAddCall() && UserManagerCompat.isUserUnlocked(context);
455     final boolean showMerge = call.can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
456     final boolean showUpgradeToVideo = !isVideo && (hasVideoCallCapabilities(call));
457     final boolean showDowngradeToAudio = isVideo && isDowngradeToAudioSupported(call);
458     final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE);
459 
460     final boolean hasCameraPermission =
461         isVideo && VideoUtils.hasCameraPermissionAndShownPrivacyToast(context);
462     // Disabling local video doesn't seem to work when dialing. See a bug.
463     final boolean showPauseVideo =
464         isVideo
465             && call.getState() != DialerCall.State.DIALING
466             && call.getState() != DialerCall.State.CONNECTING;
467 
468     otherAccount = TelecomUtil.getOtherAccount(getContext(), call.getAccountHandle());
469     boolean showSwapSim =
470         otherAccount != null
471             && !call.isVoiceMailNumber()
472             && DialerCall.State.isDialing(call.getState())
473             // Most devices cannot make calls on 2 SIMs at the same time.
474             && InCallPresenter.getInstance().getCallList().getAllCalls().size() == 1;
475 
476     inCallButtonUi.showButton(InCallButtonIds.BUTTON_AUDIO, true);
477     inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP, showSwap);
478     inCallButtonUi.showButton(InCallButtonIds.BUTTON_HOLD, showHold);
479     inCallButtonUi.setHold(isCallOnHold);
480     inCallButtonUi.showButton(InCallButtonIds.BUTTON_MUTE, showMute);
481     inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP_SIM, showSwapSim);
482     inCallButtonUi.showButton(InCallButtonIds.BUTTON_ADD_CALL, true);
483     inCallButtonUi.enableButton(InCallButtonIds.BUTTON_ADD_CALL, showAddCall);
484     inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo);
485     inCallButtonUi.showButton(InCallButtonIds.BUTTON_DOWNGRADE_TO_AUDIO, showDowngradeToAudio);
486     inCallButtonUi.showButton(
487         InCallButtonIds.BUTTON_SWITCH_CAMERA,
488         isVideo && hasCameraPermission && call.getVideoTech().isTransmitting());
489     inCallButtonUi.showButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, showPauseVideo);
490     if (isVideo) {
491       inCallButtonUi.setVideoPaused(!call.getVideoTech().isTransmitting() || !hasCameraPermission);
492     }
493     inCallButtonUi.showButton(InCallButtonIds.BUTTON_DIALPAD, true);
494     inCallButtonUi.showButton(InCallButtonIds.BUTTON_MERGE, showMerge);
495 
496     inCallButtonUi.updateButtonStates();
497   }
498 
hasVideoCallCapabilities(DialerCall call)499   private boolean hasVideoCallCapabilities(DialerCall call) {
500     return call.getVideoTech().isAvailable(context, call.getAccountHandle());
501   }
502 
503   /**
504    * Determines if downgrading from a video call to an audio-only call is supported. In order to
505    * support downgrade to audio, the SDK version must be >= N and the call should NOT have the
506    * {@link android.telecom.Call.Details#CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO}.
507    *
508    * @param call The call.
509    * @return {@code true} if downgrading to an audio-only call from a video call is supported.
510    */
isDowngradeToAudioSupported(DialerCall call)511   private boolean isDowngradeToAudioSupported(DialerCall call) {
512     // TODO(a bug): If there is an RCS video share session, return true here
513     return !call.can(CallCompat.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO);
514   }
515 
516   @Override
refreshMuteState()517   public void refreshMuteState() {
518     // Restore the previous mute state
519     if (automaticallyMuted
520         && AudioModeProvider.getInstance().getAudioState().isMuted() != previousMuteState) {
521       if (inCallButtonUi == null) {
522         return;
523       }
524       muteClicked(previousMuteState, false /* clickedByUser */);
525     }
526     automaticallyMuted = false;
527   }
528 
529   @Override
onSaveInstanceState(Bundle outState)530   public void onSaveInstanceState(Bundle outState) {
531     outState.putBoolean(KEY_AUTOMATICALLY_MUTED, automaticallyMuted);
532     outState.putBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState);
533   }
534 
535   @Override
onRestoreInstanceState(Bundle savedInstanceState)536   public void onRestoreInstanceState(Bundle savedInstanceState) {
537     automaticallyMuted = savedInstanceState.getBoolean(KEY_AUTOMATICALLY_MUTED, automaticallyMuted);
538     previousMuteState = savedInstanceState.getBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState);
539   }
540 
541   @Override
onCameraPermissionGranted()542   public void onCameraPermissionGranted() {
543     if (call != null) {
544       updateButtonsState(call);
545     }
546   }
547 
548   @Override
onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera)549   public void onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera) {
550     if (inCallButtonUi == null) {
551       return;
552     }
553     inCallButtonUi.setCameraSwitched(!isUsingFrontFacingCamera);
554   }
555 
556   @Override
getContext()557   public Context getContext() {
558     return context;
559   }
560 
getActivity()561   private InCallActivity getActivity() {
562     if (inCallButtonUi != null) {
563       Fragment fragment = inCallButtonUi.getInCallButtonUiFragment();
564       if (fragment != null) {
565         return (InCallActivity) fragment.getActivity();
566       }
567     }
568     return null;
569   }
570 }
571