Integrating mute and speaker buttons.

* Adding base classes for presenter, ui and fragments.
* Moved common presenter integration logic into base fragment.
* Adding presenter to handle button logic.
* Integrated disconnect and text from glowpad to presenter.
* Changed in-call buttons to be invisible so they do not show under transparent
glowpad.

Change-Id: I446db149769b5cf1abce960ecede01effeabfe1e
diff --git a/InCallUI/res/layout/answer_fragment.xml b/InCallUI/res/layout/answer_fragment.xml
index b334a12..bad43d6 100644
--- a/InCallUI/res/layout/answer_fragment.xml
+++ b/InCallUI/res/layout/answer_fragment.xml
@@ -16,7 +16,7 @@
   -->
 
 <!-- TODO(klp): move out to separate file -->
-<com.android.incallui.AnswerUi
+<com.android.incallui.GlowPadWrapper
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:dc="http://schemas.android.com/apk/res-auto"
 
@@ -29,7 +29,7 @@
         android:layout_centerHorizontal="true"
         android:gravity="center"
         android:layout_gravity="bottom|center_horizontal"
-        android:background="#000"
+        android:background="#00000000"
 
         dc:targetDrawables="@array/incoming_call_widget_3way_targets"
         dc:targetDescriptions="@array/incoming_call_widget_3way_target_descriptions"
diff --git a/InCallUI/res/layout/call_button_fragment.xml b/InCallUI/res/layout/call_button_fragment.xml
index 1c2e7c7..164201b 100644
--- a/InCallUI/res/layout/call_button_fragment.xml
+++ b/InCallUI/res/layout/call_button_fragment.xml
@@ -34,7 +34,8 @@
               android:orientation="vertical"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
-              android:layout_alignParentBottom="true">
+              android:layout_alignParentBottom="true"
+              android:visibility="invisible">
 
     <!-- Row 1, the "extra button row": A couple of relatively rare
          buttons used only in certain call states.
diff --git a/InCallUI/res/layout/call_card_fragment.xml b/InCallUI/res/layout/call_card_fragment.xml
index 688563d..44a9f6f 100644
--- a/InCallUI/res/layout/call_card_fragment.xml
+++ b/InCallUI/res/layout/call_card_fragment.xml
@@ -17,7 +17,7 @@
   -->
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-             android:layout_width="fill_parent"
+             android:layout_width="match_parent"
              android:layout_height="0dp"
              android:layout_weight="1">
 
diff --git a/InCallUI/res/layout/incall_screen.xml b/InCallUI/res/layout/incall_screen.xml
index 403a17c..99319f4 100644
--- a/InCallUI/res/layout/incall_screen.xml
+++ b/InCallUI/res/layout/incall_screen.xml
@@ -25,11 +25,18 @@
                   android:layout_height="match_parent"
                   android:orientation="vertical">
 
-        <!-- Call card fragment will be added here. -->
-        <!-- Call button fragment will be added here. -->
+        <fragment android:name="com.android.incallui.CallCardFragment"
+                  android:id="@+id/callCardFragment"
+                  android:layout_width="match_parent"
+                  android:layout_height="0dp"
+                  android:layout_weight="1"/>
+        <fragment android:name="com.android.incallui.CallButtonFragment"
+                  android:id="@+id/callButtonFragment"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"/>
 
     </LinearLayout>
 
-    <!-- Glowpad fragment will be added here to overlay. -->
+    <!-- Glowpad fragment loaded here on demand. -->
 
 </FrameLayout>
diff --git a/InCallUI/src/com/android/incallui/AnswerFragment.java b/InCallUI/src/com/android/incallui/AnswerFragment.java
index 743fa58..90bc436 100644
--- a/InCallUI/src/com/android/incallui/AnswerFragment.java
+++ b/InCallUI/src/com/android/incallui/AnswerFragment.java
@@ -16,7 +16,6 @@
 
 package com.android.incallui;
 
-import android.app.Fragment;
 import android.app.FragmentTransaction;
 import android.os.Bundle;
 import android.view.LayoutInflater;
@@ -26,20 +25,26 @@
 /**
  *
  */
