Revert "Revert "Support car-mode UI in telecom.""

This reverts commit 88cda8452124d43c145d54a97477dc5cf8a32a58.

Change-Id: Icfd1ab3ed3af39f62bd828dceb5ff2d4d517cb91
diff --git a/testapps/AndroidManifest.xml b/testapps/AndroidManifest.xml
index 0672b6d..e645123 100644
--- a/testapps/AndroidManifest.xml
+++ b/testapps/AndroidManifest.xml
@@ -54,6 +54,7 @@
         <service android:name="com.android.server.telecom.testapps.TestInCallServiceImpl"
                  android:process="com.android.server.telecom.testapps.TestInCallService"
                  android:permission="android.permission.BIND_INCALL_SERVICE" >
+            <meta-data android:name="android.telecom.IN_CALL_SERVICE_CAR_MODE_UI" android:value="true" />
             <intent-filter>
                 <action android:name="android.telecom.InCallService"/>
             </intent-filter>
@@ -68,6 +69,17 @@
             </intent-filter>
         </receiver>
 
+        <activity android:name="com.android.server.telecom.testapps.TestInCallUI"
+                android:process="com.android.server.telecom.testapps.TestInCallService"
+                android:label="@string/inCallUiAppLabel"
+                android:launchMode="singleInstance">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="com.android.server.telecom.testapps.TestCallActivity"
                   android:theme="@android:style/Theme.NoDisplay"
                   android:label="@string/testCallActivityLabel">
diff --git a/testapps/res/layout/call_list_item.xml b/testapps/res/layout/call_list_item.xml
new file mode 100644
index 0000000..c9f2ff7
--- /dev/null
+++ b/testapps/res/layout/call_list_item.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
+    <TextView
+            android:id="@+id/phoneNumber"
+            android:layout_gravity="left"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="25dp"
+            android:text="TextView" />
+    <TextView
+            android:id="@+id/callState"
+            android:layout_gravity="left"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="25dp"
+            android:text="TextView" />
+    <TextView
+            android:id="@+id/duration"
+            android:layout_gravity="right"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="25dp"
+            android:text="TextView" />
+</LinearLayout>
diff --git a/testapps/res/layout/incall_screen.xml b/testapps/res/layout/incall_screen.xml
new file mode 100644
index 0000000..6a891e7
--- /dev/null
+++ b/testapps/res/layout/incall_screen.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+    <ListView
+            android:id="@+id/callListView"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:divider="#FFCC00"
+            android:dividerHeight="4px">
+    </ListView>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/end_call_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/endCallButton" />
+        <Button
+            android:id="@+id/mute_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/muteButton" />
+        <Button
+            android:id="@+id/hold_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/holdButton" >
+        </Button>
+    </LinearLayout>
+</LinearLayout>
diff --git a/testapps/res/values/donottranslate_strings.xml b/testapps/res/values/donottranslate_strings.xml
index 43c302d..599d5cc 100644
--- a/testapps/res/values/donottranslate_strings.xml
+++ b/testapps/res/values/donottranslate_strings.xml
@@ -37,4 +37,12 @@
     <!-- String for button in TestDialerActivity that tries to exercise the
             TelecomManager.cancelMissedCallNotifications() functionality -->
     <string name="cancelMissedButton">Cancel missed calls</string>
+
+    <string name="endCallButton">End Call</string>
+
+    <string name="muteButton">Mute</string>
+
+    <string name="holdButton">Hold</string>
+
+    <string name="inCallUiAppLabel">Test InCall UI</string>
 </resources>