-public class AnswerFragment extends Fragment {
+public class AnswerFragment extends BaseFragment<AnswerPresenter> implements
+        GlowPadWrapper.AnswerListener {
+
+    @Override
+    public AnswerPresenter createPresenter() {
+        return new AnswerPresenter(new AnswerPresenter.Listener() {
+            @Override
+            public void onClose() {
+                close();
+            }
+        });
+    }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
-        final AnswerUi ui = (AnswerUi) inflater.inflate(R.layout.answer_fragment, container,
-                false);
-        final AnswerPresenter presenter = new AnswerPresenter(ui, new AnswerPresenter.Listener() {
-            @Override
-            public void onAnswered() {
-                close();
-            }
-        });
-        return ui;
+        final GlowPadWrapper glowPad = (GlowPadWrapper) inflater.inflate(R.layout.answer_fragment,
+                container, false);
+        glowPad.setAnswerListener(this);
+        return glowPad;
     }
 
     private void close() {
@@ -47,4 +52,19 @@
         fragmentTransaction.remove(this);
         fragmentTransaction.commit();
     }
+
+    @Override
+    public void onAnswer() {
+        getPresenter().onAnswer();
+    }
+
+    @Override
+    public void onDecline() {
+        getPresenter().onDecline();
+    }
+
+    @Override
+    public void onText() {
+        getPresenter().onText();
+    }
 }
diff --git a/InCallUI/src/com/android/incallui/AnswerPresenter.java b/InCallUI/src/com/android/incallui/AnswerPresenter.java
index 933d758..ecaa5cc 100644
--- a/InCallUI/src/com/android/incallui/AnswerPresenter.java
+++ b/InCallUI/src/com/android/incallui/AnswerPresenter.java
@@ -16,40 +16,46 @@
 
 package com.android.incallui;
 
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+
 /**
  *
  */