diff --git a/testapps/src/com/android/server/telecom/testapps/CallListAdapter.java b/testapps/src/com/android/server/telecom/testapps/CallListAdapter.java
new file mode 100644
index 0000000..bea0e63
--- /dev/null
+++ b/testapps/src/com/android/server/telecom/testapps/CallListAdapter.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2015 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.server.telecom.testapps;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Handler;
+import android.telecom.Call;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+public class CallListAdapter extends BaseAdapter {
+    private static final String TAG = "CallListAdapter";
+
+    private final TestCallList.Listener mListener = new TestCallList.Listener() {
+        @Override
+        public void onCallAdded(Call call) {
+            notifyDataSetChanged();
+        }
+
+        @Override
+        public void onCallRemoved(Call call) {
+            notifyDataSetChanged();
+            if (mCallList.size() == 0) {
+                mCallList.removeListener(this);
+            }
+        }
+    };
+
+    private final LayoutInflater mLayoutInflater;
+    private final TestCallList mCallList;
+    private final Handler mHandler = new Handler();
+    private final Runnable mSecondsRunnable = new Runnable() {
+        @Override
+        public void run() {
+            notifyDataSetChanged();
+            if (mCallList.size() > 0) {
+                mHandler.postDelayed(this, 1000);
+            }
+        }
+    };
+
+    public CallListAdapter(Context context) {
+        mLayoutInflater =
+                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        mCallList = TestCallList.getInstance();
+        mCallList.addListener(mListener);
+        mHandler.postDelayed(mSecondsRunnable, 1000);
+    }
+
+
+    @Override
+    public int getCount() {
+        Log.i(TAG, "size reporting: " + mCallList.size());
+        return mCallList.size();
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return position;
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return position;
+    }
+
+    @Override
+    public View getView(final int position, View convertView, ViewGroup parent) {
+        Log.i(TAG, "getView: " + position);
+        if (convertView == null) {
+            convertView = mLayoutInflater.inflate(R.layout.call_list_item, parent, false);
+        }
+
+        TextView phoneNumber = (TextView) convertView.findViewById(R.id.phoneNumber);
+        TextView duration = (TextView) convertView.findViewById(R.id.duration);
+        TextView state = (TextView) convertView.findViewById(R.id.callState);
+
+        Call call = mCallList.getCall(position);
+        Uri handle = call.getDetails().getHandle();
+        phoneNumber.setText(handle == null ? "No number" : handle.getSchemeSpecificPart());
+
+        long durationMs = System.currentTimeMillis() - call.getDetails().getConnectTimeMillis();
+        duration.setText((durationMs / 1000) + " secs");
+
+        state.setText(getStateString(call));
+
+        Log.i(TAG, "Call found: " + handle.getSchemeSpecificPart() + ", " + durationMs);
+
+        return convertView;
+    }
+
+    private static String getStateString(Call call) {
+        switch (call.getState()) {
+            case Call.STATE_ACTIVE:
+                return "active";
+            case Call.STATE_CONNECTING:
+                return "connecting";
+            case Call.STATE_DIALING:
+                return "dialing";
+            case Call.STATE_DISCONNECTED:
+                return "disconnected";
+            case Call.STATE_DISCONNECTING:
+                return "disconnecting";
+            case Call.STATE_HOLDING:
+                return "on hold";
+            case Call.STATE_NEW:
+                return "new";
+            case Call.STATE_RINGING:
+                return "ringing";
+            case Call.STATE_SELECT_PHONE_ACCOUNT:
+                return "select phone account";
+            default:
+                return "unknown";
+        }
+    }
+}
diff --git a/testapps/src/com/android/server/telecom/testapps/TestCallList.java b/testapps/src/com/android/server/telecom/testapps/TestCallList.java
index a16c4e2..704c83d 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestCallList.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestCallList.java
@@ -24,6 +24,8 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -31,6 +33,12 @@
  * Maintains a list of calls received via the {@link TestInCallServiceImpl}.
  */
 public class TestCallList extends Call.Listener {
+
+    public static abstract class Listener {
+        public void onCallAdded(Call call) {}
+        public void onCallRemoved(Call call) {}
+    }
+
     private static final TestCallList INSTANCE = new TestCallList();
     private static final String TAG = "TestCallList";
 
@@ -85,9 +93,10 @@
     }
 
     // The calls the call list knows about.
-    private Set<Call> mCalls = new ArraySet<Call>();
+    private List<Call> mCalls = new LinkedList<Call>();
     private Map<Call, TestVideoCallListener> mVideoCallListeners =
             new ArrayMap<Call, TestVideoCallListener>();
+    private Set<Listener> mListeners = new ArraySet<Listener>();
 
     /**
      * Singleton accessor.
@@ -96,14 +105,32 @@
         return INSTANCE;
     }
 
+    public void addListener(Listener listener) {
+        if (listener != null) {
+            mListeners.add(listener);
+        }
+    }
+
+    public boolean removeListener(Listener listener) {
+        return mListeners.remove(listener);
+    }
+
+    public Call getCall(int position) {
+        return mCalls.get(position);
+    }
+
     public void addCall(Call call) {
         if (mCalls.contains(call)) {
             Log.e(TAG, "addCall: Call already added.");
             return;
         }
-        Log.v(TAG, "addCall: " + call + " " + System.identityHashCode(this));
+        Log.i(TAG, "addCall: " + call + " " + System.identityHashCode(this));
         mCalls.add(call);
         call.addListener(this);
+
+        for (Listener l : mListeners) {
+            l.onCallAdded(call);
+        }
     }
 
     public void removeCall(Call call) {
@@ -111,9 +138,13 @@
             Log.e(TAG, "removeCall: Call cannot be removed -- doesn't exist.");
             return;
         }
-        Log.v(TAG, "removeCall: " + call);
+        Log.i(TAG, "removeCall: " + call);
         mCalls.remove(call);
         call.removeListener(this);
+
+        for (Listener l : mListeners) {
+            l.onCallRemoved(call);
+        }
     }
 
     public void clearCalls() {
@@ -126,6 +157,10 @@
         mVideoCallListeners.clear();
     }
 
+    public int size() {
+        return mCalls.size();
+    }
+
     /**
      * For any video calls tracked, sends an upgrade to video request.
      */
diff --git a/testapps/src/com/android/server/telecom/testapps/TestInCallServiceImpl.java b/testapps/src/com/android/server/telecom/testapps/TestInCallServiceImpl.java
index 68bbac9..03ca3d0 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestInCallServiceImpl.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestInCallServiceImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.server.telecom.testapps;
 
+import android.content.Context;
+import android.content.Intent;
 import android.telecom.Call;
 import android.telecom.InCallService;
 import android.telecom.Phone;
@@ -37,7 +39,12 @@
         @Override
         public void onCallAdded(Phone phone, Call call) {
             Log.i(TAG, "onCallAdded: " + call.toString());
-            TestCallList.getInstance().addCall(call);
+            TestCallList callList = TestCallList.getInstance();
+            callList.addCall(call);
+
+            if (callList.size() == 1) {
+                startInCallUI();
+            }
         }
 
         @Override
@@ -62,4 +69,11 @@
         mPhone = null;
         TestCallList.getInstance().clearCalls();
     }
+
+    private void startInCallUI() {
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.setClass(this, TestInCallUI.class);
+        startActivity(intent);
+    }
 }
diff --git a/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java b/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
new file mode 100644
index 0000000..ce53709
--- /dev/null
+++ b/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 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.server.telecom.testapps;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.telecom.Call;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ListView;
+
+public class TestInCallUI extends Activity {
+
+    private ListView mListView;
+    private TestCallList mCallList;
+
+    /** ${inheritDoc} */
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.incall_screen);
+
+        mListView = (ListView) findViewById(R.id.callListView);
+        mListView.setAdapter(new CallListAdapter(this));
+        mListView.setVisibility(View.VISIBLE);
+
+        mCallList = TestCallList.getInstance();
+        mCallList.addListener(new TestCallList.Listener() {
+            @Override
+            public void onCallRemoved(Call call) {
+                if (mCallList.size() == 0) {
+                    Log.i("Santos", "Ending the incall UI");
+                    finish();
+                }
+            }
+        });
+
+        View endCallButton = findViewById(R.id.end_call_button);
+        View holdButton = findViewById(R.id.hold_button);
+        View muteButton = findViewById(R.id.mute_button);
+
+        endCallButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Call call = mCallList.getCall(0);
+                if (call != null) {
+                    call.disconnect();
+                }
+            }
+        });
+        holdButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Call call = mCallList.getCall(0);
+                if (call != null) {
+                    if (call.getState() == Call.STATE_HOLDING) {
+                        call.unhold();
+                    } else {
+                        call.hold();
+                    }
+                }
+            }
+        });
+        muteButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Call call = mCallList.getCall(0);
+                if (call != null) {
+                }
+            }
+        });
+    }
+
+    /** ${inheritDoc} */
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+}