-public class AnswerPresenter {
+public class AnswerPresenter extends Presenter<Ui> {
 
-    private Ui mUi;
-    private Listener mListener;
+    private ArrayList<Listener> mListeners = Lists.newArrayList();
 
-    public AnswerPresenter(Ui ui, Listener listener) {
-        this.mUi = ui;
-        this.mListener = listener;
+    public AnswerPresenter(Listener listener) {
+        this.mListeners.add(listener);
+    }
 
-        mUi.setPresenter(this);
+    public void addCloseListener(Listener listener) {
+        mListeners.add(listener);
     }
 
     public void onAnswer() {
         // TODO(klp): hook in call id.
         CallCommandService.getInstance().answerCall(1);
-        mListener.onAnswered();
+        notifyListeners();
     }
 
     public void onDecline() {
-        mListener.onAnswered();
+        notifyListeners();
     }
 
     public void onText() {
-        mListener.onAnswered();
+        notifyListeners();
     }
 
-    public interface Ui {
-        void setPresenter(AnswerPresenter presenter);
+    private void notifyListeners() {
+        for (Listener listener : mListeners) {
+            listener.onClose();
+        }
     }
 
     public interface Listener {
-        void onAnswered();
+        void onClose();
     }
 }
diff --git a/InCallUI/src/com/android/incallui/BaseFragment.java b/InCallUI/src/com/android/incallui/BaseFragment.java
new file mode 100644
index 0000000..a88f840
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/BaseFragment.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 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.android.incallui;
+
+import android.app.Fragment;
+
+/**
+ *
+ */
+public abstract class BaseFragment<T extends Presenter> extends Fragment {
+
+    private T mPresenter;
+
+    protected BaseFragment() {
+        this.mPresenter = createPresenter();
+    }
+
+    abstract T createPresenter();
+
+    public T getPresenter() {
+        return mPresenter;
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/CallButtonFragment.java b/InCallUI/src/com/android/incallui/CallButtonFragment.java
index 856d186..4c74f4a 100644
--- a/InCallUI/src/com/android/incallui/CallButtonFragment.java
+++ b/InCallUI/src/com/android/incallui/CallButtonFragment.java
@@ -16,20 +16,79 @@
 
 package com.android.incallui;
 
-import android.app.Fragment;
+import android.content.Context;
+import android.media.AudioManager;
 import android.os.Bundle;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.CompoundButton;
+import android.widget.ToggleButton;
 
 /**
  * Fragment for call control buttons
  */
-public class CallButtonFragment extends Fragment {
+public class CallButtonFragment extends BaseFragment<CallButtonPresenter> implements
+        CallButtonPresenter.CallButtonUi {
+
+    @Override
+    CallButtonPresenter createPresenter() {
+        return new CallButtonPresenter();
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final AudioManager audioManager = (AudioManager) getActivity().getSystemService(
+                Context.AUDIO_SERVICE);
+        getPresenter().init(audioManager);
+    }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
-        return inflater.inflate(R.layout.call_button_fragment, container, false);
+        final View parent = inflater.inflate(R.layout.call_button_fragment, container, false);
+        final ToggleButton toggleButton = (ToggleButton) parent.findViewById(R.id.muteButton);
+        toggleButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                getPresenter().muteClicked(isChecked);
+            }
+        });
+
+        final ToggleButton audioButton = (ToggleButton) parent.findViewById(R.id.audioButton);
+        audioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                getPresenter().speakerClicked(isChecked);
+            }
+        });
+
+        return parent;
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        getPresenter().onUiReady(this);
+    }
+
+    @Override
+    public void setVisible() {
+        Log.e("TEST", "" + getView());
+        getView().setVisibility(View.VISIBLE);
+    }
+
+    @Override
+    public void setMute(boolean value) {
+        final ToggleButton button = (ToggleButton) getView().findViewById(R.id.muteButton);
+        button.setChecked(value);
+    }
+
+    @Override
+    public void setSpeaker(boolean value) {
+        final ToggleButton button = (ToggleButton) getView().findViewById(R.id.audioButton);
+        button.setChecked(value);
     }
 }
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
new file mode 100644
index 0000000..f1e83e2
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 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.android.incallui;
+
+import android.media.AudioManager;
+
+/**
+ * Logic for call buttons.
+ */
+public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButtonUi> {
+
+    private AudioManager mAudioManager;
+
+    public void init(AudioManager audioManager) {
+        mAudioManager = audioManager;
+    }
+
+    @Override
+    public void onUiReady(CallButtonUi ui) {
+        super.onUiReady(ui);
+        getUi().setMute(mAudioManager.isMicrophoneMute());
+        getUi().setSpeaker(mAudioManager.isSpeakerphoneOn());
+    }
+
+    public void show() {
+        getUi().setVisible();
+    }
+
+    public void muteClicked(boolean checked) {
+        CallCommandService.getInstance().mute(checked);
+        getUi().setMute(checked);
+    }
+
+    public void speakerClicked(boolean checked) {
+        CallCommandService.getInstance().turnSpeakerOn(checked);
+        getUi().setSpeaker(checked);
+    }
+
+    public interface CallButtonUi extends Ui {
+        void setVisible();
+        void setMute(boolean on);
+        void setSpeaker(boolean on);
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/CallCommandService.java b/InCallUI/src/com/android/incallui/CallCommandService.java
index 0264af1..7b36b76 100644
--- a/InCallUI/src/com/android/incallui/CallCommandService.java
+++ b/InCallUI/src/com/android/incallui/CallCommandService.java
@@ -55,15 +55,23 @@
         try {
             mCommandService.answerCall(callId);
         } catch (RemoteException e) {
-            Log.e(TAG, "answerCall : " + e);
+            Log.e(TAG, "Error answering call.", e);
         }
     }
 
-    public void mute() {
-
+    public void mute(boolean onOff) {
+        try {
+            mCommandService.mute(onOff);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error muting phone.", e);
+        }
     }
 
-    public void turnSpeakerOn() {
-
+    public void turnSpeakerOn(boolean onOff) {
+        try {
+            mCommandService.speaker(onOff);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error setting speaker.", e);
+        }
     }
 }
diff --git a/InCallUI/src/com/android/incallui/CallHandlerService.java b/InCallUI/src/com/android/incallui/CallHandlerService.java
index 490adc0..dac6563 100644
--- a/InCallUI/src/com/android/incallui/CallHandlerService.java
+++ b/InCallUI/src/com/android/incallui/CallHandlerService.java
@@ -19,10 +19,8 @@
 import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
-import android.os.RemoteException;
 import android.util.Log;
 
-import com.android.internal.util.Preconditions;
 import com.android.services.telephony.common.ICallCommandService;
 import com.android.services.telephony.common.ICallHandlerService;
 
diff --git a/InCallUI/src/com/android/incallui/AnswerUi.java b/InCallUI/src/com/android/incallui/GlowPadWrapper.java
similarity index 85%
rename from InCallUI/src/com/android/incallui/AnswerUi.java
rename to InCallUI/src/com/android/incallui/GlowPadWrapper.java
index 295f9b8..4403e6b 100644
--- a/InCallUI/src/com/android/incallui/AnswerUi.java
+++ b/InCallUI/src/com/android/incallui/GlowPadWrapper.java
@@ -29,10 +29,9 @@
 /**
  *
  */
-public class AnswerUi extends GlowPadView implements AnswerPresenter.Ui,
-        GlowPadView.OnTriggerListener {
+public class GlowPadWrapper extends GlowPadView implements GlowPadView.OnTriggerListener {
 
-    private static final String TAG = AnswerUi.class.getSimpleName();
+    private static final String TAG = GlowPadWrapper.class.getSimpleName();
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     // Parameters for the GlowPadView "ping" animation; see triggerPing().
@@ -51,14 +50,14 @@
         }
     };
 
-    private AnswerPresenter mPresenter;
+    private AnswerListener mAnswerListener;
     private boolean mPingEnabled = true;
 
-    public AnswerUi(Context context) {
+    public GlowPadWrapper(Context context) {
         super(context);
     }
 
-    public AnswerUi(Context context, AttributeSet attrs) {
+    public GlowPadWrapper(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
@@ -130,13 +129,13 @@
         final int resId = getResourceIdForTarget(target);
         switch (resId) {
             case R.drawable.ic_lockscreen_answer:
-                mPresenter.onAnswer();
+                mAnswerListener.onAnswer();
                 break;
             case R.drawable.ic_lockscreen_decline:
-                mPresenter.onDecline();
+                mAnswerListener.onDecline();
                 break;
             case R.drawable.ic_lockscreen_text:
-                mPresenter.onText();
+                mAnswerListener.onText();
                 break;
             default:
                 // Code should never reach here.
@@ -154,9 +153,14 @@
 
     }
 
-    @Override
-    public void setPresenter(AnswerPresenter listener) {
-        mPresenter = listener;
+    public void setAnswerListener(AnswerListener listener) {
+        mAnswerListener = listener;
+    }
+
+    public interface AnswerListener {
+        void onAnswer();
+        void onDecline();
+        void onText();
     }
 
     private void logD(String msg) {
diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java
index 380d487..c2e0b05 100644
--- a/InCallUI/src/com/android/incallui/InCallActivity.java
+++ b/InCallUI/src/com/android/incallui/InCallActivity.java
@@ -37,6 +37,8 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
+    private CallButtonPresenter mCallButtonPresenter;
+
     @Override
     protected void onCreate(Bundle icicle) {
         logD("onCreate()...  this = " + this);
@@ -58,18 +60,26 @@
         logD("onCreate(): exit");
     }
 
+
     @Override
     protected void onResume() {
         logD("onResume()...");
 
+        final CallButtonFragment callButtonFragment = (CallButtonFragment) getFragmentManager()
+                .findFragmentById(R.id.callButtonFragment);
+        mCallButtonPresenter = callButtonFragment.getPresenter();
+
         // TODO(klp): create once and reset when needed.
         final AnswerFragment answerFragment = new AnswerFragment();
-        final CallCardFragment callCardFragment = new CallCardFragment();
-        final CallButtonFragment callButtonFragment = new CallButtonFragment();
-        final FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
-        fragmentTransaction.add(R.id.in_call_and_button_container, callCardFragment);
-        fragmentTransaction.add(R.id.in_call_and_button_container, callButtonFragment);
+        final AnswerPresenter presenter = answerFragment.getPresenter();
+        presenter.addCloseListener(new AnswerPresenter.Listener() {
+            @Override
+            public void onClose() {
+                mCallButtonPresenter.show();
+            }
+        });
 
+        final FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
         fragmentTransaction.add(R.id.main, answerFragment);
         fragmentTransaction.commit();
         super.onResume();
@@ -206,10 +216,4 @@
             Log.d(TAG, msg);
         }
     }
-
-    private void logV(String msg) {
-        if (VERBOSE) {
-            Log.v(TAG, msg);
-        }
-    }
 }
diff --git a/InCallUI/src/com/android/incallui/Presenter.java b/InCallUI/src/com/android/incallui/Presenter.java
new file mode 100644
index 0000000..d4024d5
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/Presenter.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 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.android.incallui;
+
+/**
+ * Base class for Presenters.
+ */
+public abstract class Presenter<U extends Ui> {
+
+    private U mUi;
+
+    /**
+     * 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) {
+        mUi = ui;
+    }
+
+    public U getUi() {
+        return mUi;
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/Ui.java b/InCallUI/src/com/android/incallui/Ui.java
new file mode 100644
index 0000000..e453ccb
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/Ui.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2013 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.android.incallui;
+
+/**
+ * Base class for all presenter ui.
+ */
+public interface Ui {
+
+}