android: first publishable implementation of Ring on Android
Change-Id: I6d65993a0bbed0ac680d6fe5980aae0fb931116e
diff --git a/ring-android/app/app.iml b/ring-android/app/app.iml
index ece991d..3502718 100644
--- a/ring-android/app/app.iml
+++ b/ring-android/app/app.iml
@@ -12,10 +12,12 @@
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
- <option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
<option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
- <option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugAndroidTestSources" />
+ <afterSyncTasks>
+ <task>generateDebugAndroidTestSources</task>
+ <task>generateDebugSources</task>
+ </afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
@@ -24,7 +26,7 @@
</configuration>
</facet>
</component>
- <component name="NewModuleRootManager" inherit-compiler-output="false">
+ <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
<exclude-output />
@@ -34,13 +36,13 @@
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
- <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
- <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build-types/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build-types/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build-types/debug/assets" type="java-resource" />
@@ -67,11 +69,12 @@
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
- <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.2.0/jars" />
- <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/22.2.0/jars" />
- <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v13/22.2.0/jars" />
- <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/22.2.0/jars" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.0.1/jars" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/23.0.1/jars" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v13/23.0.1/jars" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.0.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.astuetz/pagerslidingtabstrip/1.0.1/jars" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/se.emilsjolander/stickylistheaders/2.7.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
@@ -87,16 +90,18 @@
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
- <orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />
+ <orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="ormlite-android-4.48" level="project" />
+ <orderEntry type="library" exported="" name="libphonenumber-7.0.11" level="project" />
<orderEntry type="library" exported="" name="pagerslidingtabstrip-1.0.1" level="project" />
<orderEntry type="library" exported="" name="ormlite-core-4.48" level="project" />
- <orderEntry type="library" exported="" name="robotium-solo-5.0.1" level="project" />
- <orderEntry type="library" exported="" name="support-annotations-22.2.0" level="project" />
- <orderEntry type="library" exported="" name="support-v13-22.2.0" level="project" />
- <orderEntry type="library" exported="" name="support-v4-22.2.0" level="project" />
- <orderEntry type="library" exported="" name="design-22.2.0" level="project" />
- <orderEntry type="library" exported="" name="appcompat-v7-22.2.0" level="project" />
+ <orderEntry type="library" exported="" name="support-v13-23.0.1" level="project" />
+ <orderEntry type="library" exported="" name="stickylistheaders-2.7.0" level="project" />
+ <orderEntry type="library" exported="" name="support-v4-23.0.1" level="project" />
+ <orderEntry type="library" exported="" name="robotium-solo-5.4.1" level="project" />
+ <orderEntry type="library" exported="" name="design-23.0.1" level="project" />
+ <orderEntry type="library" exported="" name="appcompat-v7-23.0.1" level="project" />
+ <orderEntry type="library" exported="" name="support-annotations-23.0.1" level="project" />
</component>
</module>
\ No newline at end of file
diff --git a/ring-android/app/build.gradle b/ring-android/app/build.gradle
index e1bcbff..2ca1c19 100644
--- a/ring-android/app/build.gradle
+++ b/ring-android/app/build.gradle
@@ -1,18 +1,20 @@
apply plugin: 'com.android.application'
dependencies {
- compile fileTree(dir: 'libs', include: '*.jar')
+ compile fileTree(include: '*.jar', dir: 'libs')
compile 'com.j256.ormlite:ormlite-core:4.48'
compile 'com.j256.ormlite:ormlite-android:4.48'
- compile "com.android.support:support-v13:22.2.0"
- compile 'com.android.support:design:22.2.0'
+ compile 'com.android.support:support-v13:23.0.+'
+ compile 'com.android.support:design:23.0.+'
compile 'com.jayway.android.robotium:robotium-solo:5.4.1'
compile 'com.astuetz:pagerslidingtabstrip:1.0.1'
+ compile 'com.googlecode.libphonenumber:libphonenumber:7.0.11'
+ compile 'se.emilsjolander:stickylistheaders:2.7.+'
}
android {
- compileSdkVersion 22
- buildToolsVersion "22.0.1"
+ compileSdkVersion 23
+ buildToolsVersion "23.0.1"
sourceSets {
main {
@@ -33,4 +35,8 @@
debug.setRoot('build-types/debug')
release.setRoot('build-types/release')
}
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_7
+ targetCompatibility JavaVersion.VERSION_1_7
+ }
}
\ No newline at end of file
diff --git a/ring-android/app/src/main/AndroidManifest.xml b/ring-android/app/src/main/AndroidManifest.xml
index ceb3bd8..321aa68 100644
--- a/ring-android/app/src/main/AndroidManifest.xml
+++ b/ring-android/app/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2004-2014 Savoir-Faire Linux Inc.
+Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
Adrien Beraud <adrien.beraud@gmail.com>
@@ -36,6 +36,10 @@
android:versionCode="14"
android:versionName="2.0.0" >
+ <uses-sdk
+ android:minSdkVersion="16"
+ android:targetSdkVersion="23" />
+
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
@@ -43,10 +47,6 @@
android:smallScreens="true"
android:xlargeScreens="true" />
- <uses-sdk
- android:minSdkVersion="15"
- android:targetSdkVersion="22" />
-
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
@@ -67,6 +67,8 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.USE_SIP" />
+ <uses-permission android:name="android.permission.GET_TASKS" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-feature
android:name="android.hardware.wifi"
@@ -89,7 +91,7 @@
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
- android:name="cx.ring.client.HomeActivity"
+ android:name=".client.HomeActivity"
android:label="@string/title_activity_sflphone_home"
android:screenOrientation="portrait"
android:theme="@style/AppThemeWithOverlay"
@@ -101,40 +103,74 @@
</intent-filter>
</activity>
<activity
- android:name="cx.ring.client.AccountWizard"
+ android:name=".client.AccountWizard"
android:screenOrientation="portrait"
android:theme="@style/AppThemeWithoutOverlay" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value="cx.ring.client.AccountPreferenceActivity" />
+ android:value="cx.ring.client.AccountEditionActivity" />
</activity>
<activity
- android:name="cx.ring.client.AccountEditionActivity"
+ android:name=".client.AccountEditionActivity"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@style/AppThemeWithoutOverlay" />
<activity
- android:name="cx.ring.client.DetailHistoryActivity"
+ android:name=".client.DetailHistoryActivity"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@style/AppThemeWithoutOverlay" />
+
+ <activity android:name=".client.NewConversationActivity" android:theme="@style/AppThemeWithoutOverlay" android:label="@string/app_name" >
+ </activity>
+
<activity
- android:name="cx.ring.client.CallActivity"
+ android:name=".client.CallActivity"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@style/AppThemeWithoutOverlay"
android:windowSoftInputMode="adjustPan" >
<intent-filter>
<action android:name="android.intent.action.CALL_PRIVILEGED" />
+ <action android:name="android.intent.action.CALL" />
+ <action android:name="android.intent.action.DIAL" />
+ <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel" />
+ <data android:scheme="ring" />
+ <data android:scheme="sip" />
</intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.CALL" />
+ <action android:name="android.intent.action.DIAL" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+
+ <data android:mimeType="vnd.android.cursor.item/phone" />
+ <data android:mimeType="vnd.android.cursor.item/phone_v2" />
+ <data android:mimeType="vnd.android.cursor.item/person" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".client.ConversationActivity"
+ android:label="@string/app_name"
+ android:parentActivityName=".client.HomeActivity"
+ android:screenOrientation="portrait"
+ android:theme="@style/AppThemeWithoutOverlay"
+ android:windowSoftInputMode="adjustResize" >
</activity>
<service
- android:name="cx.ring.service.SipService"
+ android:name=".service.LocalService"
+ android:exported="false" >
+ <intent-filter>
+ <action android:name=".service.LocalService" />
+ </intent-filter>
+ </service>
+ <service
+ android:name=".service.SipService"
android:exported="false" >
<intent-filter>
<action android:name=".service.SipService" />
@@ -142,4 +178,4 @@
</service>
</application>
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/ring-android/app/src/main/java/cx/ring/adapters/AccountSelectionAdapter.java b/ring-android/app/src/main/java/cx/ring/adapters/AccountSelectionAdapter.java
index 003f347..6a2ab31 100644
--- a/ring-android/app/src/main/java/cx/ring/adapters/AccountSelectionAdapter.java
+++ b/ring-android/app/src/main/java/cx/ring/adapters/AccountSelectionAdapter.java
@@ -32,12 +32,11 @@
package cx.ring.adapters;
import java.io.File;
-import java.util.ArrayList;
+import java.util.List;
import cx.ring.R;
import android.content.Context;
-import android.graphics.Color;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -51,12 +50,12 @@
private static final String TAG = AccountSelectionAdapter.class.getSimpleName();
- ArrayList<Account> accounts;
+ List<Account> accounts;
Context mContext;
int selectedAccount = -1;
static final String DEFAULT_ACCOUNT_ID = "IP2IP";
- public AccountSelectionAdapter(Context cont, ArrayList<Account> newList) {
+ public AccountSelectionAdapter(Context cont, List<Account> newList) {
super();
accounts = newList;
mContext = cont;
@@ -133,7 +132,11 @@
private void updateAccountView(AccountView entryView, Account acc) {
entryView.alias.setText(acc.getAlias());
- entryView.host.setText(acc.getHost()/*+ " - " + acc.getRegistered_state()*/);
+ if (acc.isRing()) {
+ entryView.host.setText(acc.getBasicDetails().getUsername());
+ } else {
+ entryView.host.setText(acc.getBasicDetails().getUsername() + "@" + acc.getBasicDetails().getHostname());
+ }
entryView.error.setVisibility(acc.isRegistered() ? View.GONE : View.VISIBLE);
}
@@ -171,7 +174,13 @@
}
- public void addAll(ArrayList<Account> results) {
+ public void addAll(List<Account> results) {
+ accounts.addAll(results);
+ notifyDataSetChanged();
+ }
+
+ public void replaceAll(List<Account> results) {
+ accounts.clear();
accounts.addAll(results);
notifyDataSetChanged();
}
@@ -184,7 +193,7 @@
for (Account a : accounts) {
if (a.getAccountID().contentEquals(accoundID)) {
- a.setRegistered_state(state, code);
+ a.setRegistrationState(state, code);
Log.i(TAG, "updateAccount " + accoundID + " " + code);
notifyDataSetChanged();
return;
diff --git a/ring-android/app/src/main/java/cx/ring/adapters/ContactPictureTask.java b/ring-android/app/src/main/java/cx/ring/adapters/ContactPictureTask.java
index 5596a06..0377dc7 100644
--- a/ring-android/app/src/main/java/cx/ring/adapters/ContactPictureTask.java
+++ b/ring-android/app/src/main/java/cx/ring/adapters/ContactPictureTask.java
@@ -32,6 +32,8 @@
package cx.ring.adapters;
import java.io.InputStream;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import cx.ring.R;
import cx.ring.model.CallContact;
@@ -51,20 +53,55 @@
import android.graphics.Shader;
import android.net.Uri;
import android.provider.ContactsContract;
+import android.util.Log;
+import android.util.LruCache;
+import android.view.animation.AnimationUtils;
import android.widget.ImageView;
public class ContactPictureTask implements Runnable {
- private ImageView view;
- private CallContact contact;
+ static final String TAG = ContactPictureTask.class.getSimpleName();
+
+ private final WeakReference<ImageView> view;
+ private final CallContact contact;
+
private ContentResolver cr;
- private static int PADDING = 5;
+ private final Resources res;
+ //private PictureLoadedCallback cb;
+ private final ArrayList<PictureLoadedCallback> callbacks = new ArrayList<>(1);
+
+ //int w = photo_bmp.getWidth(), h = photo_bmp.getHeight();
+ private final int vw, vh;
+
+ public void addCallback(PictureLoadedCallback cb) {
+ //this.cb = callback;
+ synchronized (callbacks) {
+ view.clear();
+ callbacks.add(cb);
+ }
+ }
// private final String TAG = ContactPictureTask.class.getSimpleName();
+ public interface PictureLoadedCallback {
+ void onPictureLoaded(Bitmap bmp);
+ };
+
public ContactPictureTask(Context context, ImageView element, CallContact item) {
contact = item;
cr = context.getContentResolver();
- view = element;
+ res = context.getResources();
+ view = new WeakReference<>(element);
+ vw = element.getWidth();
+ vh = element.getHeight();
+ }
+ public ContactPictureTask(Context context, ImageView element, CallContact item, PictureLoadedCallback cb) {
+ contact = item;
+ cr = context.getContentResolver();
+ res = context.getResources();
+ vw = element.getWidth();
+ vh = element.getHeight();
+ view = new WeakReference<>(element);
+ addCallback(cb);
}
public static Bitmap loadContactPhoto(ContentResolver cr, long id) {
@@ -80,17 +117,27 @@
@Override
public void run() {
+ Log.i(TAG, "ContactPictureTask run " + contact.getId());
+
Bitmap photo_bmp;
try {
photo_bmp = loadContactPhoto(cr, contact.getId());
} catch (IllegalArgumentException e) {
photo_bmp = null;
}
+ cr = null;
- int dpiPadding = (int) (PADDING * view.getResources().getDisplayMetrics().density);
+ /*final ImageView v = view.get();
+ view.clear();
+ if (v == null) {
+ Log.i(TAG, "ContactPictureTask cancelling: view is now null");
+ return;
+ }*/
+
+ //int dpiPadding = (int) (PADDING * view.getResources().getDisplayMetrics().density);
if (photo_bmp == null) {
- photo_bmp = decodeSampledBitmapFromResource(view.getResources(), R.drawable.ic_contact_picture, view.getWidth(), view.getHeight());
+ photo_bmp = decodeSampledBitmapFromResource(res, R.drawable.ic_contact_picture, vw, vh);
}
int w = photo_bmp.getWidth(), h = photo_bmp.getHeight();
@@ -119,14 +166,41 @@
// internalCanvas.drawOval(new RectF(PADDING, PADDING, externalBMP.getWidth() - dpiPadding, externalBMP.getHeight() - dpiPadding), paint);
internalCanvas.drawOval(new RectF(0, 0, externalBMP.getWidth(), externalBMP.getHeight()), paint);
- view.post(new Runnable() {
+ photo_bmp.recycle();
+
+ contact.setPhoto(externalBMP);
+ //v.invalidate();
+ synchronized (callbacks) {
+ final ImageView v = view.get();
+ view.clear();
+ if (v == null) {
+ for (PictureLoadedCallback cb : callbacks) {
+ cb.onPictureLoaded(externalBMP);
+ }
+ } else {
+ v.post(new Runnable() {
+ @Override
+ public void run() {
+ v.setImageBitmap(externalBMP);
+ }
+ });
+ }
+ callbacks.clear();
+ }
+
+ /*v.post(new Runnable() {
@Override
public void run() {
- view.setImageBitmap(externalBMP);
+ Log.w(TAG, "ContactPictureTask END " + contact.getId());
+ //v.setImageBitmap(externalBMP);
contact.setPhoto(externalBMP);
- view.invalidate();
+ //v.invalidate();
+ for (PictureLoadedCallback cb : callbacks) {
+ cb.onPictureLoaded(externalBMP);
+ }
+ callbacks.clear();
}
- });
+ });*/
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
diff --git a/ring-android/app/src/main/java/cx/ring/adapters/ContactsAdapter.java b/ring-android/app/src/main/java/cx/ring/adapters/ContactsAdapter.java
index de47d94..1754589 100644
--- a/ring-android/app/src/main/java/cx/ring/adapters/ContactsAdapter.java
+++ b/ring-android/app/src/main/java/cx/ring/adapters/ContactsAdapter.java
@@ -33,28 +33,30 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import cx.ring.R;
import cx.ring.fragments.ContactListFragment;
import cx.ring.model.CallContact;
-import cx.ring.views.stickylistheaders.StickyListHeadersAdapter;
+import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.util.LruCache;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.SectionIndexer;
import android.widget.TextView;
-import android.widget.Toast;
public class ContactsAdapter extends BaseAdapter implements StickyListHeadersAdapter, SectionIndexer {
-
private ExecutorService infos_fetcher = Executors.newCachedThreadPool();
Context mContext;
@@ -64,30 +66,41 @@
WeakReference<ContactListFragment> parent;
private LayoutInflater mInflater;
- // private static final String TAG = ContactsAdapter.class.getSimpleName();
+ final private LruCache<Long, Bitmap> mMemoryCache;
+ final private HashMap<Long, WeakReference<ContactPictureTask>> running_tasks = new HashMap<>();
+
+ private static final String TAG = ContactsAdapter.class.getSimpleName();
public ContactsAdapter(ContactListFragment contactListFragment) {
super();
mContext = contactListFragment.getActivity();
mInflater = LayoutInflater.from(mContext);
- parent = new WeakReference<ContactListFragment>(contactListFragment);
- mContacts = new ArrayList<CallContact>();
+ parent = new WeakReference<>(contactListFragment);
+ mContacts = new ArrayList<>();
mSectionIndices = getSectionIndices();
mSectionLetters = getSectionLetters();
+ final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
+ final int cacheSize = maxMemory / 8;
+ mMemoryCache = new LruCache<Long, Bitmap>(cacheSize){
+ @Override
+ protected int sizeOf(Long key, Bitmap bitmap) {
+ return bitmap.getByteCount() / 1024;
+ }
+ };
}
public static final int TYPE_HEADER = 0;
public static final int TYPE_CONTACT = 1;
private int[] getSectionIndices() {
- ArrayList<Integer> sectionIndices = new ArrayList<Integer>();
+ ArrayList<Integer> sectionIndices = new ArrayList<>();
if (mContacts.isEmpty())
return new int[0];
- char lastFirstChar = mContacts.get(0).getmDisplayName().charAt(0);
+ char lastFirstChar = mContacts.get(0).getDisplayName().charAt(0);
sectionIndices.add(0);
for (int i = 1; i < mContacts.size(); i++) {
- if (mContacts.get(i).getmDisplayName().charAt(0) != lastFirstChar) {
- lastFirstChar = mContacts.get(i).getmDisplayName().charAt(0);
+ if (mContacts.get(i).getDisplayName().charAt(0) != lastFirstChar) {
+ lastFirstChar = mContacts.get(i).getDisplayName().charAt(0);
sectionIndices.add(i);
}
}
@@ -101,26 +114,27 @@
private Character[] getSectionLetters() {
Character[] letters = new Character[mSectionIndices.length];
for (int i = 0; i < mSectionIndices.length; i++) {
- letters[i] = mContacts.get(mSectionIndices[i]).getmDisplayName().charAt(0);
+ letters[i] = mContacts.get(mSectionIndices[i]).getDisplayName().charAt(0);
}
return letters;
}
@Override
- public View getView(int position, View convertView, ViewGroup root) {
+ public View getView(final int position, View convertView, ViewGroup root) {
ContactView entryView;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_contact, null);
entryView = new ContactView();
- entryView.quick_starred = (ImageButton) convertView.findViewById(R.id.quick_starred);
+ /*entryView.quick_starred = (ImageButton) convertView.findViewById(R.id.quick_starred);
entryView.quick_edit = (ImageButton) convertView.findViewById(R.id.quick_edit);
entryView.quick_discard = (ImageButton) convertView.findViewById(R.id.quick_discard);
entryView.quick_call = (ImageButton) convertView.findViewById(R.id.quick_call);
- entryView.quick_msg = (ImageButton) convertView.findViewById(R.id.quick_message);
+ entryView.quick_msg = (ImageButton) convertView.findViewById(R.id.quick_message);*/
entryView.photo = (ImageView) convertView.findViewById(R.id.photo);
entryView.display_name = (TextView) convertView.findViewById(R.id.display_name);
+ entryView.position = -1;
convertView.setTag(entryView);
} else {
entryView = (ContactView) convertView.getTag();
@@ -128,14 +142,56 @@
final CallContact item = mContacts.get(position);
- entryView.display_name.setText(item.getmDisplayName());
+ if (entryView.position == position || (entryView.contact != null && entryView.contact.get() != null && item.getId() == entryView.contact.get().getId()))
+ return convertView;
- if (item.hasPhoto()) {
- entryView.photo.setImageBitmap(item.getPhoto());
- } else {
- infos_fetcher.execute(new ContactPictureTask(mContext, entryView.photo, item));
+ entryView.display_name.setText(item.getDisplayName());
+ entryView.contact = new WeakReference<>(item);
+ entryView.position = position;
+ final Long cid = item.getId();
+ final Long pid = item.getPhotoId();
+ Bitmap bmp = item.getPhoto();
+ if (bmp == null) {
+ bmp = mMemoryCache.get(pid);
+ if (bmp != null) item.setPhoto(bmp);
}
+ if (bmp != null) {
+ entryView.photo.setImageBitmap(bmp);
+ } else {
+ entryView.photo.setImageBitmap(null);
+ final WeakReference<ContactView> wh = new WeakReference<>(entryView);
+ infos_fetcher.execute(new ContactPictureTask(mContext, entryView.photo, item, new ContactPictureTask.PictureLoadedCallback() {
+ @Override
+ public void onPictureLoaded(final Bitmap bmp) {
+ mMemoryCache.put(pid, bmp);
+ final ContactView fh = wh.get();
+ if (fh == null || fh.photo.getParent() == null)
+ return;
+ if (fh.position == position)
+ fh.photo.post(new Runnable() {
+ @Override
+ public void run() {
+ final CallContact c = fh.contact.get();
+ if (c.getId() == cid) {
+ c.setPhoto(bmp);
+ fh.photo.setImageBitmap(bmp);
+ fh.photo.startAnimation(AnimationUtils.loadAnimation(fh.photo.getContext(), R.anim.contact_fadein));
+ }
+ }
+ });
+ }
+ }));
+ }
+
+ convertView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ parent.get().mCallbacks.onTextContact(item);
+ }
+ });
+
+/*
entryView.quick_call.setOnClickListener(new OnClickListener() {
@Override
@@ -182,7 +238,7 @@
entryView.quick_edit.setClickable(false);
entryView.quick_discard.setClickable(false);
entryView.quick_starred.setClickable(false);
-
+*/
return convertView;
}
@@ -190,9 +246,11 @@
* ViewHolder Pattern
*********************/
public class ContactView {
- ImageButton quick_starred, quick_edit, quick_discard, quick_call, quick_msg;
+ ImageButton /*quick_starred, quick_edit, quick_discard, */quick_call, quick_msg;
ImageView photo;
TextView display_name;
+ WeakReference<CallContact> contact = new WeakReference<>(null);
+ int position;
}
@Override
@@ -224,7 +282,7 @@
}
// set header text as first char in name
- char headerChar = mContacts.get(position).getmDisplayName().subSequence(0, 1).charAt(0);
+ char headerChar = mContacts.get(position).getDisplayName().subSequence(0, 1).charAt(0);
holder.text.setText("" + headerChar);
@@ -240,7 +298,7 @@
public long getHeaderId(int position) {
// return the first character of the name as ID because this is what
// headers are based upon
- return mContacts.get(position).getmDisplayName().subSequence(0, 1).charAt(0);
+ return mContacts.get(position).getDisplayName().subSequence(0, 1).charAt(0);
}
@Override
@@ -274,14 +332,14 @@
}
public void clear() {
- mContacts = new ArrayList<CallContact>();
+ mContacts = new ArrayList<>();
mSectionIndices = new int[0];
mSectionLetters = new Character[0];
notifyDataSetChanged();
}
-
+/*
public void restore() {
- mContacts = new ArrayList<CallContact>();
+ mContacts = new ArrayList<>();
mSectionIndices = getSectionIndices();
mSectionLetters = getSectionLetters();
notifyDataSetChanged();
@@ -292,6 +350,13 @@
mSectionIndices = getSectionIndices();
mSectionLetters = getSectionLetters();
notifyDataSetChanged();
+ }*/
+
+ public void setData(ArrayList<CallContact> contacts) {
+ mContacts = contacts;
+ mSectionIndices = getSectionIndices();
+ mSectionLetters = getSectionLetters();
+ notifyDataSetChanged();
}
}
diff --git a/ring-android/app/src/main/java/cx/ring/adapters/DiscussArrayAdapter.java b/ring-android/app/src/main/java/cx/ring/adapters/DiscussArrayAdapter.java
deleted file mode 100644
index 9cdfc41..0000000
--- a/ring-android/app/src/main/java/cx/ring/adapters/DiscussArrayAdapter.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2004-2014 Savoir-Faire Linux Inc.
- *
- * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Additional permission under GNU GPL version 3 section 7:
- *
- * If you modify this program, or any covered work, by linking or
- * combining it with the OpenSSL project's OpenSSL library (or a
- * modified version of that library), containing parts covered by the
- * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
- * grants you additional permission to convey the resulting work.
- * Corresponding Source for a non-source form of such a combination
- * shall include the source code for the parts of OpenSSL used as well
- * as that of the covered work.
- */
-
-package cx.ring.adapters;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import cx.ring.R;
-import cx.ring.model.SipMessage;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-public class DiscussArrayAdapter extends BaseAdapter {
-
- private TextView countryName;
- private List<SipMessage> messages = new ArrayList<SipMessage>();
- private LinearLayout wrapper;
- private Context mContext;
-
- public DiscussArrayAdapter(Context context, Bundle args) {
- mContext = context;
-
- if(args == null)
- messages = new ArrayList<SipMessage>();
- else
- messages = args.getParcelableArrayList("messages");
-
- }
-
- public void add(SipMessage object) {
- messages.add(object);
- notifyDataSetChanged();
- }
-
- public int getCount() {
- return this.messages.size();
- }
-
- public SipMessage getItem(int index) {
- return this.messages.get(index);
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- View row = convertView;
- if (row == null) {
- LayoutInflater inflater = LayoutInflater.from(mContext);
- row = inflater.inflate(R.layout.item_message, parent, false);
- }
-
- wrapper = (LinearLayout) row.findViewById(R.id.wrapper);
-
- SipMessage coment = getItem(position);
-
- countryName = (TextView) row.findViewById(R.id.comment);
-
- countryName.setText(coment.comment);
-
- countryName.setBackgroundResource(coment.left ? R.drawable.bubble_left_selector : R.drawable.bubble_right_selector);
- wrapper.setGravity(coment.left ? Gravity.LEFT : Gravity.RIGHT);
-
- return row;
- }
-
- @Override
- public long getItemId(int position) {
- return 0;
- }
-
-}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/adapters/SectionsPagerAdapter.java b/ring-android/app/src/main/java/cx/ring/adapters/SectionsPagerAdapter.java
index b9b2d13..1f3be00 100644
--- a/ring-android/app/src/main/java/cx/ring/adapters/SectionsPagerAdapter.java
+++ b/ring-android/app/src/main/java/cx/ring/adapters/SectionsPagerAdapter.java
@@ -50,12 +50,11 @@
private static final String TAG = SectionsPagerAdapter.class.getSimpleName();
Context mContext;
- ArrayList<Fragment> fragments;
+ private final ArrayList<Fragment> fragments = new ArrayList<>();
public SectionsPagerAdapter(Context c, FragmentManager fm) {
super(fm);
mContext = c;
- fragments = new ArrayList<Fragment>();
fragments.add(new DialingFragment());
fragments.add(new CallListFragment());
fragments.add(new HistoryFragment());
diff --git a/ring-android/app/src/main/java/cx/ring/adapters/StarredContactsAdapter.java b/ring-android/app/src/main/java/cx/ring/adapters/StarredContactsAdapter.java
index 6690341..d2a4c1d 100644
--- a/ring-android/app/src/main/java/cx/ring/adapters/StarredContactsAdapter.java
+++ b/ring-android/app/src/main/java/cx/ring/adapters/StarredContactsAdapter.java
@@ -70,6 +70,11 @@
notifyDataSetChanged();
}
+ public void setData(ArrayList<CallContact> contacts) {
+ dataset = contacts;
+ notifyDataSetChanged();
+ }
+
@Override
public int getCount() {
return dataset.size();
@@ -97,7 +102,7 @@
CallContact item = dataset.get(pos);
- ((TextView) v.findViewById(R.id.display_name)).setText(item.getmDisplayName());
+ ((TextView) v.findViewById(R.id.display_name)).setText(item.getDisplayName());
ImageView photo_view = (ImageView) v.findViewById(R.id.photo);
if(item.hasPhoto()){
diff --git a/ring-android/app/src/main/java/cx/ring/client/AccountEditionActivity.java b/ring-android/app/src/main/java/cx/ring/client/AccountEditionActivity.java
index 8026bb8..bd93dd1 100644
--- a/ring-android/app/src/main/java/cx/ring/client/AccountEditionActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/client/AccountEditionActivity.java
@@ -37,6 +37,7 @@
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.*;
+import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -49,26 +50,33 @@
import cx.ring.R;
import cx.ring.fragments.AdvancedAccountFragment;
import cx.ring.fragments.AudioManagementFragment;
+import cx.ring.fragments.HomeFragment;
+import cx.ring.fragments.MenuFragment;
import cx.ring.fragments.NestedSettingsFragment;
import cx.ring.fragments.SecurityAccountFragment;
import cx.ring.model.account.Account;
import cx.ring.service.ISipService;
+import cx.ring.service.LocalService;
import cx.ring.service.SipService;
import com.astuetz.PagerSlidingTabStrip;
import java.util.ArrayList;
import java.util.Locale;
+import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import cx.ring.fragments.GeneralAccountFragment;
-public class AccountEditionActivity extends Activity implements GeneralAccountFragment.Callbacks, AudioManagementFragment.Callbacks,
+public class AccountEditionActivity extends Activity implements LocalService.Callbacks, GeneralAccountFragment.Callbacks, AudioManagementFragment.Callbacks,
AdvancedAccountFragment.Callbacks, SecurityAccountFragment.Callbacks, NestedSettingsFragment.Callbacks {
private static final String TAG = AccountEditionActivity.class.getSimpleName();
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(LocalService.AUTHORITY_URI, "accounts");
+
private boolean mBound = false;
- private ISipService service;
- private Account acc_selected;
+ private LocalService service;
+
+ private Account acc_selected = null;
private NestedSettingsFragment toDisplay;
@@ -81,24 +89,35 @@
};
PreferencesPagerAdapter mPreferencesPagerAdapter;
+
private ServiceConnection mConnection = new ServiceConnection() {
@Override
- public void onServiceConnected(ComponentName className, IBinder binder) {
- service = ISipService.Stub.asInterface(binder);
+ public void onServiceConnected(ComponentName className, IBinder s) {
+ LocalService.LocalBinder binder = (LocalService.LocalBinder) s;
+ service = binder.getService();
mBound = true;
- ArrayList<Fragment> fragments = new ArrayList<Fragment>();
+ setContentView(R.layout.activity_account_settings);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ String account_id = getIntent().getData().getLastPathSegment();
+ Log.i(TAG, "Service connected " + className.getClassName() + " " + getIntent().getData().toString());
+
+ acc_selected = service.getAccount(account_id);
+ acc_selected.addObserver(mAccountObserver);
+ getActionBar().setTitle(acc_selected.getAlias());
+
+ ArrayList<Fragment> fragments = new ArrayList<>();
if (acc_selected.isIP2IP()) {
fragments.add(new AudioManagementFragment());
} else {
fragments.add(new GeneralAccountFragment());
fragments.add(new AudioManagementFragment());
- if(acc_selected.isSip())
- {
- fragments.add(new AdvancedAccountFragment());
- fragments.add(new SecurityAccountFragment());
- }
+ if(acc_selected.isSip())
+ {
+ fragments.add(new AdvancedAccountFragment());
+ fragments.add(new SecurityAccountFragment());
+ }
}
ViewPager mViewPager = (ViewPager) findViewById(R.id.pager);
@@ -110,33 +129,22 @@
final PagerSlidingTabStrip strip = PagerSlidingTabStrip.class.cast(findViewById(R.id.pager_sliding_strip));
strip.setViewPager(mViewPager);
-
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
-
+ acc_selected.deleteObserver(mAccountObserver);
+ mBound = false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- setContentView(R.layout.activity_account_settings);
-
- getActionBar().setDisplayHomeAsUpEnabled(true);
-
- acc_selected = getIntent().getExtras().getParcelable("account");
-
- acc_selected.addObserver(mAccountObserver);
-
if (!mBound) {
- Log.i(TAG, "onCreate: Binding service...");
- Intent intent = new Intent(this, SipService.class);
+ Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
-
}
@Override
@@ -206,8 +214,12 @@
private void processAccount() {
try {
- service.setCredentials(acc_selected.getAccountID(), acc_selected.getCredentialsHashMapList());
- service.setAccountDetails(acc_selected.getAccountID(), acc_selected.getDetails());
+ service.getRemoteService().setCredentials(acc_selected.getAccountID(), acc_selected.getCredentialsHashMapList());
+ Map<String, String> details = acc_selected.getDetails();
+ service.getRemoteService().setAccountDetails(acc_selected.getAccountID(), details);
+ Log.w(TAG, "service.setAccountDetails " + details.get("Account.hostname"));
+ getActionBar().setTitle(acc_selected.getAlias());;
+
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -225,7 +237,7 @@
bundle.putString("AccountID", acc_selected.getAccountID());
try {
- service.removeAccount(acc_selected.getAccountID());
+ service.getRemoteService().removeAccount(acc_selected.getAccountID());
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -244,6 +256,16 @@
return alertDialog;
}
+ @Override
+ public ISipService getRemoteService() {
+ return service.getRemoteService();
+ }
+
+ @Override
+ public LocalService getService() {
+ return service;
+ }
+
public class PreferencesPagerAdapter extends FragmentStatePagerAdapter {
Context mContext;
@@ -290,11 +312,6 @@
}
@Override
- public ISipService getService() {
- return service;
- }
-
- @Override
public Account getAccount() {
return acc_selected;
}
diff --git a/ring-android/app/src/main/java/cx/ring/client/CallActivity.java b/ring-android/app/src/main/java/cx/ring/client/CallActivity.java
index 32682b7..c481bca 100644
--- a/ring-android/app/src/main/java/cx/ring/client/CallActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/client/CallActivity.java
@@ -33,28 +33,25 @@
package cx.ring.client;
-import java.util.*;
-
import android.app.Activity;
import android.util.Log;
import cx.ring.R;
import cx.ring.fragments.CallFragment;
-import cx.ring.fragments.IMFragment;
+import cx.ring.model.Conversation;
+import cx.ring.model.TextMessage;
import cx.ring.model.account.Account;
import cx.ring.model.CallContact;
import cx.ring.model.Conference;
import cx.ring.model.SipCall;
-import cx.ring.model.SipMessage;
+import cx.ring.model.account.AccountDetailBasic;
import cx.ring.service.ISipService;
-import cx.ring.service.SipService;
+import cx.ring.service.LocalService;
import cx.ring.utils.CallProximityManager;
-import cx.ring.views.CallPaneLayout;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.graphics.Color;
import android.graphics.PixelFormat;
import android.net.Uri;
import android.os.Bundle;
@@ -62,88 +59,37 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.support.v4.widget.SlidingPaneLayout;
import android.view.KeyEvent;
-import android.view.View;
import android.view.Window;
import android.view.WindowManager;
-public class CallActivity extends Activity implements IMFragment.Callbacks, CallFragment.Callbacks, CallProximityManager.ProximityDirector {
-
+public class CallActivity extends Activity implements LocalService.Callbacks, CallFragment.Callbacks, CallProximityManager.ProximityDirector {
@SuppressWarnings("unused")
static final String TAG = "CallActivity";
- private ISipService mService;
- CallPaneLayout mSlidingPaneLayout;
+ private boolean init = false;
+ private LocalService service;
- IMFragment mIMFragment;
CallFragment mCurrentCallFragment;
private Conference mDisplayedConference;
/* result code sent in case of call failure */
public static int RESULT_FAILURE = -10;
- private CallProximityManager mProximityManager;
+ private CallProximityManager mProximityManager = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
+ Log.i(TAG, "CallActivity onCreate");
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_call_layout);
Window w = getWindow();
w.setFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED, WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
- setUpSlidingPanel();
-
- mProximityManager = new CallProximityManager(this, this);
- mProximityManager.startTracking();
-
- mCurrentCallFragment = new CallFragment();
- mIMFragment = new IMFragment();
-
- if(!checkExternalCall()) {
- mDisplayedConference = getIntent().getParcelableExtra("conference");
- Bundle IMBundle = new Bundle();
- if (getIntent().getBooleanExtra("resuming", false)) {
- IMBundle.putParcelableArrayList("messages", mDisplayedConference.getMessages());
- mIMFragment.setArguments(IMBundle);
- } else {
- IMBundle.putParcelableArrayList("messages", new ArrayList<SipMessage>());
- mIMFragment.setArguments(IMBundle);
- }
- }
-
- mSlidingPaneLayout.setCurFragment(mCurrentCallFragment);
- getFragmentManager().beginTransaction().replace(R.id.ongoingcall_pane, mCurrentCallFragment)
- .replace(R.id.message_list_frame, mIMFragment).commit();
- }
-
- private void setUpSlidingPanel() {
- mSlidingPaneLayout = (CallPaneLayout) findViewById(R.id.slidingpanelayout);
- mSlidingPaneLayout.setParallaxDistance(500);
- mSlidingPaneLayout.setSliderFadeColor(Color.TRANSPARENT);
-
- mSlidingPaneLayout.setPanelSlideListener(new SlidingPaneLayout.PanelSlideListener() {
-
- @Override
- public void onPanelSlide(View view, float offSet) {
- }
-
- @Override
- public void onPanelOpened(View view) {
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
- }
-
- @Override
- public void onPanelClosed(View view) {
- mCurrentCallFragment.getBubbleView().restartDrawing();
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
- }
- });
+ Intent intent = new Intent(this, LocalService.class);
+ bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
public void onFragmentCreated() {
- Intent intent = new Intent(this, SipService.class);
- bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
@@ -182,11 +128,12 @@
@Override
protected void onDestroy() {
-
+ Log.i(TAG, "CallActivity onDestroy");
unbindService(mConnection);
-
- mProximityManager.stopTracking();
- mProximityManager.release(0);
+ if (mProximityManager != null) {
+ mProximityManager.stopTracking();
+ mProximityManager.release(0);
+ }
super.onDestroy();
}
@@ -198,15 +145,34 @@
@SuppressWarnings("unchecked")
@Override
public void onServiceConnected(ComponentName className, IBinder binder) {
- mService = ISipService.Stub.asInterface(binder);
+ service = ((LocalService.LocalBinder)binder).getService();
+
+ if (!init) {
+ mProximityManager = new CallProximityManager(CallActivity.this, CallActivity.this);
+ mProximityManager.startTracking();
+
+ if(!checkExternalCall()) {
+ mDisplayedConference = getIntent().getParcelableExtra("conference");
+ }
+ Log.i(TAG, "CallActivity onCreate in:" + mDisplayedConference.isIncoming() + " out:" + mDisplayedConference.isOnGoing() + " contact" + mDisplayedConference.getParticipants().get(0).getContact().getDisplayName());
+ init = true;
+ }
if (mDisplayedConference.getState().contentEquals("NONE")) {
+ SipCall call = mDisplayedConference.getParticipants().get(0);
try {
- mService.placeCall(mDisplayedConference.getParticipants().get(0));
+ String callId = service.getRemoteService().placeCall(call);
+ if (callId == null || callId.isEmpty()) {
+ CallActivity.this.terminateCall();
+ }
+ mDisplayedConference = service.getRemoteService().getConference(callId);
} catch (RemoteException e) {
e.printStackTrace();
}
}
+
+ setContentView(R.layout.activity_call_layout);
+ mCurrentCallFragment = (CallFragment) getFragmentManager().findFragmentById(R.id.ongoingcall_pane);
}
@Override
@@ -217,25 +183,30 @@
private boolean checkExternalCall() {
Uri u = getIntent().getData();
if (u != null) {
- CallContact c = CallContact.ContactBuilder.buildUnknownContact(u.getSchemeSpecificPart());
+ String number = u.getSchemeSpecificPart();
+ Log.w(TAG, "number " + number);
+ number = CallContact.canonicalNumber(number);
+ Log.w(TAG, "canonicalNumber " + number);
+ CallContact c = service.findContactByNumber(number);
+ Conversation conv = service.getByContact(c);
+ if (conv == null)
+ conv = new Conversation(c);
+ Account acc = service.getAccounts().get(0);
+ String id = conv.getLastAccountUsed();
+ if (id != null && !id.isEmpty()) {
+ Account alt_acc = service.getAccount(id);
+ Log.w(TAG, "Found suitable account for calling " + u.getSchemeSpecificPart() + " " + id + " " + alt_acc.getBasicDetails().getDetailString(AccountDetailBasic.CONFIG_ACCOUNT_TYPE));
+ if (alt_acc.isEnabled())
+ acc = alt_acc;
+ } else {
+ acc = service.guessAccount(c, number);
+ }
try {
- String accountID = (String) mService.getAccountList().get(1); // We use the first account to place outgoing calls
- Map<String, String> details = (Map<String, String>) mService.getAccountDetails(accountID);
- ArrayList<Map<String, String>> credentials = (ArrayList<Map<String, String>>) mService.getCredentials(accountID);
- Map<String, String> state = (Map<String, String>) mService.getVolatileAccountDetails(accountID);
- Account acc = new Account(accountID, details, credentials, state);
-
- Bundle args = new Bundle();
- args.putString(SipCall.ID, Integer.toString(Math.abs(new Random().nextInt())));
- args.putParcelable(SipCall.ACCOUNT, acc);
- args.putInt(SipCall.STATE, SipCall.state.CALL_STATE_NONE);
- args.putInt(SipCall.TYPE, SipCall.direction.CALL_TYPE_OUTGOING);
- args.putParcelable(SipCall.CONTACT, c);
-
+ SipCall call = new SipCall(null, acc.getAccountID(), number, SipCall.Direction.OUTGOING);
+ call.setCallState(SipCall.State.NONE);
+ call.setContact(c);
mDisplayedConference = new Conference(Conference.DEFAULT_ID);
- mDisplayedConference.getParticipants().add(new SipCall(args));
- } catch (RemoteException e) {
- e.printStackTrace();
+ mDisplayedConference.getParticipants().add(call);
} catch (Exception e) {
e.printStackTrace();
}
@@ -245,8 +216,13 @@
}
@Override
- public ISipService getService() {
- return mService;
+ public ISipService getRemoteService() {
+ return service.getRemoteService();
+ }
+
+ @Override
+ public LocalService getService() {
+ return service;
}
@Override
@@ -273,30 +249,7 @@
@Override
public void terminateCall() {
mHandler.removeCallbacks(mUpdateTimeTask);
- mCurrentCallFragment.getBubbleView().stopThread();
- TimerTask quit = new TimerTask() {
-
- @Override
- public void run() {
- finish();
- }
- };
-
- new Timer().schedule(quit, 1000);
- }
-
- @Override
- public boolean sendIM(SipMessage msg) {
-
- try {
- Log.i(TAG, "Sending:"+msg.comment+"to"+mDisplayedConference.getId());
- mService.sendTextMessage(mDisplayedConference.getId(), msg);
- } catch (RemoteException e) {
- e.printStackTrace();
- return false;
- }
-
- return true;
+ finish();
}
@Override
@@ -305,24 +258,11 @@
}
@Override
- public void slideChatScreen() {
-
- if (mSlidingPaneLayout.isOpen()) {
- mSlidingPaneLayout.closePane();
- } else {
- mCurrentCallFragment.getBubbleView().stopThread();
- mSlidingPaneLayout.openPane();
- }
- }
-
- @Override
public boolean shouldActivateProximity() {
return true;
}
@Override
public void onProximityTrackingChanged(boolean acquired) {
- // TODO Stub de la méthode généré automatiquement
-
}
}
diff --git a/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java b/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java
new file mode 100644
index 0000000..a0ce375
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java
@@ -0,0 +1,386 @@
+package cx.ring.client;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import cx.ring.R;
+import cx.ring.adapters.ContactPictureTask;
+import cx.ring.model.CallContact;
+import cx.ring.model.Conference;
+import cx.ring.model.Conversation;
+import cx.ring.model.SipCall;
+import cx.ring.model.TextMessage;
+import cx.ring.model.account.Account;
+import cx.ring.service.LocalService;
+
+public class ConversationActivity extends Activity {
+ private static final String TAG = ConversationActivity.class.getSimpleName();
+
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(LocalService.AUTHORITY_URI, "conversations");
+
+ private boolean mBound = false;
+ private LocalService service = null;
+ private Conversation conversation = null;
+ private String preferredNumber = null;
+
+
+ private ListView histList = null;
+ private View msgSendBtn = null;
+ private EditText msgEditTxt = null;
+ private ViewGroup bottomPane = null;
+
+ private ConversationAdapter adapter = null;
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder binder) {
+ service = ((LocalService.LocalBinder)binder).getService();
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(LocalService.ACTION_CONF_UPDATE);
+ registerReceiver(receiver, intentFilter);
+
+ mBound = true;
+
+ String conv_id = getIntent().getData().getLastPathSegment();
+ preferredNumber = getIntent().getStringExtra("number");
+ conversation = service.getConversation(conv_id);
+ if (conversation == null) {
+ long contact_id = CallContact.contactIdFromId(conv_id);
+ CallContact contact;
+ if (contact_id >= 0)
+ contact = service.findContactById(contact_id);
+ else if (preferredNumber != null && !preferredNumber.isEmpty()) {
+ contact = service.findContactByNumber(preferredNumber);
+ if (contact == null)
+ contact = CallContact.ContactBuilder.buildUnknownContact(conv_id);
+ } else {
+ contact = service.findContactByNumber(conv_id);
+ if (contact == null)
+ contact = CallContact.ContactBuilder.buildUnknownContact(conv_id);
+ preferredNumber = conv_id;
+ }
+ conversation = service.startConversation(contact);
+ }
+
+ Log.w(TAG, "ConversationActivity onServiceConnected " + conv_id);
+
+ if (conversation == null) {
+ finish();
+ return;
+ }
+
+ getActionBar().setTitle(conversation.getContact().getDisplayName());
+
+ Conference conf = conversation.getCurrentCall();
+ bottomPane.setVisibility(conf == null ? View.GONE : View.VISIBLE);
+ if (conf != null) {
+ Log.w(TAG, "ConversationActivity onServiceConnected " + conf.getId() + " " + conversation.getCurrentCall());
+ bottomPane.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startActivity(new Intent(ConversationActivity.this.getApplicationContext(), CallActivity.class).putExtra("conference", conversation.getCurrentCall()));
+ }
+ });
+ }
+
+ adapter.updateDataset(conversation.getHistory());
+ scrolltoBottom();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ Log.w(TAG, "ConversationActivity onServiceDisconnected " + arg0.getClassName());
+ mBound = false;
+ }
+ };
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.w(TAG, "onReceive " + intent.getAction() + " " + intent.getDataString());
+ //conversation = service.getConversation(conversation.getId());
+ conversation = service.getByContact(conversation.getContact());
+ adapter.updateDataset(conversation.getHistory());
+ scrolltoBottom();
+ Conference conf = conversation.getCurrentCall();
+ bottomPane.setVisibility(conf == null ? View.GONE : View.VISIBLE);
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.frag_conversation);
+ msgEditTxt = (EditText) findViewById(R.id.msg_input_txt);
+ msgSendBtn = findViewById(R.id.msg_send);
+ msgSendBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onSendTextMessage(msgEditTxt.getText().toString());
+ msgEditTxt.setText("");
+ }
+ });
+ bottomPane = (ViewGroup) findViewById(R.id.ongoingcall_pane);
+ bottomPane.setVisibility(View.GONE);
+ //getActionBar().setDisplayHomeAsUpEnabled(true);
+ conversation = getIntent().getParcelableExtra("conversation");
+
+ adapter = new ConversationAdapter(this);
+ histList = (ListView) findViewById(R.id.hist_list);
+ histList.setAdapter(adapter);
+
+ if (!mBound) {
+ Log.i(TAG, "onCreate: Binding service...");
+ Intent intent = new Intent(this, LocalService.class);
+ bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+ service = null;
+ }
+ }
+
+ private void scrolltoBottom() {
+ histList.post(new Runnable() {
+ @Override
+ public void run() {
+ // Select the last row so it will scroll into view...
+ histList.setSelection(adapter.getCount() - 1);
+ }
+ });
+ }
+
+ private class ConversationAdapter extends BaseAdapter {
+ final private Context context;
+ final private ArrayList<Conversation.ConversationElement> texts = new ArrayList<>();
+ private ExecutorService infos_fetcher = Executors.newCachedThreadPool();
+
+ public void updateDataset(ArrayList<Conversation.ConversationElement> list) {
+ Log.i(TAG, "updateDataset " + list.size());
+ if (list.size() == 0 && texts.size() == 0)
+ return;
+ texts.clear();
+ texts.addAll(list);
+ notifyDataSetChanged();
+ }
+
+ ConversationAdapter(Context ctx) {
+ context = ctx;
+ }
+
+ @Override
+ public int getCount() {
+ return texts.size();
+ }
+
+ @Override
+ public Conversation.ConversationElement getItem(int position) {
+ return texts.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null)
+ convertView = LayoutInflater.from(context).inflate(R.layout.item_textmsg, null);
+
+ ViewGroup txtEntry = (ViewGroup) convertView.findViewById(R.id.txt_entry);
+ TextView msgTxt = (TextView) convertView.findViewById(R.id.msg_txt);
+ TextView msgDetailTxt = (TextView) convertView.findViewById(R.id.msg_details_txt);
+ ImageView photo = (ImageView) convertView.findViewById(R.id.photo);
+
+ ViewGroup txtEntryRight = (ViewGroup) convertView.findViewById(R.id.txt_entry_right);
+ TextView msgTxtRight = (TextView) convertView.findViewById(R.id.msg_txt_right);
+ TextView msgDetailTxtRight = (TextView) convertView.findViewById(R.id.msg_details_txt_right);
+
+ ViewGroup callEntry = (ViewGroup) convertView.findViewById(R.id.call_entry);
+ TextView histTxt = (TextView) convertView.findViewById(R.id.call_hist_txt);
+ TextView histDetailTxt = (TextView) convertView.findViewById(R.id.call_details_txt);
+
+ Conversation.ConversationElement txt = texts.get(position);
+ if (txt.text != null) {
+ callEntry.setVisibility(View.GONE);
+ if (txt.text.isIncoming()) {
+ txtEntry.setVisibility(View.VISIBLE);
+ txtEntryRight.setVisibility(View.GONE);
+ msgTxt.setText(txt.text.getMessage());
+ msgDetailTxt.setText(DateFormat.getDateTimeInstance().format(new Date(txt.text.getTimestamp())));
+ infos_fetcher.execute(new ContactPictureTask(context, photo, txt.text.getContact()));
+ } else {
+ txtEntry.setVisibility(View.GONE);
+ txtEntryRight.setVisibility(View.VISIBLE);
+ msgTxtRight.setText(txt.text.getMessage());
+ msgDetailTxtRight.setText(DateFormat.getDateTimeInstance().format(new Date(txt.text.getTimestamp())));
+ }
+ } else {
+ callEntry.setVisibility(View.VISIBLE);
+ txtEntry.setVisibility(View.GONE);
+ txtEntryRight.setVisibility(View.GONE);
+ msgTxt.setText("");
+ histTxt.setText((txt.call.isIncoming() ? "Incoming" : "Outgoing") + " call with " + txt.call.getNumber());
+ histDetailTxt.setText(DateFormat.getDateTimeInstance().format(txt.call.getStartDate()));
+ }
+
+ return convertView;
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mBound) {
+ unregisterReceiver(receiver);
+ unbindService(mConnection);
+ mBound = false;
+ }
+ super.onDestroy();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu items for use in the action bar
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.conversation_actions, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle presses on the action bar items
+ switch (item.getItemId()) {
+ case R.id.conv_action_audiocall:
+ onAudioCall();
+ return true;
+ case R.id.conv_action_videocall:
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ public void launchCallActivity(SipCall infos) {
+ Conference tmp = conversation.getCurrentCall();
+ if (tmp == null)
+ //tmp = service.startConversation(infos.getContact());
+ tmp = new Conference(Conference.DEFAULT_ID);
+
+ tmp.getParticipants().add(infos);
+ Intent intent = new Intent().setClass(this, CallActivity.class);
+ intent.putExtra("conference", tmp);
+ intent.putExtra("resuming", false);
+ startActivityForResult(intent, HomeActivity.REQUEST_CODE_CALL);
+ // overridePendingTransition(R.anim.slide_down, R.anim.slide_up);
+ }
+
+ private void onSendTextMessage(String txt) {
+
+ Conference conf = conversation.getCurrentCall();
+ if (conf == null || !conf.isOnGoing()) {
+ String account = conversation.getLastAccountUsed();
+ if (account == null || account.isEmpty())
+ account = service.guessAccount(conversation.getContact(), conversation.contact.getPhones().get(0).getNumber()).getAccountID();
+ String number = preferredNumber;
+ if (number == null || number.isEmpty())
+ number = conversation.getLastNumberUsed(account);
+ if (number == null || number.isEmpty())
+ number = conversation.contact.getPhones().get(0).getNumber();
+ try {
+ service.getRemoteService().sendAccountTextMessage(account, number, txt);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ } else {
+ try {
+ service.getRemoteService().sendTextMessage(conf.getId(), new TextMessage(false, txt));
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void onAudioCall() {
+ Conference conf = conversation.getCurrentCall();
+ if (conf != null) {
+ startActivity(new Intent(ConversationActivity.this.getApplicationContext(), CallActivity.class).putExtra("conference", conversation.getCurrentCall()));
+ return;
+ }
+
+ if (service.getAccounts().isEmpty()) {
+ //createNotRegisteredDialog().show();
+ return;
+ }
+
+ Account usedAccount = service.getAccounts().get(0);
+ CallContact contact = null;
+ if (conversation != null) {
+ String last_used = conversation.getLastAccountUsed();
+ Account a = service.getAccount(last_used);
+ if (a != null/* && a.isEnabled()*/)
+ usedAccount = a;
+ else {
+ Set<String> acc_ids = conversation.getAccountsUsed();
+ for (Account acc : service.getAccounts()) {
+ if (acc_ids.contains(acc.getAccountID())) {
+ usedAccount = acc;
+ break;
+ }
+ }
+ }
+ contact = conversation.getContact();
+ }
+
+ String number = preferredNumber;
+ if (number == null)
+ number = conversation.getLastNumberUsed(usedAccount.getAccountID());
+ if (number == null && contact != null)
+ number = contact.getPhones().get(0).getNumber();
+
+ //conversation.getHistory().getAccountID()
+ //if (usedAccount.isRegistered() || usedAccount.isIP2IP()) {
+ /* Bundle args = new Bundle();
+ args.putParcelable(SipCall.ACCOUNT, usedAccount);
+ args.putInt(SipCall.STATE, SipCall.State.NONE);
+ args.putInt(SipCall.TYPE, SipCall.Direction.OUTGOING);
+ args.putParcelable(SipCall.CONTACT, contact);*/
+ SipCall call = new SipCall(null, usedAccount.getAccountID(), number, SipCall.Direction.OUTGOING);
+ call.setContact(contact);
+
+ try {
+ launchCallActivity(call);
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(TAG, e.toString());
+ }
+ /*} else {
+ createNotRegisteredDialog().show();
+ }*/
+
+ }
+}
diff --git a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java
index 9358e7e..52d9b37 100644
--- a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java
@@ -36,9 +36,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.Random;
import java.util.Timer;
-import java.util.TimerTask;
+import java.util.regex.Pattern;
import android.app.Activity;
import android.app.AlertDialog;
@@ -52,14 +51,13 @@
import cx.ring.fragments.HomeFragment;
import cx.ring.fragments.MenuFragment;
import cx.ring.history.HistoryEntry;
+import cx.ring.history.HistoryManager;
+import cx.ring.loaders.LoaderConstants;
import cx.ring.model.account.Account;
-import cx.ring.model.CallContact;
import cx.ring.model.Conference;
import cx.ring.model.SipCall;
import cx.ring.service.ISipService;
-import cx.ring.service.SipService;
-import cx.ring.views.SlidingUpPanelLayout;
-import cx.ring.views.SlidingUpPanelLayout.PanelSlideListener;
+import cx.ring.service.LocalService;
import android.app.Fragment;
import android.app.FragmentManager;
@@ -72,18 +70,12 @@
import android.content.ServiceConnection;
import android.content.res.AssetManager;
import android.content.res.Configuration;
-import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.SipAddress;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.NavigationView;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
@@ -91,14 +83,14 @@
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.RelativeLayout;
-import android.widget.Toast;
-public class HomeActivity extends AppCompatActivity implements DialingFragment.Callbacks, AccountsManagementFragment.Callbacks,
- ContactListFragment.Callbacks, CallListFragment.Callbacks, HistoryFragment.Callbacks, NavigationView.OnNavigationItemSelectedListener, MenuFragment.Callbacks {
+public class HomeActivity extends AppCompatActivity implements LocalService.Callbacks, DialingFragment.Callbacks,
+ /*ContactListFragment.Callbacks, */HistoryFragment.Callbacks, NavigationView.OnNavigationItemSelectedListener {
static final String TAG = HomeActivity.class.getSimpleName();
@@ -107,12 +99,13 @@
private MenuFragment fMenuHead = null;
private boolean mBound = false;
- private ISipService service;
+ private LocalService service;
public static final int REQUEST_CODE_PREFERENCES = 1;
public static final int REQUEST_CODE_CALL = 3;
+ public static final int REQUEST_CODE_CONVERSATION = 4;
- SlidingUpPanelLayout mContactDrawer;
+ //SlidingUpPanelLayout mContactDrawer;
private DrawerLayout mNavigationDrawer;
private ActionBarDrawerToggle mDrawerToggle;
private Toolbar toolbar;
@@ -123,6 +116,22 @@
protected Fragment fContent;
+ public static final Pattern RING_ID_REGEX = Pattern.compile("^\\s+(?:ring(?:[\\s\\:]+))?(\\p{XDigit}{40})\\s+$", Pattern.CASE_INSENSITIVE);
+
+ private static void setDefaultUncaughtExceptionHandler() {
+ try {
+ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ @Override
+ public void uncaughtException(Thread t, Throwable e) {
+ Log.e(TAG, "Uncaught Exception detected in thread ", e);
+ //e.printStackTrace();
+ }
+ });
+ } catch (SecurityException e) {
+ Log.e(TAG, "Could not set the Default Uncaught Exception Handler");
+ }
+ }
+
/* called before activity is killed, e.g. rotation */
@Override
protected void onSaveInstanceState(Bundle bundle) {
@@ -131,14 +140,18 @@
@Override
public void onCreate(Bundle savedInstanceState) {
+ setDefaultUncaughtExceptionHandler();
+
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
// Bind to LocalService
if (!mBound) {
Log.i(TAG, "onStart: Binding service...");
- Intent intent = new Intent(this, SipService.class);
+ /*Intent intent = new Intent(this, SipService.class);
startService(intent);
+ bindService(intent, mConnection, Context.BIND_AUTO_CREATE);*/
+ Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@@ -148,9 +161,13 @@
fMenu = (NavigationView) findViewById(R.id.left_drawer);
fMenu.setNavigationItemSelectedListener(this);
-
- mContactsFragment = new ContactListFragment();
- getFragmentManager().beginTransaction().replace(R.id.contacts_frame, mContactsFragment).commit();
+/*
+ FragmentManager fm = getFragmentManager();
+ mContactsFragment = (ContactListFragment) fm.findFragmentByTag(ContactListFragment.TAG);
+ if(mContactsFragment == null) {
+ mContactsFragment = new ContactListFragment();
+ getFragmentManager().beginTransaction().replace(R.id.contacts_frame, mContactsFragment, ContactListFragment.TAG).commit();
+ }
mContactDrawer = (SlidingUpPanelLayout) findViewById(R.id.contact_panel);
// mContactDrawer.setShadowDrawable(getResources().getDrawable(R.drawable.above_shadow));
@@ -187,7 +204,7 @@
}
});
-
+*/
mNavigationDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@@ -217,17 +234,23 @@
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
- // Sync the toggle state after onRestoreInstanceState has occurred.
+ // Sync the toggle State after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
- if (mContactDrawer.isExpanded()) {
+ /*if (mContactDrawer.isExpanded()) {
getSupportActionBar().hide();
- }
+ }*/
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- mDrawerToggle.onConfigurationChanged(newConfig);
+ //mDrawerToggle.onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.history, menu);
+ return super.onCreateOptionsMenu(menu);
}
@Override
@@ -248,7 +271,11 @@
TypedValue tv = new TypedValue();
if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
int abSz = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
- ViewGroup.LayoutParams params = toolbar.getLayoutParams();
+ ViewGroup.LayoutParams params = toolbar.getLayoutParams();//toolbar.setContentInsetsRelative();
+
+ //TypedArray a = obtainStyledAttributes(attrs, R.styleable.Toolbar_titleMarginBottom);
+
+ //toolbar.get
if (double_h) {
params.height = abSz*2;
actionButton.setVisibility(View.VISIBLE);
@@ -261,6 +288,7 @@
toolbar.setMinimumHeight(abSz);
}
toolbar.setTitle(title_res);
+ //toolbar.setTitleTextAppearance(toolbar.getT);
}
public FloatingActionButton getActionButton() {
@@ -336,10 +364,10 @@
return;
}
- if (mContactDrawer.isExpanded() || mContactDrawer.isAnchored()) {
+ /*if (mContactDrawer.isExpanded() || mContactDrawer.isAnchored()) {
mContactDrawer.collapsePane();
return;
- }
+ }*/
if (getFragmentManager().getBackStackEntryCount() > 1) {
popCustomBackStack();
@@ -347,20 +375,7 @@
return;
}
- if (isClosing) {
- super.onBackPressed();
- t.cancel();
- finish();
- } else {
- t.schedule(new TimerTask() {
- @Override
- public void run() {
- isClosing = false;
- }
- }, 3000);
- Toast.makeText(this, getResources().getString(R.string.close_msg), Toast.LENGTH_SHORT).show();
- isClosing = true;
- }
+ super.onBackPressed();
}
private void popCustomBackStack() {
@@ -376,20 +391,20 @@
@Override
protected void onPause() {
super.onPause();
-
- if (mBound) {
- unbindService(mConnection);
- mBound = false;
- }
+ Log.d(TAG, "onPause");
}
/* activity finishes itself or is being killed by the system */
@Override
protected void onDestroy() {
super.onDestroy();
- Log.i(TAG, "onDestroy: destroying service...");
- Intent sipServiceIntent = new Intent(this, SipService.class);
- stopService(sipServiceIntent);
+ if (mBound) {
+ unbindService(mConnection);
+ mBound = false;
+ }
+ //Log.i(TAG, "onDestroy: destroying service...");
+ //Intent sipServiceIntent = new Intent(this, SipService.class);
+ //stopService(sipServiceIntent);
}
public void launchCallActivity(SipCall infos) {
@@ -410,9 +425,13 @@
private ServiceConnection mConnection = new ServiceConnection() {
@Override
- public void onServiceConnected(ComponentName className, IBinder binder) {
- service = ISipService.Stub.asInterface(binder);
- fContent = new HomeFragment();
+ public void onServiceConnected(ComponentName className, IBinder s) {
+ Log.i(TAG, "onServiceConnected " + className.getClassName());
+ LocalService.LocalBinder binder = (LocalService.LocalBinder) s;
+ service = binder.getService();
+
+ //service = ISipService.Stub.asInterface(binder);
+ fContent = new CallListFragment();
if (fMenuHead != null)
fMenu.removeHeaderView(fMenuHead.getView());
fMenu.inflateHeaderView(R.layout.menuheader);
@@ -420,18 +439,19 @@
getFragmentManager().beginTransaction().replace(R.id.main_frame, fContent, "Home").addToBackStack("Home").commit();
mBound = true;
- Log.d(TAG, "Service connected service=" + service);
+ Log.i(TAG, "Service connected service=" + service);
}
@Override
- public void onServiceDisconnected(ComponentName arg0) {
+ public void onServiceDisconnected(ComponentName className) {
+ Log.i(TAG, "onServiceConnected " + className.getClassName());
if (fMenuHead != null) {
fMenu.removeHeaderView(fMenuHead.getView());
fMenuHead = null;
}
mBound = false;
- Log.d(TAG, "Service disconnected service=" + service);
+ Log.i(TAG, "Service disconnected service=" + service);
}
};
@@ -442,8 +462,17 @@
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
-
- return super.onOptionsItemSelected(item);
+ switch (item.getItemId()) {
+ case R.id.menu_clear_history:
+ // TODO clean Database!
+ //mHistoryManager.clearDB();
+ //getLoaderManager().restartLoader(LoaderConstants.HISTORY_LOADER, null, this);
+ HistoryManager m = new HistoryManager(this);
+ m.clearDB();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
}
@Override
@@ -466,10 +495,16 @@
}
@Override
- public ISipService getService() {
+ public ISipService getRemoteService() {
+ return service.getRemoteService();
+ }
+
+ @Override
+ public LocalService getService() {
return service;
}
+ /*
@Override
public void onTextContact(final CallContact c) {
// TODO
@@ -483,6 +518,7 @@
startActivity(intent);
}
+
@Override
public void onCallContact(final CallContact c) {
@@ -506,10 +542,10 @@
public void run() {
Bundle args = new Bundle();
- args.putString(SipCall.ID, Integer.toString(Math.abs(new Random().nextInt())));
+ //args.putString(SipCall.ID, Integer.toString(Math.abs(new Random().nextInt())));
args.putParcelable(SipCall.ACCOUNT, fMenuHead.getSelectedAccount());
- args.putInt(SipCall.STATE, SipCall.state.CALL_STATE_NONE);
- args.putInt(SipCall.TYPE, SipCall.direction.CALL_TYPE_OUTGOING);
+ args.putInt(SipCall.STATE, SipCall.State.NONE);
+ args.putInt(SipCall.TYPE, SipCall.Direction.OUTGOING);
Cursor cPhones = getContentResolver().query(Phone.CONTENT_URI, CONTACTS_PHONES_PROJECTION, Phone.CONTACT_ID + " =" + c.getId(),
null, null);
@@ -533,10 +569,10 @@
}
});
launcher.start();
- mContactDrawer.collapsePane();
+ //mContactDrawer.collapsePane();
}
-
+*/
@Override
public void onCallHistory(HistoryEntry to) {
@@ -549,10 +585,10 @@
if (usedAccount.isRegistered()) {
Bundle args = new Bundle();
- args.putString(SipCall.ID, Integer.toString(Math.abs(new Random().nextInt())));
+ //args.putString(SipCall.ID, Integer.toString(Math.abs(new Random().nextInt())));
args.putParcelable(SipCall.ACCOUNT, usedAccount);
- args.putInt(SipCall.STATE, SipCall.state.CALL_STATE_NONE);
- args.putInt(SipCall.TYPE, SipCall.direction.CALL_TYPE_OUTGOING);
+ args.putInt(SipCall.STATE, SipCall.State.NONE);
+ args.putInt(SipCall.TYPE, SipCall.Direction.OUTGOING);
args.putParcelable(SipCall.CONTACT, to.getContact());
try {
@@ -567,7 +603,15 @@
@Override
public void onCallDialed(String to) {
- Account usedAccount = fMenuHead.getSelectedAccount();
+ Intent intent = new Intent()
+ .setClass(this, CallActivity.class)
+ .setAction(Intent.ACTION_CALL)
+ .setData(Uri.parse(to));
+ /*intent.putExtra("conference", tmp);
+ intent.putExtra("resuming", false);*/
+ startActivityForResult(intent, REQUEST_CODE_CALL);
+
+ /*Account usedAccount = fMenuHead.getSelectedAccount();
if (usedAccount == null) {
createAccountDialog().show();
@@ -576,10 +620,14 @@
if (usedAccount.isRegistered() || usedAccount.isIP2IP()) {
Bundle args = new Bundle();
- args.putString(SipCall.ID, Integer.toString(Math.abs(new Random().nextInt())));
+
+ Matcher m = RING_ID_REGEX.matcher(to);
+ if (m.matches() && m.groupCount() > 0) {
+ to = "ring:"+m.group(1);
+ }
args.putParcelable(SipCall.ACCOUNT, usedAccount);
- args.putInt(SipCall.STATE, SipCall.state.CALL_STATE_NONE);
- args.putInt(SipCall.TYPE, SipCall.direction.CALL_TYPE_OUTGOING);
+ args.putInt(SipCall.STATE, SipCall.State.NONE);
+ args.putInt(SipCall.TYPE, SipCall.Direction.OUTGOING);
args.putParcelable(SipCall.CONTACT, CallContact.ContactBuilder.buildUnknownContact(to));
try {
@@ -589,7 +637,7 @@
}
} else {
createNotRegisteredDialog().show();
- }
+ }*/
}
private AlertDialog createNotRegisteredDialog() {
@@ -634,6 +682,7 @@
return alertDialog;
}
+ /*
@Override
public void onContactDragged() {
mContactDrawer.collapsePane();
@@ -661,7 +710,7 @@
public void setDragView(RelativeLayout relativeLayout) {
mContactDrawer.setDragView(relativeLayout);
}
-
+*/
@Override
public boolean onNavigationItemSelected(MenuItem pos) {
pos.setChecked(true);
@@ -670,7 +719,7 @@
switch (pos.getItemId()) {
case R.id.menuitem_home:
- if (fContent instanceof HomeFragment)
+ if (fContent instanceof CallListFragment)
break;
if (getFragmentManager().getBackStackEntryCount() == 1)
diff --git a/ring-android/app/src/main/java/cx/ring/client/NewConversationActivity.java b/ring-android/app/src/main/java/cx/ring/client/NewConversationActivity.java
new file mode 100644
index 0000000..9ccde78
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/client/NewConversationActivity.java
@@ -0,0 +1,141 @@
+package cx.ring.client;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.RelativeLayout;
+import android.widget.SearchView;
+
+import cx.ring.R;
+import cx.ring.fragments.ContactListFragment;
+import cx.ring.model.CallContact;
+
+public class NewConversationActivity extends Activity implements ContactListFragment.Callbacks {
+
+ //private SearchView searchView = null;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ setContentView(R.layout.activity_new_conversation);
+ }
+/*
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.newconv_option_menu, menu);
+ //searchView = (SearchView) menu.findItem(R.id.contact_search).getActionView();
+ return true;
+ }
+*/
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onCallContact(final CallContact c) {
+ if (c.getPhones().size() > 1) {
+ final CharSequence colors[] = new CharSequence[c.getPhones().size()];
+ int i = 0;
+ for (CallContact.Phone p : c.getPhones())
+ colors[i++] = p.getNumber();
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle("Choose a number");
+ builder.setItems(colors, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ CharSequence selected = colors[which];
+ Intent intent = new Intent()
+ .setClass(NewConversationActivity.this, ConversationActivity.class)
+ .setAction(Intent.ACTION_VIEW)
+ .setData(Uri.withAppendedPath(ConversationActivity.CONTENT_URI, c.getIds().get(0)))
+ .putExtra("number", selected);
+ startActivityForResult(intent, HomeActivity.REQUEST_CODE_CONVERSATION);
+ }
+ });
+ builder.show();
+ } else {
+ Intent intent = new Intent()
+ .setClass(this, ConversationActivity.class)
+ .setAction(Intent.ACTION_VIEW)
+ .setData(Uri.withAppendedPath(ConversationActivity.CONTENT_URI, c.getIds().get(0)));
+ startActivityForResult(intent, HomeActivity.REQUEST_CODE_CONVERSATION);
+ }
+ }
+
+ @Override
+ public void onTextContact(final CallContact c) {
+ if (c.getPhones().size() > 1) {
+ final CharSequence colors[] = new CharSequence[c.getPhones().size()];// {"red", "green", "blue", "black"};
+ int i = 0;
+ for (CallContact.Phone p : c.getPhones())
+ colors[i++] = p.getNumber();
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle("Choose a number");
+ builder.setItems(colors, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ CharSequence selected = colors[which];
+ Intent intent = new Intent()
+ .setClass(NewConversationActivity.this, ConversationActivity.class)
+ .setAction(Intent.ACTION_VIEW)
+ .setData(Uri.withAppendedPath(ConversationActivity.CONTENT_URI, c.getIds().get(0)))
+ .putExtra("number", selected);
+ startActivityForResult(intent, HomeActivity.REQUEST_CODE_CONVERSATION);
+ }
+ });
+ builder.show();
+ } else {
+ Intent intent = new Intent()
+ .setClass(this, ConversationActivity.class)
+ .setAction(Intent.ACTION_VIEW)
+ .setData(Uri.withAppendedPath(ConversationActivity.CONTENT_URI, c.getIds().get(0)));
+ startActivityForResult(intent, HomeActivity.REQUEST_CODE_CONVERSATION);
+ }
+ }
+
+ @Override
+ public void onContactDragged() {
+
+ }
+
+ @Override
+ public void toggleDrawer() {
+
+ }
+
+ @Override
+ public void onEditContact(CallContact item) {
+
+ }
+
+ @Override
+ public void setDragView(RelativeLayout relativeLayout) {
+
+ }
+
+ @Override
+ public void toggleForSearchDrawer() {
+
+ }
+/*
+ @Override
+ public SearchView getSearchView() {
+ return searchView;
+ }*/
+}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/AccountWrapperFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/AccountWrapperFragment.java
deleted file mode 100644
index e22fb0c..0000000
--- a/ring-android/app/src/main/java/cx/ring/fragments/AccountWrapperFragment.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2004-2014 Savoir-Faire Linux Inc.
- *
- * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Additional permission under GNU GPL version 3 section 7:
- *
- * If you modify this program, or any covered work, by linking or
- * combining it with the OpenSSL project's OpenSSL library (or a
- * modified version of that library), containing parts covered by the
- * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
- * grants you additional permission to convey the resulting work.
- * Corresponding Source for a non-source form of such a combination
- * shall include the source code for the parts of OpenSSL used as well
- * as that of the covered work.
- */
-
-package cx.ring.fragments;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.app.Fragment;
-import android.util.Log;
-import cx.ring.interfaces.AccountsInterface;
-import cx.ring.service.ConfigurationManagerCallback;
-
-public abstract class AccountWrapperFragment extends Fragment implements AccountsInterface
-{
- static final String TAG = "AccountWrapperFragment";
-
- private AccountsReceiver mReceiver;
-
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mReceiver = new AccountsReceiver();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(ConfigurationManagerCallback.ACCOUNT_STATE_CHANGED);
- intentFilter.addAction(ConfigurationManagerCallback.ACCOUNTS_CHANGED);
- getActivity().registerReceiver(mReceiver, intentFilter);
- }
-
- @Override
- public void accountsChanged() {
- Log.i(TAG, "accountsChanged");
- }
-
- @Override
- public void accountStateChanged(String accoundID, String state, int code) {
- Log.i(TAG, "accountStateChanged" + accoundID + " " + state + " " + code);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- getActivity().unregisterReceiver(mReceiver);
- }
-
- public class AccountsReceiver extends BroadcastReceiver {
-
- private final String TAG = AccountsReceiver.class.getSimpleName();
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().contentEquals(ConfigurationManagerCallback.ACCOUNT_STATE_CHANGED)) {
- Log.i(TAG, "Received " + intent.getAction() + " " + intent.getStringExtra("Account") + " " + intent.getStringExtra("state") + " " + intent.getIntExtra("code", 0));
- accountStateChanged(intent.getStringExtra("Account"), intent.getStringExtra("state"), intent.getIntExtra("code", 0));
- } else if (intent.getAction().contentEquals(ConfigurationManagerCallback.ACCOUNTS_CHANGED)) {
- Log.i(TAG, "Received" + intent.getAction());
- accountsChanged();
- }
-
- }
- }
-
-
-}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/AccountsManagementFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/AccountsManagementFragment.java
index d9b2570..f8ca5ab 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/AccountsManagementFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/AccountsManagementFragment.java
@@ -2,7 +2,8 @@
* Copyright (C) 2004-2014 Savoir-Faire Linux Inc.
*
* Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
- * Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ * Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ * Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,11 +36,12 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
-import android.app.LoaderManager;
-import android.content.AsyncTaskLoader;
+import android.app.Fragment;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.Loader;
+import android.content.IntentFilter;
+import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import android.support.design.widget.FloatingActionButton;
@@ -53,28 +55,25 @@
import cx.ring.client.AccountEditionActivity;
import cx.ring.client.AccountWizard;
import cx.ring.client.HomeActivity;
-import cx.ring.loaders.AccountsStateLoader;
-import cx.ring.loaders.AccountsLoader;
-import cx.ring.loaders.LoaderConstants;
import cx.ring.model.account.Account;
-import cx.ring.model.account.AccountDetailAdvanced;
import cx.ring.model.account.AccountDetailBasic;
-import cx.ring.service.ISipService;
+import cx.ring.service.LocalService;
import cx.ring.views.dragsortlv.DragSortListView;
import java.io.File;
import java.util.ArrayList;
-import java.util.Map;
+import java.util.List;
-public class AccountsManagementFragment extends AccountWrapperFragment implements LoaderManager.LoaderCallbacks<Bundle> {
- static final String TAG = "AccountManagementFrag";
- static final String DEFAULT_ACCOUNT_ID = "IP2IP";
- static final int ACCOUNT_CREATE_REQUEST = 1;
+public class AccountsManagementFragment extends Fragment {
+ static final String TAG = AccountsManagementFragment.class.getSimpleName();
+
+ private static final String DEFAULT_ACCOUNT_ID = "IP2IP";
+ public static final int ACCOUNT_CREATE_REQUEST = 1;
public static final int ACCOUNT_EDIT_REQUEST = 2;
- AccountsAdapter mAccountsAdapter;
- AccountsAdapter mIP2IPAdapter;
+ private AccountsAdapter mAccountsAdapter;
+ private AccountsAdapter mIP2IPAdapter;
- DragSortListView mDnDListView;
+ private DragSortListView mDnDListView;
private View mLoadingView;
private int mShortAnimationDuration;
@@ -86,46 +85,31 @@
mAccountsAdapter.remove(item);
mAccountsAdapter.insert(item, to);
try {
- mCallbacks.getService().setAccountOrder(mAccountsAdapter.generateAccountOrder());
+ mCallbacks.getService().getRemoteService().setAccountOrder(mAccountsAdapter.generateAccountOrder());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
-
};
- private Callbacks mCallbacks = sDummyCallbacks;
- private Account ip2ip;
- private static Callbacks sDummyCallbacks = new Callbacks() {
-
- @Override
- public ISipService getService() {
- return null;
- }
- };
- private AccountsLoader accountsLoader;
-
- public interface Callbacks {
-
- public ISipService getService();
-
- }
+ private LocalService.Callbacks mCallbacks = LocalService.DUMMY_CALLBACKS;
+ //private Account ip2ip;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
- if (!(activity instanceof Callbacks)) {
+ if (!(activity instanceof LocalService.Callbacks)) {
throw new IllegalStateException("Activity must implement fragment's callbacks.");
}
- mCallbacks = (Callbacks) activity;
+ mCallbacks = (LocalService.Callbacks) activity;
}
@Override
public void onDetach() {
super.onDetach();
- mCallbacks = sDummyCallbacks;
+ mCallbacks = LocalService.DUMMY_CALLBACKS;
}
@Override
@@ -133,13 +117,23 @@
super.onCreate(savedInstanceState);
Log.i(TAG, "Create Account Management Fragment");
- mAccountsAdapter = new AccountsAdapter(getActivity(), new ArrayList<Account>());
- mIP2IPAdapter = new AccountsAdapter(getActivity(), new ArrayList<Account>());
+ mAccountsAdapter = new AccountsAdapter(getActivity());
+ mIP2IPAdapter = new AccountsAdapter(getActivity());
this.setHasOptionsMenu(true);
mShortAnimationDuration = getResources().getInteger(android.R.integer.config_mediumAnimTime);
Log.i(TAG, "anim time: " + mShortAnimationDuration);
- getLoaderManager().initLoader(LoaderConstants.ACCOUNTS_LOADER, null, this);
+ //getLoaderManager().initLoader(LoaderConstants.ACCOUNTS_LOADER, null, this);
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(LocalService.ACTION_ACCOUNT_UPDATE);
+ getActivity().registerReceiver(mReceiver, intentFilter);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ getActivity().unregisterReceiver(mReceiver);
}
@Override
@@ -170,8 +164,7 @@
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
- launchAccountEditActivity(ip2ip);
-
+ launchAccountEditActivity(mIP2IPAdapter.accounts.get(0));
}
});
@@ -185,7 +178,8 @@
public void onResume() {
super.onResume();
- accountsLoader.onContentChanged();
+ //accountsLoader.onContentChanged();
+ refreshAccountList();
((HomeActivity) getActivity()).setToolbarState(true, R.string.menu_item_accounts);
FloatingActionButton btn = ((HomeActivity) getActivity()).getActionButton();
btn.setImageResource(R.drawable.ic_add_white_24dp);
@@ -196,6 +190,7 @@
startActivityForResult(intent, ACCOUNT_CREATE_REQUEST);
}
});
+ crossfade();
}
@Override
@@ -220,29 +215,17 @@
private void launchAccountEditActivity(Account acc) {
Log.i(TAG, "Launch account edit activity");
- Intent intent = new Intent().setClass(getActivity(), AccountEditionActivity.class);
- Bundle bundle = new Bundle();
- bundle.putParcelable("account", acc);
-
- intent.putExtras(bundle);
-
+ Intent intent = new Intent()
+ .setClass(getActivity(), AccountEditionActivity.class)
+ .setAction(Intent.ACTION_EDIT)
+ .setData(Uri.withAppendedPath(AccountEditionActivity.CONTENT_URI, acc.getAccountID()));
startActivityForResult(intent, ACCOUNT_EDIT_REQUEST);
}
@Override
- public void accountsChanged() {
- accountsLoader.onContentChanged();
- }
-
- @Override
- public void accountStateChanged(String accoundID, String state, int code) {
- mAccountsAdapter.updateAccount(accoundID, state, code);
- }
-
- @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
- accountsLoader.onContentChanged();
+ refreshAccountList();
}
/**
@@ -254,13 +237,12 @@
// private static final String TAG = AccountSelectionAdapter.class.getSimpleName();
- ArrayList<Account> accounts;
- Context mContext;
+ private final ArrayList<Account> accounts = new ArrayList<>();
+ private final Context mContext;
- public AccountsAdapter(Context cont, ArrayList<Account> newList) {
+ public AccountsAdapter(Context c) {
super();
- accounts = newList;
- mContext = cont;
+ mContext = c;
}
public void insert(Account item, int to) {
@@ -335,7 +317,7 @@
public void onClick(View v) {
item.setEnabled(!item.isEnabled());
try {
- mCallbacks.getService().setAccountDetails(item.getAccountID(), item.getDetails());
+ mCallbacks.getService().getRemoteService().setAccountDetails(item.getAccountID(), item.getDetails());
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -381,39 +363,27 @@
}
- public void addAll(ArrayList<Account> results) {
+ public void addAll(List<Account> results) {
Log.i(TAG, "AccountsAdapter addAll " + results.size());
accounts.addAll(results);
- /*for (final Account a : results) {
- final String acc_id = a.getAccountID();
- getLoaderManager().initLoader((int)(Long.parseLong(acc_id.substring(0, 8), 16) & 0x00000000FFFFFFFFL), null, new LoaderManager.LoaderCallbacks<Map<String, String>>() {
- @Override
- public Loader<Map<String, String>> onCreateLoader(int id, Bundle args) {
- Log.i(TAG, "AccountsAdapter addAll onCreateLoader " + id + " " + acc_id);
- return new AccountsStateLoader(getActivity(), mCallbacks.getService(), acc_id);
- }
- @Override
- public void onLoadFinished(Loader<Map<String, String>> loader, Map<String, String> data) {
- Log.w(TAG, "onLoadFinished");
- a.setRegistered_state(data.get(AccountDetailAdvanced.CONFIG_ACCOUNT_REGISTRATION_STATUS), Integer.getInteger(data.get(AccountDetailAdvanced.CONFIG_ACCOUNT_REGISTRATION_STATE_CODE)));
- notifyDataSetChanged();
- }
- @Override
- public void onLoaderReset(Loader<Map<String, String>> loader) {
- }
- });
- }*/
+ notifyDataSetChanged();
+ }
+
+ public void replaceAll(List<Account> results) {
+ Log.i(TAG, "AccountsAdapter replaceAll " + results.size());
+ accounts.clear();
+ accounts.addAll(results);
notifyDataSetChanged();
}
/**
- * Modify state of specific account
+ * Modify State of specific account
*/
public void updateAccount(String accoundID, String state, int code) {
Log.i(TAG, "updateAccount:" + state);
for (Account a : accounts) {
if (a.getAccountID().contentEquals(accoundID)) {
- a.setRegistered_state(state, code);
+ a.setRegistrationState(state, code);
notifyDataSetChanged();
return;
}
@@ -453,33 +423,24 @@
});
}
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().contentEquals(LocalService.ACTION_ACCOUNT_UPDATE)) {
+ refreshAccountList();
+ }
+ }
+ };
- @Override
- public AsyncTaskLoader<Bundle> onCreateLoader(int arg0, Bundle arg1) {
- accountsLoader = new AccountsLoader(getActivity(), mCallbacks.getService());
- return accountsLoader;
- }
-
- @Override
- public void onLoadFinished(Loader<Bundle> bundleLoader, Bundle results) {
- mAccountsAdapter.removeAll();
- ArrayList<Account> tmp = results.getParcelableArrayList(AccountsStateLoader.ACCOUNTS);
- ip2ip = results.getParcelable(AccountsStateLoader.ACCOUNT_IP2IP);
- mAccountsAdapter.addAll(tmp);
- mIP2IPAdapter.removeAll();
- mIP2IPAdapter.insert(ip2ip, 0);
+ private void refreshAccountList() {
+ Log.i(TAG, "refreshAccountList");
+ mAccountsAdapter.replaceAll(mCallbacks.getService().getAccounts());
if (mAccountsAdapter.isEmpty()) {
mDnDListView.setEmptyView(getView().findViewById(R.id.empty_account_list));
}
- for (Account acc : tmp) {
-
- }
- crossfade();
+ mIP2IPAdapter.replaceAll(mCallbacks.getService().getIP2IPAccount());
+ Log.i(TAG, "refreshAccountList DONE");
+ mAccountsAdapter.notifyDataSetChanged();
+ mIP2IPAdapter.notifyDataSetChanged();
}
-
- @Override
- public void onLoaderReset(Loader<Bundle> bundleLoader) {
-
- }
-
}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/AudioManagementFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/AudioManagementFragment.java
index ec688e0..4f90c00 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/AudioManagementFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/AudioManagementFragment.java
@@ -42,6 +42,7 @@
import cx.ring.model.account.Account;
import cx.ring.model.Codec;
import cx.ring.service.ISipService;
+import cx.ring.service.LocalService;
import cx.ring.views.dragsortlv.DragSortListView;
import android.app.Activity;
@@ -73,26 +74,23 @@
ArrayList<Codec> codecs;
private DragSortListView mCodecList;
CodecAdapter listAdapter;
- private static Callbacks sDummyCallbacks = new Callbacks() {
-
+ private static final Callbacks sDummyCallbacks = new Callbacks() {
@Override
- public ISipService getService() {
+ public ISipService getRemoteService() {
return null;
}
-
+ @Override
+ public LocalService getService() {
+ return null;
+ }
@Override
public Account getAccount() {
return null;
}
-
};
- public interface Callbacks {
-
- public ISipService getService();
-
- public Account getAccount();
-
+ public interface Callbacks extends LocalService.Callbacks {
+ Account getAccount();
}
@Override
@@ -104,7 +102,7 @@
mCallbacks = (Callbacks) activity;
try {
- codecs = (ArrayList<Codec>) mCallbacks.getService().getAudioCodecList(mCallbacks.getAccount().getAccountID());
+ codecs = (ArrayList<Codec>) mCallbacks.getRemoteService().getAudioCodecList(mCallbacks.getAccount().getAccountID());
//mCallbacks.getService().getRingtoneList();
} catch (RemoteException e) {
e.printStackTrace();
@@ -125,7 +123,7 @@
listAdapter.remove(item);
listAdapter.insert(item, to);
try {
- mCallbacks.getService().setActiveCodecList(getActiveCodecList(), mCallbacks.getAccount().getAccountID());
+ mCallbacks.getRemoteService().setActiveCodecList(getActiveCodecList(), mCallbacks.getAccount().getAccountID());
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -193,7 +191,7 @@
listAdapter.getItem(pos).toggleState();
listAdapter.notifyDataSetChanged();
try {
- mCallbacks.getService().setActiveCodecList(getActiveCodecList(), mCallbacks.getAccount().getAccountID());
+ mCallbacks.getRemoteService().setActiveCodecList(getActiveCodecList(), mCallbacks.getAccount().getAccountID());
} catch (RemoteException e) {
e.printStackTrace();
}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java
index 24bd94f..1abbce9 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java
@@ -33,59 +33,68 @@
import android.app.Activity;
import android.app.FragmentManager;
+import android.net.Uri;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationManagerCompat;
+import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.PointF;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.RemoteException;
-import android.util.FloatMath;
import android.util.Log;
import android.view.*;
-import android.view.SurfaceHolder.Callback;
import android.view.View.OnClickListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.*;
import android.widget.CompoundButton.OnCheckedChangeListener;
import cx.ring.R;
+import cx.ring.adapters.ContactPictureTask;
+import cx.ring.client.CallActivity;
+import cx.ring.client.ConversationActivity;
+import cx.ring.client.HomeActivity;
import cx.ring.interfaces.CallInterface;
-import cx.ring.service.ISipService;
import java.util.ArrayList;
import java.util.Locale;
+import java.util.Random;
-import cx.ring.model.Attractor;
-import cx.ring.model.Bubble;
import cx.ring.model.BubbleContact;
-import cx.ring.model.BubbleModel;
-import cx.ring.model.BubbleUser;
-import cx.ring.model.BubblesView;
import cx.ring.model.CallContact;
import cx.ring.model.Conference;
import cx.ring.model.SecureSipCall;
import cx.ring.model.SipCall;
+import cx.ring.model.account.Account;
+import cx.ring.service.LocalService;
+import cx.ring.service.SipService;
-public class CallFragment extends CallableWrapperFragment implements CallInterface, Callback {
+public class CallFragment extends CallableWrapperFragment implements CallInterface {
static final String TAG = "CallFragment";
-
-
private float bubbleSize = 75; // dip
private float attractorSize = 40;
public static final int REQUEST_TRANSFER = 10;
// Screen wake lock for incoming call
private WakeLock mScreenWakeLock;
+ private ImageView contactBubbleView;
+ private TextView contactBubbleTxt;
+ private View acceptButton;
+ private View refuseButton;
+ private View hangupButton;
+ private View securityIndicator;
+
+ /*
private BubblesView mBubbleView;
private BubbleModel mBubbleModel;
@@ -95,15 +104,15 @@
private Bitmap buttonUnhold;
private Bitmap buttonTransfer;
private Bitmap buttonHangUp;
-
+*/
private final int BTN_MSG_IDX = 0;
private final int BTN_HOLD_IDX = 1;
private final int BTN_TRANSFER_IDX = 2;
private final int BTN_HUNGUP_IDX = 3;
-
+/*
private BubbleModel.ActionGroup userActions;
private BubbleModel.ActionGroup callActions;
-
+*/
ViewSwitcher mSecuritySwitch;
private TextView mCallStatusTxt;
private ToggleButton mToggleSpeakers;
@@ -124,10 +133,11 @@
@Override
public void onCreate(Bundle savedBundle) {
+ Log.i(TAG, "onCreate");
super.onCreate(savedBundle);
Resources r = getResources();
-
+/*
bubbleSize = r.getDimension(R.dimen.bubble_size);
attractorSize = r.getDimension(R.dimen.bubble_action_size);
float attractorMargin = r.getDimension(R.dimen.bubble_action_margin);
@@ -203,7 +213,6 @@
@Override
public boolean bubbleEjected(Bubble b) {
- //if (b.isUser) {
try {
if (b.isConference())
mCallbacks.getService().hangUpConference(b.getCallID());
@@ -214,15 +223,13 @@
e.printStackTrace();
}
return true;
- /*}
- return false;*/
}
- });
+ });*/
setHasOptionsMenu(true);
PowerManager powerManager = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE);
mScreenWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE,
- "org.sflphone.onIncomingCall");
+ "cx.ring.onIncomingCall");
mScreenWakeLock.setReferenceCounted(false);
Log.d(TAG, "Acquire wake up lock");
@@ -240,65 +247,38 @@
}
/**
+ * The Activity calling this fragment has to implement this interface
+ */
+ public interface Callbacks extends LocalService.Callbacks {
+ void onFragmentCreated();
+ void startTimer();
+ void terminateCall();
+ Conference getDisplayedConference();
+ void updateDisplayedConference(Conference c);
+ }
+
+ /**
* A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity.
*/
- private static Callbacks sDummyCallbacks = new Callbacks() {
-
+ private static class DummyCallbacks extends LocalService.DummyCallbacks implements Callbacks {
@Override
- public void onFragmentCreated() {
-
- }
-
+ public void onFragmentCreated() {}
@Override
- public ISipService getService() {
- return null;
- }
-
- @Override
- public void terminateCall() {
- }
-
+ public void terminateCall() {}
@Override
public Conference getDisplayedConference() {
return null;
}
-
@Override
- public void updateDisplayedConference(Conference c) {
- }
-
+ public void updateDisplayedConference(Conference c) { }
@Override
- public void startTimer() {
- }
-
- @Override
- public void slideChatScreen() {
- }
-
- };
-
- /**
- * The Activity calling this fragment has to implement this interface
- */
- public interface Callbacks {
-
- public void onFragmentCreated();
-
- public ISipService getService();
-
- public void startTimer();
-
- public void slideChatScreen();
-
- public void terminateCall();
-
- public Conference getDisplayedConference();
-
- public void updateDisplayedConference(Conference c);
+ public void startTimer() { }
}
+ private static final Callbacks sDummyCallbacks = new DummyCallbacks();
@Override
public void onAttach(Activity activity) {
+ Log.i(TAG, "onAttach");
super.onAttach(activity);
if (!(activity instanceof Callbacks)) {
@@ -312,6 +292,30 @@
}
+ public void refreshState() {
+ Conference conf = getConference();
+ if (conf == null) {
+ contactBubbleView.setImageBitmap(null);
+ contactBubbleTxt.setText("");
+ acceptButton.setVisibility(View.GONE);
+ refuseButton.setVisibility(View.GONE);
+ hangupButton.setVisibility(View.GONE);
+ } else if (conf.getParticipants().size() == 1) {
+ SipCall call = conf.getParticipants().get(0);
+ if (call.isIncoming() && call.isRinging()) {
+ Log.w(TAG, "CallFragment refreshState INCOMING " + call.getCallId());
+ initIncomingCallDisplay();
+ } else if (conf.getParticipants().get(0).isRinging()) {
+ Log.w(TAG, "CallFragment refreshState RINGING " + call.getCallId());
+ initOutGoingCallDisplay();
+ } else if (call.isOngoing()) {
+ initNormalStateDisplay();
+ }
+ } else if (conf.getParticipants().size() > 1) {
+ initNormalStateDisplay();
+ }
+ }
+
@Override
public void onCreateOptionsMenu(Menu m, MenuInflater inf) {
super.onCreateOptionsMenu(m, inf);
@@ -323,7 +327,15 @@
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.menuitem_chat:
- mCallbacks.slideChatScreen();
+ //mCallbacks.slideChatScreen();
+ Intent intent = new Intent()
+ .setClass(getActivity(), ConversationActivity.class)
+ .setAction(Intent.ACTION_VIEW)
+ .setData(Uri.withAppendedPath(ConversationActivity.CONTENT_URI, getConference().getParticipants().get(0).getContact().getIds().get(0)));
+ intent.putExtra("resuming", true);
+ //intent.putExtra("contact", ((Conversation) v.getTag()).getContact());
+ //intent.putExtra("conversation", (Conversation) v.getTag());
+ startActivityForResult(intent, HomeActivity.REQUEST_CODE_CONVERSATION);
break;
}
@@ -345,6 +357,7 @@
public void onResume() {
super.onResume();
initializeWiFiListener();
+ refreshState();
}
@Override
@@ -358,7 +371,13 @@
@Override
public void callStateChanged(Conference updated, String callID, String newState) {
- mCallbacks.updateDisplayedConference(updated);
+ Conference cur = getConference();
+ if (cur.getId().equals(callID) || cur.getCallById(callID) != null) {
+ mCallbacks.updateDisplayedConference(updated);
+ } else {
+ return;
+ }
+
Log.i(TAG, "Call :" + callID + " " + newState);
if (getConference().isOnGoing()) {
@@ -371,6 +390,8 @@
} else
initOutGoingCallDisplay();
} else {
+ NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getActivity());
+ notificationManager.cancel(getConference().notificationId);
mCallStatusTxt.setText(newState);
mCallbacks.terminateCall();
}
@@ -420,7 +441,7 @@
transfer = data.getParcelableExtra("transfer");
try {
- mCallbacks.getService().attendedTransfer(transfer.getCallId(), c.getParticipants().get(0).getCallId());
+ mCallbacks.getRemoteService().attendedTransfer(transfer.getCallId(), c.getParticipants().get(0).getCallId());
} catch (RemoteException e) {
e.printStackTrace();
@@ -431,17 +452,17 @@
String to = data.getStringExtra("to_number");
transfer = data.getParcelableExtra("transfer");
try {
- mCallbacks.getService().transfer(transfer.getCallId(), to);
- mCallbacks.getService().hangUp(transfer.getCallId());
+ mCallbacks.getRemoteService().transfer(transfer.getCallId(), to);
+ mCallbacks.getRemoteService().hangUp(transfer.getCallId());
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case Activity.RESULT_CANCELED:
default:
- synchronized (mBubbleModel) {
+ /*synchronized (mBubbleModel) {
mBubbleModel.clear();
- }
+ }*/
initNormalStateDisplay();
break;
}
@@ -453,10 +474,17 @@
Log.i(TAG, "onCreateView");
final ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.frag_call, container, false);
+/*
mBubbleView = (BubblesView) rootView.findViewById(R.id.main_view);
//mBubbleView.setFragment(this);
mBubbleView.setModel(mBubbleModel);
mBubbleView.getHolder().addCallback(this);
+*/
+ contactBubbleView = (ImageView) rootView.findViewById(R.id.contact_bubble);
+ contactBubbleTxt = (TextView) rootView.findViewById(R.id.contact_bubble_txt);
+ acceptButton = rootView.findViewById(R.id.call_accept_btn);
+ refuseButton = rootView.findViewById(R.id.call_refuse_btn);
+ hangupButton = rootView.findViewById(R.id.call_hangup_btn);
mCallStatusTxt = (TextView) rootView.findViewById(R.id.call_status_txt);
@@ -468,18 +496,18 @@
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
try {
- mCallbacks.getService().toggleSpeakerPhone(isChecked);
+ mCallbacks.getRemoteService().toggleSpeakerPhone(isChecked);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
-
+/*
synchronized (mBubbleModel) {
mBubbleModel.setSize(mBubbleView.getWidth(), mBubbleView.getHeight() - mToggleSpeakers.getHeight(), bubbleSize);
- }
-
+ }*/
+/*
rootView.findViewById(R.id.dialpad_btn).setOnClickListener(new OnClickListener() {
@Override
@@ -487,7 +515,9 @@
InputMethodManager lManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
lManager.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, InputMethodManager.HIDE_IMPLICIT_ONLY);
}
- });
+ });*/
+
+ securityIndicator = rootView.findViewById(R.id.security_indicator);
return rootView;
}
@@ -498,7 +528,49 @@
private void initNormalStateDisplay() {
Log.i(TAG, "Start normal display");
- synchronized (mBubbleModel) {
+ mCallbacks.startTimer();
+ acceptButton.setVisibility(View.GONE);
+ refuseButton.setVisibility(View.GONE);
+
+ final SipCall call = getConference().getParticipants().get(0);
+ CallContact contact = call.getContact();
+ //contactBubbleView.setImageBitmap(getContactPhoto(contact, contactBubbleView.getWidth()));
+ new ContactPictureTask(getActivity(), contactBubbleView, contact).run();
+ contactBubbleTxt.setText(contact.getDisplayName());
+
+ hangupButton.setVisibility(View.VISIBLE);
+ hangupButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ try {
+ mCallbacks.getRemoteService().hangUp(call.getCallId());
+ mCallbacks.terminateCall();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+
+ NotificationCompat.Builder noti = new NotificationCompat.Builder(getActivity())
+ .setContentTitle("Current call with " + contact.getDisplayName())
+ .setOngoing(true)
+ .setSmallIcon(R.drawable.ic_launcher)
+ .setContentText("call")
+ .setContentIntent(PendingIntent.getActivity(getActivity(), new Random().nextInt(),
+ new Intent(getActivity(), CallActivity.class).putExtra("conference", getConference()), PendingIntent.FLAG_ONE_SHOT))
+ .addAction(R.drawable.ic_call_end_white_24dp, "Hangup",
+ PendingIntent.getService(getActivity(), new Random().nextInt(),
+ new Intent(getActivity(), SipService.class)
+ .setAction(SipService.ACTION_CALL_END)
+ .putExtra("conf", call.getCallId()),
+ PendingIntent.FLAG_ONE_SHOT));
+ Log.w("CallNotification ", "Updating " + getConference().notificationId + " for " + contact.getDisplayName());
+ NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getActivity());
+ notificationManager.notify(getConference().notificationId, noti.build());
+
+ getActivity().getActionBar().setTitle(contact.getDisplayName());
+
+ /*synchronized (mBubbleModel) {
mCallbacks.startTimer();
mBubbleModel.clearAttractors();
PointF c = mBubbleModel.getCircleCenter();
@@ -513,19 +585,28 @@
if (partee == null) {
continue;
}
- float dX = FloatMath.cos(angle_part * i + angle_shift) * radiusCalls;
- float dY = FloatMath.sin(angle_part * i + angle_shift) * radiusCalls;
+ double dX = Math.cos(angle_part * i + angle_shift) * radiusCalls;
+ double dY = Math.sin(angle_part * i + angle_shift) * radiusCalls;
getBubbleFor(partee, (int) (c.x + dX), (int) (c.y + dY));
}
}
- mBubbleModel.curState = BubbleModel.State.Incall;
+ mBubbleModel.curState = BubbleModel.State.Incall;*/
updateSecurityDisplay();
}
private void updateSecurityDisplay() {
-
//First we check if at least one participant use a security layer.
- if (!getConference().useSecureLayer())
+ boolean secure_call = false;
+ for (SipCall c : getConference().getParticipants()) {
+ Account acc = mCallbacks.getService().getAccount(c.getAccount());
+ if (acc != null && (acc.isRing() || acc.useSecureLayer())) {
+ secure_call = true;
+ break;
+ }
+ }
+
+ securityIndicator.setVisibility(secure_call ? View.VISIBLE : View.GONE);
+ if (!secure_call)
return;
Log.i(TAG, "Enable security display");
@@ -550,7 +631,7 @@
@Override
public void onClick(View v) {
try {
- mCallbacks.getService().confirmSAS(secured.getCallId());
+ mCallbacks.getRemoteService().confirmSAS(secured.getCallId());
showLock(R.drawable.green_lock);
} catch (RemoteException e) {
e.printStackTrace();
@@ -573,18 +654,86 @@
mSecuritySwitch.setVisibility(View.VISIBLE);
}
+ protected Bitmap getContactPhoto(CallContact contact, int size) {
+ if (contact.getPhotoId() > 0) {
+ return ContactPictureTask.loadContactPhoto(getActivity().getContentResolver(), contact.getId());
+ } else {
+ return ContactPictureTask.decodeSampledBitmapFromResource(getResources(), R.drawable.ic_contact_picture, size, size);
+ }
+ }
+
private void initIncomingCallDisplay() {
Log.i(TAG, "Start incoming display");
- if (getConference().getParticipants().get(0).getAccount().isAutoanswerEnabled()) {
+ if (mCallbacks.getService().getAccount(getConference().getParticipants().get(0).getAccount()).isAutoanswerEnabled()) {
try {
- mCallbacks.getService().accept(getConference().getParticipants().get(0).getCallId());
+ mCallbacks.getRemoteService().accept(getConference().getParticipants().get(0).getCallId());
} catch (RemoteException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
} else {
- getBubbleFor(getConference().getParticipants().get(0), mBubbleModel.getWidth() / 2, 2 * mBubbleModel.getHeight() / 3);
+ final SipCall call = getConference().getParticipants().get(0);
+ CallContact contact = call.getContact();
+ //contactBubbleView.setImageBitmap(getContactPhoto(contact, contactBubbleView.getWidth()));
+ new ContactPictureTask(getActivity(), contactBubbleView, contact).run();
+ contactBubbleTxt.setText(contact.getDisplayName());
+ acceptButton.setVisibility(View.VISIBLE);
+ acceptButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ acceptButton.setOnClickListener(null);
+ refuseButton.setOnClickListener(null);
+ try {
+ mCallbacks.getRemoteService().accept(call.getCallId());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ refuseButton.setVisibility(View.VISIBLE);
+ refuseButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ acceptButton.setOnClickListener(null);
+ refuseButton.setOnClickListener(null);
+ try {
+ mCallbacks.getRemoteService().refuse(call.getCallId());
+ mCallbacks.terminateCall();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ hangupButton.setVisibility(View.GONE);
+
+ NotificationCompat.Builder noti = new NotificationCompat.Builder(getActivity())
+ .setContentTitle("Incoming call with " + contact.getDisplayName())
+ .setContentText("incoming call")
+ .setOngoing(true)
+ .setSmallIcon(R.drawable.ic_launcher)
+ .setContentIntent(PendingIntent.getActivity(getActivity(), new Random().nextInt(),
+ new Intent(getActivity(), CallActivity.class).putExtra("conference", getConference()), PendingIntent.FLAG_ONE_SHOT))
+ .addAction(R.drawable.ic_action_accept, "Accept",
+ PendingIntent.getService(getActivity(), new Random().nextInt(),
+ new Intent(getActivity(), SipService.class)
+ .setAction(SipService.ACTION_CALL_ACCEPT)
+ .putExtra("conf", call.getCallId()),
+ PendingIntent.FLAG_ONE_SHOT))
+ .addAction(R.drawable.ic_call_end_white_24dp, "Refuse",
+ PendingIntent.getService(getActivity(), new Random().nextInt(),
+ new Intent(getActivity(), SipService.class)
+ .setAction(SipService.ACTION_CALL_REFUSE)
+ .putExtra("conf", call.getCallId()),
+ PendingIntent.FLAG_ONE_SHOT));
+ Log.w("CallNotification ", "Updating for incoming " + getConference().notificationId);
+ NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getActivity());
+ notificationManager.notify(getConference().notificationId, noti.build());
+
+ getActivity().getActionBar().setTitle(contact.getDisplayName());
+
+
+ /*getBubbleFor(getConference().getParticipants().get(0), mBubbleModel.getWidth() / 2, 2 * mBubbleModel.getHeight() / 3);
synchronized (mBubbleModel) {
mBubbleModel.clearAttractors();
mBubbleModel.addAttractor(new Attractor(new PointF(3 * mBubbleModel.getWidth() / 4, 2 * mBubbleModel.getHeight() / 3), attractorSize, new Attractor.Callback() {
@@ -616,13 +765,56 @@
}
}, buttonHangUp));
}
- mBubbleModel.curState = BubbleModel.State.Incoming;
+ mBubbleModel.curState = BubbleModel.State.Incoming;*/
}
}
private void initOutGoingCallDisplay() {
Log.i(TAG, "Start outgoing display");
- synchronized (mBubbleModel) {
+
+ final SipCall call = getConference().getParticipants().get(0);
+ CallContact contact = call.getContact();
+ //contactBubbleView.setImageBitmap(getContactPhoto(contact, contactBubbleView.getWidth()));
+ new ContactPictureTask(getActivity(), contactBubbleView, contact).run();
+ contactBubbleTxt.setText(contact.getDisplayName());
+
+ acceptButton.setVisibility(View.GONE);
+ refuseButton.setVisibility(View.GONE);
+
+ hangupButton.setVisibility(View.VISIBLE);
+ hangupButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ try {
+ mCallbacks.getRemoteService().hangUp(call.getCallId());
+ mCallbacks.terminateCall();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+
+ NotificationCompat.Builder noti = new NotificationCompat.Builder(getActivity())
+ .setContentTitle("Outgoing call with " + contact.getDisplayName())
+ .setOngoing(true)
+ .setSmallIcon(R.drawable.ic_launcher)
+ .setContentText("Outgoing call")
+ .setContentIntent(PendingIntent.getActivity(getActivity(), new Random().nextInt(),
+ new Intent(getActivity(), CallActivity.class).putExtra("conference", getConference()), PendingIntent.FLAG_ONE_SHOT))
+ .addAction(R.drawable.ic_call_end_white_24dp, "Cancel",
+ PendingIntent.getService(getActivity(), new Random().nextInt(),
+ new Intent(getActivity(), SipService.class)
+ .setAction(SipService.ACTION_CALL_END)
+ .putExtra("conf", call.getCallId()),
+ PendingIntent.FLAG_ONE_SHOT));
+
+ Log.w("CallNotification ", "Updating for outgoing " + getConference().notificationId);
+ NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getActivity());
+ notificationManager.notify(getConference().notificationId, noti.build());
+
+ getActivity().getActionBar().setTitle(contact.getDisplayName());
+
+ /*synchronized (mBubbleModel) {
PointF c = mBubbleModel.getCircleCenter();
float radiusCalls = mBubbleModel.getCircleSize();
getBubbleForUser(getConference(), c.x, c.y);
@@ -634,18 +826,18 @@
}
mBubbleModel.clearAttractors();
}
- mBubbleModel.curState = BubbleModel.State.Outgoing;
+ mBubbleModel.curState = BubbleModel.State.Outgoing;*/
}
-
- /**
- * Retrieves or create a bubble for a given contact. If the bubble exists, it is moved to the new location.
- *
- * @param call The call associated to a contact
- * @param x Initial or new x position.
- * @param y Initial or new y position.
- * @return Bubble corresponding to the contact.
- */
- private Bubble getBubbleFor(SipCall call, float x, float y) {
+ /*
+ /**
+ * Retrieves or create a bubble for a given contact. If the bubble exists, it is moved to the new location.
+ *
+ * @param call The call associated to a contact
+ * @param x Initial or new x position.
+ * @param y Initial or new y position.
+ * @return Bubble corresponding to the contact.
+ */
+ /* private Bubble getBubbleFor(SipCall call, float x, float y) {
Bubble contact_bubble = mBubbleModel.getBubble(call.getCallId());
if (contact_bubble != null) {
((BubbleContact) contact_bubble).setCall(call);
@@ -670,31 +862,41 @@
contact_bubble = new BubbleUser(getActivity(), CallContact.ContactBuilder.buildUserContact(getActivity().getContentResolver()), conf, x, y,
bubbleSize * 1.3f);
-/*
- try {
- ((BubbleUser) contact_bubble).setMute(mCallbacks.getService().isCaptureMuted());
- } catch (RemoteException e) {
- e.printStackTrace();
- } catch (NullPointerException e1) {
- e1.printStackTrace();
- }*/
mBubbleModel.addBubble(contact_bubble);
return contact_bubble;
}
+
public boolean canOpenIMPanel() {
return mBubbleModel.curState == BubbleModel.State.Incall && (mBubbleView == null || !mBubbleView.isDraggingBubble());
}
+
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
synchronized (mBubbleModel) {
mBubbleModel.setSize(width, height, bubbleSize);
}
+ Log.w(TAG, "CallFragment surfaceChanged " + getConference().getParticipants().size());
if (getConference().getParticipants().size() == 1) {
if (getConference().getParticipants().get(0).isIncoming() && getConference().getParticipants().get(0).isRinging()) {
+ Log.w(TAG, "CallFragment surfaceChanged INCOMING" + getConference().getParticipants().get(0).getCallId());
initIncomingCallDisplay();
} else if (getConference().getParticipants().get(0).isRinging()) {
+ Log.w(TAG, "CallFragment surfaceChanged RINGING" + getConference().getParticipants().get(0).getCallId());
+ initOutGoingCallDisplay();
+ } else if (getConference().getParticipants().get(0).isOngoing()) {
+ initNormalStateDisplay();
+ }
+ } else if (getConference().getParticipants().size() > 1) {
+ initNormalStateDisplay();
+ }
+ if (getConference().getParticipants().size() == 1) {
+ if (getConference().getParticipants().get(0).isIncoming() && getConference().getParticipants().get(0).isRinging()) {
+ Log.w(TAG, "CallFragment surfaceChanged INCOMING" + getConference().getParticipants().get(0).getCallId());
+ initIncomingCallDisplay();
+ } else if (getConference().getParticipants().get(0).isRinging()) {
+ Log.w(TAG, "CallFragment surfaceChanged RINGING" + getConference().getParticipants().get(0).getCallId());
initOutGoingCallDisplay();
} else if (getConference().getParticipants().get(0).isOngoing()) {
initNormalStateDisplay();
@@ -703,13 +905,13 @@
initNormalStateDisplay();
}
}
-
+*/
public void makeTransfer(BubbleContact contact) {
FragmentManager fm = getFragmentManager();
editName = TransferDFragment.newInstance();
Bundle b = new Bundle();
try {
- b.putParcelableArrayList("calls", (ArrayList<Conference>) mCallbacks.getService().getConcurrentCalls());
+ b.putParcelableArrayList("calls", (ArrayList<Conference>) mCallbacks.getRemoteService().getConcurrentCalls());
b.putParcelable("call_selected", contact.associated_call);
editName.setArguments(b);
editName.setTargetFragment(this, REQUEST_TRANSFER);
@@ -719,7 +921,7 @@
}
}
-
+/*
@Override
public void surfaceCreated(SurfaceHolder holder) {
@@ -738,10 +940,10 @@
public BubblesView getBubbleView() {
return mBubbleView;
}
-
+*/
public void updateTime() {
if (getConference() != null) {
- long duration = System.currentTimeMillis() - getConference().getParticipants().get(0).getTimestampStart_();
+ long duration = System.currentTimeMillis() - getConference().getParticipants().get(0).getTimestampStart();
duration = duration / 1000;
if (getConference().isOnGoing())
mCallStatusTxt.setText(String.format("%d:%02d:%02d", duration / 3600, duration % 3600 / 60, duration % 60));
@@ -760,7 +962,7 @@
String toSend = Character.toString(event.getDisplayLabel());
toSend = toSend.toUpperCase(Locale.getDefault());
Log.d(TAG, "toSend " + toSend);
- mCallbacks.getService().playDtmf(toSend);
+ mCallbacks.getRemoteService().playDtmf(toSend);
break;
}
} catch (RemoteException e) {
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/CallListFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/CallListFragment.java
index 997085e..36a19db 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/CallListFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/CallListFragment.java
@@ -31,60 +31,124 @@
package cx.ring.fragments;
import android.app.Activity;
+import android.app.Fragment;
+import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipData.Item;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Bitmap;
import android.graphics.Color;
+import android.net.Uri;
import android.os.*;
+import android.support.design.widget.FloatingActionButton;
import android.util.Log;
+import android.util.LruCache;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnDragListener;
import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
import android.widget.*;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
-import cx.ring.client.CallActivity;
+import cx.ring.R;
+import cx.ring.adapters.ContactPictureTask;
+import cx.ring.client.ConversationActivity;
import cx.ring.client.HomeActivity;
+import cx.ring.client.NewConversationActivity;
import cx.ring.model.Conference;
-import cx.ring.service.ISipService;
+import cx.ring.model.Conversation;
+import cx.ring.service.LocalService;
+import java.lang.ref.WeakReference;
+import java.text.DateFormat;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
-import java.util.Observable;
-import java.util.Observer;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
-public class CallListFragment extends CallableWrapperFragment {
+public class CallListFragment extends Fragment {
private static final String TAG = CallListFragment.class.getSimpleName();
- private Callbacks mCallbacks = sDummyCallbacks;
- private TextView mConversationsTitleTextView;
- CallListAdapter mConferenceAdapter;
+ private LocalService.Callbacks mCallbacks = LocalService.DUMMY_CALLBACKS;
+ //private TextView mConversationsTitleTextView;
+ private CallListAdapter mConferenceAdapter;
+ private FloatingActionButton newconv_btn = null;
+
+ @Override
+ public void onStart() {
+ Log.i(TAG, "onStart");
+ super.onStart();
+ // Bind to LocalService
+ /*Intent intent = new Intent(getActivity(), LocalService.class);
+ getActivity().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);*/
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(LocalService.ACTION_CONF_UPDATE);
+ getActivity().registerReceiver(receiver, intentFilter);
+ updateLists();
+ }
+
+ @Override
+ public void onStop() {
+ Log.i(TAG, "onStop");
+ super.onStop();
+ // Unbind from the service
+ /*if (mBound) {
+ getActivity().unbindService(mConnection);
+ mBound = false;
+ }*/
+ getActivity().unregisterReceiver(receiver);
+ }
+
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.w(TAG, "onReceive " + intent.getAction() + " " + intent.getDataString());
+ updateLists();
+ }
+ };
+/*
+ private ServiceConnection mConnection = new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Log.w(TAG, "onServiceConnected " + className.getClassName());
+ // We've bound to LocalService, cast the IBinder and get LocalService instance
+ LocalService.LocalBinder binder = (LocalService.LocalBinder) service;
+ mService = binder.getService();
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(LocalService.ACTION_CONF_UPDATE);
+
+ getActivity().registerReceiver(receiver, intentFilter);
+ mBound = true;
+
+ updateLists();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ Log.w(TAG, "onServiceDisconnected " + arg0.getClassName());
+ getActivity().unregisterReceiver(receiver);
+ mBound = false;
+ }
+ };
+*/
public static final int REQUEST_TRANSFER = 10;
public static final int REQUEST_CONF = 20;
- /**
- * A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity.
- */
- private static Callbacks sDummyCallbacks = new Callbacks() {
-
- @Override
- public ISipService getService() {
- Log.i(TAG, "I'm a dummy");
- return null;
- }
-
- };
-
+ /*
@Override
- public void callStateChanged(Conference c, String callID, String state) {
- Log.i(TAG, "callStateChanged" + callID + " " + state);
+ public void callStateChanged(Conference c, String callID, String State) {
+ Log.i(TAG, "callStateChanged " + callID + " " + State);
updateLists();
}
@@ -101,7 +165,7 @@
}
@Override
- public void confChanged(Conference c, String id, String state) {
+ public void confChanged(Conference c, String id, String State) {
Log.i(TAG, "confChanged");
updateLists();
}
@@ -111,26 +175,29 @@
Log.i(TAG, "confChanged");
updateLists();
}
-
- /**
- * The Activity calling this fragment has to implement this interface
- */
- public interface Callbacks {
- public ISipService getService();
- }
+*/
@Override
public void onAttach(Activity activity) {
+ Log.i(TAG, "onAttach");
super.onAttach(activity);
- if (!(activity instanceof Callbacks)) {
+ if (!(activity instanceof LocalService.Callbacks)) {
throw new IllegalStateException("Activity must implement fragment's callbacks.");
}
- mCallbacks = (Callbacks) activity;
-
+ mCallbacks = (LocalService.Callbacks) activity;
+ if (mCallbacks.getService() != null) {
+ /*mConvList = new ConversationList(getActivity(), mCallbacks.getService());
+ if (mConferenceAdapter != null) {
+ Log.i(TAG, "mConvList.addObserver");
+ mConferenceAdapter.updateDataset(mConvList.getConversations());
+ mConvList.addObserver(mConferenceAdapter);
+ }*/
+ }
}
+ /*
private Runnable mUpdateTimeTask = new Runnable() {
public void run() {
final long start = SystemClock.uptimeMillis();
@@ -142,52 +209,51 @@
mConferenceAdapter.notifyDataSetChanged();
mHandler.postAtTime(this, start + (((minutes * 60) + seconds + 1) * 1000));
}
- };
+ };*/
- private Handler mHandler = new Handler();
+ //private Handler mHandler = new Handler();
+ /*9
@Override
public void onResume() {
super.onResume();
if (mCallbacks.getService() != null) {
+ if (mConvList != null)
+ mConvList.startListener();
updateLists();
- if (!mConferenceAdapter.isEmpty()) {
- mHandler.postDelayed(mUpdateTimeTask, 0);
- }
}
}
-
- @SuppressWarnings("unchecked")
- // No proper solution with HashMap runtime cast
+*/
public void updateLists() {
- try {
- HashMap<String, Conference> confs = (HashMap<String, Conference>) mCallbacks.getService().getConferenceList();
- String newTitle = getResources().getQuantityString(cx.ring.R.plurals.home_conferences_title, confs.size(), confs.size());
- mConversationsTitleTextView.setText(newTitle);
- mConferenceAdapter.updateDataset(new ArrayList<Conference>(confs.values()));
- } catch (RemoteException e) {
- e.printStackTrace();
- }
+ if (mCallbacks.getService() != null)
+ mConferenceAdapter.updateDataset(mCallbacks.getService().getConversations());
}
@Override
public void onDetach() {
+ Log.i(TAG, "onDetach");
super.onDetach();
- mCallbacks = sDummyCallbacks;
-
+ mCallbacks = LocalService.DUMMY_CALLBACKS;
}
@Override
public void onCreate(Bundle savedInstanceState) {
+ Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState);
}
@Override
public void onPause() {
super.onPause();
- mHandler.removeCallbacks(mUpdateTimeTask);
+ //mHandler.removeCallbacks(mUpdateTimeTask);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ ((HomeActivity)getActivity()).setToolbarState(false, R.string.app_name);
}
@Override
@@ -200,24 +266,51 @@
Log.i(TAG, "onCreateView");
View inflatedView = inflater.inflate(cx.ring.R.layout.frag_call_list, container, false);
- mConversationsTitleTextView = (TextView) inflatedView.findViewById(cx.ring.R.id.confs_counter);
+ newconv_btn = (FloatingActionButton) inflatedView.findViewById(R.id.newconv_fab);
+ newconv_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startActivity(new Intent().setClass(getActivity(), NewConversationActivity.class));
+ }
+ });
+ //mConversationsTitleTextView = (TextView) inflatedView.findViewById(cx.ring.R.id.confs_counter);
+/*
+ if (mConferenceAdapter != null && mConvList != null)
+ mConvList.deleteObserver(mConferenceAdapter);*/
mConferenceAdapter = new CallListAdapter(getActivity());
- ((ListView) inflatedView.findViewById(cx.ring.R.id.confs_list)).setAdapter(mConferenceAdapter);
- ((ListView) inflatedView.findViewById(cx.ring.R.id.confs_list)).setOnItemClickListener(callClickListener);
- ((ListView) inflatedView.findViewById(cx.ring.R.id.confs_list)).setOnItemLongClickListener(mItemLongClickListener);
+ /*if (mConvList != null) {
+ Log.i(TAG, "mConvList.addObserver");
+ mConferenceAdapter.updateDataset(mConvList.getConversations());
+ mConvList.addObserver(mConferenceAdapter);
+ }*/
+ /*if (mBound) {
+ mConferenceAdapter.updateDataset(mService.getConversations());
+ }*/
+ LocalService service = mCallbacks.getService();
+ if (service != null)
+ mConferenceAdapter.updateDataset(mCallbacks.getService().getConversations());
+
+ ListView list = (ListView) inflatedView.findViewById(cx.ring.R.id.confs_list);
+ list.setAdapter(mConferenceAdapter);
+ list.setOnItemClickListener(callClickListener);
+ list.setOnItemLongClickListener(mItemLongClickListener);
return inflatedView;
}
- OnItemClickListener callClickListener = new OnItemClickListener() {
+ private final OnItemClickListener callClickListener = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View v, int arg2, long arg3) {
- Intent intent = new Intent().setClass(getActivity(), CallActivity.class);
+ Intent intent = new Intent()
+ .setClass(getActivity(), ConversationActivity.class)
+ .setAction(Intent.ACTION_VIEW)
+ .setData(Uri.withAppendedPath(ConversationActivity.CONTENT_URI, ((CallListAdapter.ViewHolder) v.getTag()).conv.getContact().getIds().get(0)));
intent.putExtra("resuming", true);
- intent.putExtra("conference", (Conference) v.getTag());
- startActivityForResult(intent, HomeActivity.REQUEST_CODE_CALL);
+ //intent.putExtra("contact", ((Conversation) v.getTag()).getContact());
+ //intent.putExtra("conversation", (Conversation) v.getTag());
+ startActivityForResult(intent, HomeActivity.REQUEST_CODE_CONVERSATION);
}
};
@@ -229,7 +322,8 @@
vibe.vibrate(80);
Intent i = new Intent();
Bundle b = new Bundle();
- b.putParcelable("conference", (Conference) adptv.getAdapter().getItem(pos));
+ //b.putParcelable("conference", (Conference) adptv.getAdapter().getItem(pos));
+ b.putParcelable("contact", ((Conversation) adptv.getAdapter().getItem(pos)).getContact());
i.putExtra("bconference", b);
DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
@@ -240,20 +334,32 @@
};
- public class CallListAdapter extends BaseAdapter implements Observer {
-
- private ArrayList<Conference> calls;
+ public class CallListAdapter extends BaseAdapter /*implements Observer*/ {
+ final private ArrayList<Conversation> calls = new ArrayList<>();
+ final private ExecutorService infos_fetcher = Executors.newCachedThreadPool();
+ final private LruCache<Long, Bitmap> mMemoryCache;
+ final private HashMap<Long, WeakReference<ContactPictureTask>> running_tasks = new HashMap<>();
private Context mContext;
public CallListAdapter(Context act) {
super();
mContext = act;
- calls = new ArrayList<Conference>();
-
+ final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
+ final int cacheSize = maxMemory / 8;
+ Log.i(TAG, "CallListAdapter created " + cacheSize);
+ mMemoryCache = new LruCache<Long, Bitmap>(cacheSize){
+ @Override
+ protected int sizeOf(Long key, Bitmap bitmap) {
+ return bitmap.getByteCount() / 1024;
+ }
+ };
}
- public void updateDataset(ArrayList<Conference> list) {
+ public void updateDataset(Collection<Conversation> list) {
+ Log.i(TAG, "updateDataset " + list.size());
+ if (list.size() == 0 && calls.size() == 0)
+ return;
calls.clear();
calls.addAll(list);
notifyDataSetChanged();
@@ -265,7 +371,7 @@
}
@Override
- public Conference getItem(int position) {
+ public Conversation getItem(int position) {
return calls.get(position);
}
@@ -274,38 +380,82 @@
return 0;
}
+ private class ViewHolder {
+ TextView conv_title;
+ TextView conv_status;
+ ImageView photo;
+ int position;
+ Conversation conv;
+ }
+
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
+ public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = LayoutInflater.from(mContext).inflate(cx.ring.R.layout.item_calllist, null);
- Conference call = calls.get(position);
- if (call.getParticipants().size() == 1) {
- ((TextView) convertView.findViewById(cx.ring.R.id.call_title)).setText(call.getParticipants().get(0).getmContact().getmDisplayName());
-
- long duration = (System.currentTimeMillis() - (call.getParticipants().get(0).getTimestampStart_())) / 1000;
-
- ((TextView) convertView.findViewById(cx.ring.R.id.call_time)).setText(String.format("%d:%02d:%02d", duration / 3600, (duration % 3600) / 60,
- (duration % 60)));
- } else {
-// String tmp = "Conference with " + call.getParticipants().size() + " participants";
- ((TextView) convertView.findViewById(cx.ring.R.id.call_title)).setText(getString(cx.ring.R.string.home_conf_item, call.getParticipants().size()));
+ ViewHolder holder = (ViewHolder) convertView.getTag();
+ if (holder == null) {
+ holder = new ViewHolder();
+ holder.photo = (ImageView) convertView.findViewById(R.id.photo);
+ holder.conv_title = (TextView) convertView.findViewById(cx.ring.R.id.msg_txt);
+ holder.conv_status = (TextView) convertView.findViewById(cx.ring.R.id.call_status);
+ holder.position = -1;
+ convertView.setTag(holder);
}
- // ((TextView) convertView.findViewById(R.id.num_participants)).setText("" + call.getParticipants().size());
- ((TextView) convertView.findViewById(cx.ring.R.id.call_status)).setText(call.getState());
+ final ViewHolder h = holder;
+ if (h.position == position && h.conv != null && h.conv == calls.get(position)) {
+ return convertView;
+ }
+ h.conv = calls.get(position);
+ h.position = position;
+ h.conv_title.setText(h.conv.getContact().getDisplayName());
+ h.conv_status.setText(DateFormat.getDateTimeInstance().format(h.conv.getLastInteraction()));
+ final Long cid = h.conv.getContact().getId();
+ Bitmap bmp = mMemoryCache.get(cid);
+ if (bmp != null) {
+ h.photo.setImageBitmap(bmp);
+ } else {
+ holder.photo.setImageBitmap(mMemoryCache.get(-1l));
+ final WeakReference<ViewHolder> wh = new WeakReference<>(holder);
+ final ContactPictureTask.PictureLoadedCallback cb = new ContactPictureTask.PictureLoadedCallback() {
+ @Override
+ public void onPictureLoaded(final Bitmap bmp) {
+ final ViewHolder fh = wh.get();
+ if (fh == null || fh.photo.getParent() == null)
+ return;
+ //if (fh.position == position) {
+ if (fh.conv.getContact().getId() == cid) {
+ fh.photo.post(new Runnable() {
+ @Override
+ public void run() {
+ fh.photo.setImageBitmap(bmp);
+ fh.photo.startAnimation(AnimationUtils.loadAnimation(fh.photo.getContext(), R.anim.contact_fadein));
+ }
+ });
+ }
+ }
+ };
+ WeakReference<ContactPictureTask> wtask = running_tasks.get(cid);
+ ContactPictureTask task = wtask == null ? null : wtask.get();
+ if (task != null) {
+ task.addCallback(cb);
+ } else {
+ task = new ContactPictureTask(mContext, h.photo, h.conv.getContact(), new ContactPictureTask.PictureLoadedCallback() {
+ @Override
+ public void onPictureLoaded(Bitmap bmp) {
+ mMemoryCache.put(cid, bmp);
+ running_tasks.remove(cid);
+ }
+ });
+ task.addCallback(cb);
+ running_tasks.put(cid, new WeakReference<>(task));
+ infos_fetcher.execute(task);
+ }
+ }
convertView.setOnDragListener(dragListener);
- convertView.setTag(call);
-
return convertView;
}
-
- @Override
- public void update(Observable observable, Object data) {
- Log.i(TAG, "Updating views...");
- notifyDataSetChanged();
- }
-
}
OnDragListener dragListener = new OnDragListener() {
@@ -335,8 +485,8 @@
Intent intent = i.getIntent();
intent.setExtrasClassLoader(Conference.class.getClassLoader());
- Conference initial = (Conference) view.getTag();
- Conference target = (Conference) v.getTag();
+ Conversation initial = ((CallListAdapter.ViewHolder) view.getTag()).conv;
+ Conversation target = ((CallListAdapter.ViewHolder) v.getTag()).conv;
if (initial == target) {
return true;
@@ -344,8 +494,8 @@
DropActionsChoice dialog = DropActionsChoice.newInstance();
Bundle b = new Bundle();
- b.putParcelable("call_initial", initial);
- b.putParcelable("call_targeted", target);
+ b.putParcelable("call_initial", initial.getCurrentCall());
+ b.putParcelable("call_targeted", target.getCurrentCall());
dialog.setArguments(b);
dialog.setTargetFragment(CallListFragment.this, 0);
dialog.show(getFragmentManager(), "dialog");
@@ -376,7 +526,7 @@
Conference c = data.getParcelableExtra("target");
transfer = data.getParcelableExtra("transfer");
try {
- mCallbacks.getService().attendedTransfer(transfer.getParticipants().get(0).getCallId(), c.getParticipants().get(0).getCallId());
+ mCallbacks.getService().getRemoteService().attendedTransfer(transfer.getParticipants().get(0).getCallId(), c.getParticipants().get(0).getCallId());
mConferenceAdapter.notifyDataSetChanged();
} catch (RemoteException e) {
// TODO Auto-generated catch block
@@ -389,10 +539,10 @@
String to = data.getStringExtra("to_number");
transfer = data.getParcelableExtra("transfer");
try {
- Toast.makeText(getActivity(), getString(cx.ring.R.string.home_transfering, transfer.getParticipants().get(0).getmContact().getmDisplayName(), to),
+ Toast.makeText(getActivity(), getString(cx.ring.R.string.home_transfering, transfer.getParticipants().get(0).getContact().getDisplayName(), to),
Toast.LENGTH_SHORT).show();
- mCallbacks.getService().transfer(transfer.getParticipants().get(0).getCallId(), to);
- mCallbacks.getService().hangUp(transfer.getParticipants().get(0).getCallId());
+ mCallbacks.getService().getRemoteService().transfer(transfer.getParticipants().get(0).getCallId(), to);
+ mCallbacks.getService().getRemoteService().hangUp(transfer.getParticipants().get(0).getCallId());
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
@@ -424,20 +574,20 @@
if (call_target.hasMultipleParticipants() && !call_to_add.hasMultipleParticipants()) {
- mCallbacks.getService().addParticipant(call_to_add.getParticipants().get(0), call_target.getId());
+ mCallbacks.getService().getRemoteService().addParticipant(call_to_add.getParticipants().get(0), call_target.getId());
} else if (call_target.hasMultipleParticipants() && call_to_add.hasMultipleParticipants()) {
// We join two conferences
- mCallbacks.getService().joinConference(call_to_add.getId(), call_target.getId());
+ mCallbacks.getService().getRemoteService().joinConference(call_to_add.getId(), call_target.getId());
} else if (!call_target.hasMultipleParticipants() && call_to_add.hasMultipleParticipants()) {
- mCallbacks.getService().addParticipant(call_target.getParticipants().get(0), call_to_add.getId());
+ mCallbacks.getService().getRemoteService().addParticipant(call_target.getParticipants().get(0), call_to_add.getId());
} else {
// We join two single calls to create a conf
- mCallbacks.getService().joinParticipant(call_to_add.getParticipants().get(0).getCallId(),
+ mCallbacks.getService().getRemoteService().joinParticipant(call_to_add.getParticipants().get(0).getCallId(),
call_target.getParticipants().get(0).getCallId());
}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/CallableWrapperFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/CallableWrapperFragment.java
index eb0a632..3ea8dca 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/CallableWrapperFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/CallableWrapperFragment.java
@@ -46,19 +46,11 @@
public abstract class CallableWrapperFragment extends Fragment implements CallInterface {
-
- private CallReceiver mReceiver;
-
+ private final CallReceiver mReceiver = new CallReceiver();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mReceiver = new CallReceiver();
- }
-
- @Override
- public void onResume() {
- super.onResume();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(CallManagerCallBack.INCOMING_CALL);
intentFilter.addAction(CallManagerCallBack.INCOMING_TEXT);
@@ -76,10 +68,9 @@
getActivity().registerReceiver(mReceiver, intentFilter);
}
-
@Override
- public void onPause() {
- super.onPause();
+ public void onDestroy() {
+ super.onDestroy();
getActivity().unregisterReceiver(mReceiver);
}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/ConferenceDFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/ConferenceDFragment.java
index 7bfbafa..1e70751 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/ConferenceDFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/ConferenceDFragment.java
@@ -26,7 +26,7 @@
import android.widget.ListView;
import android.widget.TextView;
-public class ConferenceDFragment extends DialogFragment implements LoaderManager.LoaderCallbacks<Bundle> {
+public class ConferenceDFragment extends DialogFragment implements LoaderManager.LoaderCallbacks<ContactsLoader.Result> {
SimpleCallListAdapter mAdapter;
@@ -75,7 +75,7 @@
- final AlertDialog a = new AlertDialog.Builder(getActivity()).setView(rootView).setTitle("Transfer " + call_selected.getParticipants().get(0).getmContact())
+ final AlertDialog a = new AlertDialog.Builder(getActivity()).setView(rootView).setTitle("Transfer " + call_selected.getParticipants().get(0).getContact())
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
@@ -87,7 +87,7 @@
}
@Override
- public Loader<Bundle> onCreateLoader(int id, Bundle args) {
+ public Loader<ContactsLoader.Result> onCreateLoader(int id, Bundle args) {
Uri baseUri;
if (args != null) {
@@ -101,14 +101,14 @@
}
@Override
- public void onLoadFinished(Loader<Bundle> loader, Bundle data) {
+ public void onLoadFinished(Loader<ContactsLoader.Result> loader, ContactsLoader.Result data) {
// ArrayList<CallContact> tmp = data.getParcelableArrayList("Contacts");
}
@Override
- public void onLoaderReset(Loader<Bundle> loader) {
+ public void onLoaderReset(Loader<ContactsLoader.Result> loader) {
// Thi is called when the last Cursor provided to onLoadFinished
// mListAdapter.swapCursor(null);
}
@@ -136,7 +136,7 @@
}
if(calls.get(position).getParticipants().size() == 1){
- tv.setText(calls.get(position).getParticipants().get(0).getmContact().getmDisplayName());
+ tv.setText(calls.get(position).getParticipants().get(0).getContact().getDisplayName());
} else {
tv.setText("Conference with "+ calls.get(position).getParticipants().size() + " participants");
}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/ContactListFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/ContactListFragment.java
index 9640452..d3ff53a 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/ContactListFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/ContactListFragment.java
@@ -39,9 +39,7 @@
import cx.ring.loaders.ContactsLoader;
import cx.ring.loaders.LoaderConstants;
import cx.ring.model.CallContact;
-import cx.ring.service.ISipService;
-import cx.ring.views.SwipeListViewTouchListener;
-import cx.ring.views.stickylistheaders.StickyListHeadersListView;
+import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
import android.app.Activity;
import android.app.Fragment;
@@ -51,14 +49,13 @@
import android.os.Bundle;
import android.provider.ContactsContract.Contacts;
import android.util.Log;
-import android.view.DragEvent;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.MeasureSpec;
-import android.view.View.OnClickListener;
-import android.view.View.OnDragListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
@@ -67,66 +64,58 @@
import android.widget.GridView;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
-import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.SearchView;
import android.widget.SearchView.OnQueryTextListener;
+import android.widget.TextView;
-public class ContactListFragment extends Fragment implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Bundle> {
- private static final String TAG = "ContactListFragment";
+public class ContactListFragment extends Fragment implements OnQueryTextListener, LoaderManager.LoaderCallbacks<ContactsLoader.Result> {
+ public static final String TAG = "ContactListFragment";
ContactsAdapter mListAdapter;
StarredContactsAdapter mGridAdapter;
- SearchView mQuickReturnSearchView;
+ //SearchView mQuickReturnSearchView;
String mCurFilter;
StickyListHeadersListView mContactList;
+
+ // favorite contacts
+ private LinearLayout llMain;
private GridView mStarredGrid;
- private SwipeListViewTouchListener mSwipeLvTouchListener;
+ private TextView favHeadLabel;
+ //private SwipeListViewTouchListener mSwipeLvTouchListener;
private LinearLayout mHeader;
+ private ViewGroup newcontact;
@Override
public void onCreate(Bundle savedInBundle) {
super.onCreate(savedInBundle);
mGridAdapter = new StarredContactsAdapter(getActivity());
mListAdapter = new ContactsAdapter(this);
+ setHasOptionsMenu(true);
}
public Callbacks mCallbacks = sDummyCallbacks;
- private LinearLayout llMain;
/**
* A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity.
*/
- private static Callbacks sDummyCallbacks = new Callbacks() {
+ private static final Callbacks sDummyCallbacks = new Callbacks() {
@Override
public void onCallContact(CallContact c) {
}
-
@Override
public void onTextContact(CallContact c) {
}
-
@Override
public void onEditContact(CallContact c) {
}
-
- @Override
- public ISipService getService() {
- Log.i(TAG, "Dummy");
- return null;
- }
-
@Override
public void onContactDragged() {
}
-
@Override
public void toggleDrawer() {
}
-
@Override
public void setDragView(RelativeLayout relativeLayout) {
-
}
-
@Override
public void toggleForSearchDrawer() {
}
@@ -134,21 +123,12 @@
public interface Callbacks {
void onCallContact(CallContact c);
-
void onTextContact(CallContact c);
-
- public ISipService getService();
-
void onContactDragged();
-
void toggleDrawer();
-
void onEditContact(CallContact item);
-
void setDragView(RelativeLayout relativeLayout);
-
void toggleForSearchDrawer();
-
}
@Override
@@ -157,9 +137,7 @@
if (!(activity instanceof Callbacks)) {
throw new IllegalStateException("Activity must implement fragment's callbacks.");
}
-
mCallbacks = (Callbacks) activity;
-
}
@Override
@@ -169,10 +147,19 @@
}
@Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ inflater.inflate(R.menu.newconv_option_menu, menu);
+ SearchView searchView = (SearchView) menu.findItem(R.id.contact_search).getActionView();
+ searchView.setOnQueryTextListener(ContactListFragment.this);
+ }
+
+ @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View inflatedView = inflater.inflate(R.layout.frag_contact_list, container, false);
mHeader = (LinearLayout) inflater.inflate(R.layout.frag_contact_list_header, null);
mContactList = (StickyListHeadersListView) inflatedView.findViewById(R.id.contacts_stickylv);
+ //mContactList.setDividerHeight(0);
+ mContactList.setDivider(null);
inflatedView.findViewById(R.id.drag_view).setOnTouchListener(new OnTouchListener() {
@@ -182,7 +169,7 @@
}
});
- inflatedView.findViewById(R.id.contact_search_button).setOnClickListener(new OnClickListener() {
+ /*inflatedView.findViewById(R.id.contact_search_button).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -203,10 +190,23 @@
});
mCallbacks.setDragView(((RelativeLayout) inflatedView.findViewById(R.id.slider_button)));
-
- mQuickReturnSearchView = (SearchView) mHeader.findViewById(R.id.contact_search);
+*/
+ //mQuickReturnSearchView = (SearchView) mHeader.findViewById(R.id.contact_search);
mStarredGrid = (GridView) mHeader.findViewById(R.id.favorites_grid);
llMain = (LinearLayout) mHeader.findViewById(R.id.llMain);
+ favHeadLabel = (TextView) mHeader.findViewById(R.id.fav_head_label);
+ newcontact = (ViewGroup) mHeader.findViewById(R.id.newcontact_element);
+ newcontact.setVisibility(View.GONE);
+ newcontact.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ CallContact c = (CallContact) v.getTag();
+ if (c == null)
+ return;
+ mCallbacks.onCallContact(c);
+ }
+ });
+
return inflatedView;
}
@@ -218,7 +218,7 @@
mContactList.setAdapter(mListAdapter);
mStarredGrid.setAdapter(mGridAdapter);
- mQuickReturnSearchView.setIconifiedByDefault(false);
+ /*mQuickReturnSearchView.setIconifiedByDefault(false);
mQuickReturnSearchView.setOnClickListener(new OnClickListener() {
@@ -228,7 +228,7 @@
mQuickReturnSearchView.setFocusable(true);
}
});
- mQuickReturnSearchView.setOnQueryTextListener(ContactListFragment.this);
+ mQuickReturnSearchView.setOnQueryTextListener(ContactListFragment.this);*/
getLoaderManager().initLoader(LoaderConstants.CONTACT_LOADER, null, this);
@@ -246,7 +246,7 @@
};
private void setGridViewListeners() {
- mStarredGrid.setOnDragListener(dragListener);
+ //mStarredGrid.setOnDragListener(dragListener);
mStarredGrid.setOnItemClickListener(new OnItemClickListener() {
@Override
@@ -258,7 +258,7 @@
}
private void setListViewListeners() {
- mSwipeLvTouchListener = new SwipeListViewTouchListener(mContactList.getWrappedList(), new SwipeListViewTouchListener.OnSwipeCallback() {
+ /*mSwipeLvTouchListener = new SwipeListViewTouchListener(mContactList.getWrappedList(), new SwipeListViewTouchListener.OnSwipeCallback() {
@Override
public void onSwipeLeft(ListView listView, int[] reverseSortedPositions) {
}
@@ -270,21 +270,21 @@
down.findViewById(R.id.quick_starred).setClickable(true);
}
- }, true, false);
+ }, true, false);*/
- mContactList.getWrappedList().setOnDragListener(dragListener);
- mContactList.getWrappedList().setOnTouchListener(mSwipeLvTouchListener);
+ /*mContactList.getWrappedList().setOnDragListener(dragListener);
+ mContactList.getWrappedList().setOnTouchListener(mSwipeLvTouchListener);*/
mContactList.getWrappedList().setOnItemLongClickListener(mItemLongClickListener);
- mContactList.getWrappedList().setOnItemClickListener(new OnItemClickListener() {
+ /*mContactList.getWrappedList().setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View view, int pos, long id) {
Log.i(TAG, "Opening Item");
mSwipeLvTouchListener.openItem(view, pos, id);
}
- });
+ });*/
}
-
+/*
OnDragListener dragListener = new OnDragListener() {
@Override
@@ -309,29 +309,36 @@
return true;
}
- };
+ };*/
@Override
public boolean onQueryTextChange(String newText) {
+ mCurFilter = newText;
if (newText.isEmpty()) {
getLoaderManager().restartLoader(LoaderConstants.CONTACT_LOADER, null, this);
+ newcontact.setVisibility(View.GONE);
return true;
}
- mCurFilter = newText;
Bundle b = new Bundle();
b.putString("filter", mCurFilter);
getLoaderManager().restartLoader(LoaderConstants.CONTACT_LOADER, b, this);
+ newcontact.setVisibility(View.VISIBLE);
+ ((TextView)newcontact.findViewById(R.id.display_name)).setText("Call \"" + newText + "\"");
+ CallContact contact = CallContact.ContactBuilder.buildUnknownContact(newText);
+ newcontact.setTag(contact);
return true;
}
@Override
public boolean onQueryTextSubmit(String query) {
// Return false to let the SearchView perform the default action
- return false;
+ //return false;
+
+ return true;
}
@Override
- public Loader<Bundle> onCreateLoader(int id, Bundle args) {
+ public Loader<ContactsLoader.Result> onCreateLoader(int id, Bundle args) {
Uri baseUri;
Log.i(TAG, "createLoader");
@@ -347,26 +354,27 @@
}
@Override
- public void onLoadFinished(Loader<Bundle> loader, Bundle data) {
-
- mGridAdapter.removeAll();
- mListAdapter.clear();
- ArrayList<CallContact> tmp = data.getParcelableArrayList("Contacts");
- ArrayList<CallContact> tmp2 = data.getParcelableArrayList("Starred");
- mListAdapter.addAll(tmp);
- mGridAdapter.addAll(tmp2);
-
+ public void onLoadFinished(Loader<ContactsLoader.Result> loader, ContactsLoader.Result data) {
+ mListAdapter.setData(data.contacts);
setListViewListeners();
- setGridViewListeners();
- mStarredGrid.post(new Runnable() {
+ if (data.starred.isEmpty()) {
+ llMain.setVisibility(View.GONE);
+ favHeadLabel.setVisibility(View.GONE);
+ mGridAdapter.removeAll();
+ } else {
+ llMain.setVisibility(View.VISIBLE);
+ favHeadLabel.setVisibility(View.VISIBLE);
+ mGridAdapter.setData(data.starred);
+ setGridViewListeners();
+ mStarredGrid.post(new Runnable() {
- @Override
- public void run() {
- setGridViewHeight(mStarredGrid, llMain);
- }
- });
-
+ @Override
+ public void run() {
+ setGridViewHeight(mStarredGrid, llMain);
+ }
+ });
+ }
}
// Sets the GridView holder's height to fully expand it
@@ -399,6 +407,6 @@
}
@Override
- public void onLoaderReset(Loader<Bundle> loader) {
+ public void onLoaderReset(Loader<ContactsLoader.Result> loader) {
}
}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/DetailsHistoryEntryFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/DetailsHistoryEntryFragment.java
index 608115b..5af66eb 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/DetailsHistoryEntryFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/DetailsHistoryEntryFragment.java
@@ -51,7 +51,6 @@
import cx.ring.service.ISipService;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Random;
@@ -126,7 +125,7 @@
lvMain.setAdapter(mAdapter);
iv = (RelativeLayout) inflatedView.findViewById(R.id.iv);
- ((TextView) iv.findViewById(R.id.history_call_name)).setText(toDisplay.getContact().getmDisplayName());
+ ((TextView) iv.findViewById(R.id.history_call_name)).setText(toDisplay.getContact().getDisplayName());
tasker = new ContactPictureTask(getActivity(), (ImageView) inflatedView.findViewById(R.id.contact_photo), toDisplay.getContact());
tasker.run();
@@ -142,8 +141,8 @@
Bundle args = new Bundle();
args.putString(SipCall.ID, Integer.toString(Math.abs(new Random().nextInt())));
args.putParcelable(SipCall.ACCOUNT, new Account(toDisplay.getAccountID(), details, creds, state));
- args.putInt(SipCall.STATE, SipCall.state.CALL_STATE_RINGING);
- args.putInt(SipCall.TYPE, SipCall.direction.CALL_TYPE_OUTGOING);
+ args.putInt(SipCall.STATE, SipCall.State.RINGING);
+ args.putInt(SipCall.TYPE, SipCall.Direction.OUTGOING);
args.putParcelable(SipCall.CONTACT, toDisplay.getContact());
mCallbacks.onCall(new SipCall(args));
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/DialingFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/DialingFragment.java
index 71425d4..7d434fc 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/DialingFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/DialingFragment.java
@@ -36,6 +36,7 @@
import android.app.Fragment;
import cx.ring.R;
import cx.ring.service.ISipService;
+import cx.ring.service.LocalService;
import cx.ring.views.ClearableEditText;
import android.app.Activity;
@@ -65,30 +66,21 @@
private Callbacks mCallbacks = sDummyCallbacks;
/**
- * A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity.
+ * The Activity calling this fragment has to implement this interface
+ *
*/
- private static Callbacks sDummyCallbacks = new Callbacks() {
- @Override
- public void onCallDialed(String to) {
- }
-
- @Override
- public ISipService getService() {
- // TODO Auto-generated method stub
- return null;
- }
- };
+ public interface Callbacks extends LocalService.Callbacks {
+ void onCallDialed(String account);
+ }
/**
- * The Activity calling this fragment has to implement this interface
- *
+ * A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity.
*/
- public interface Callbacks {
- void onCallDialed(String account);
-
- public ISipService getService();
-
+ private static class DummyCallbacks extends LocalService.DummyCallbacks implements Callbacks {
+ @Override
+ public void onCallDialed(String to) {}
}
+ private static final Callbacks sDummyCallbacks = new DummyCallbacks();
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
@@ -211,7 +203,7 @@
try {
String toSend = Character.toString(s.charAt(start));
toSend.toUpperCase(Locale.getDefault());
- mCallbacks.getService().playDtmf(toSend);
+ mCallbacks.getRemoteService().playDtmf(toSend);
} catch (RemoteException e) {
e.printStackTrace();
}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountFragment.java
index 6e9a5f2..7e1774c 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/GeneralAccountFragment.java
@@ -60,7 +60,7 @@
public interface Callbacks {
- public Account getAccount();
+ Account getAccount();
}
@@ -133,30 +133,31 @@
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
- Log.i(TAG, "Changing preference value:" + newValue);
+ Log.i(TAG, "Changing preference " + preference.getKey() + " to value:" + newValue);
+ final Account acc = mCallbacks.getAccount();
if (preference instanceof CheckBoxPreference) {
- mCallbacks.getAccount().getBasicDetails().setDetailString(preference.getKey(), newValue.toString());
+ acc.getBasicDetails().setDetailString(preference.getKey(), newValue.toString());
} else {
if (preference instanceof PasswordPreference) {
String tmp = "";
for (int i = 0; i < ((String) newValue).length(); ++i) {
tmp += "*";
}
- if(mCallbacks.getAccount().isSip())
- mCallbacks.getAccount().getCredentials().get(0).setDetailString(preference.getKey(), newValue.toString());
+ if(acc.isSip())
+ acc.getCredentials().get(0).setDetailString(preference.getKey(), newValue.toString());
preference.setSummary(tmp);
} else if(preference.getKey().contentEquals(AccountDetailBasic.CONFIG_ACCOUNT_USERNAME)) {
- if(mCallbacks.getAccount().isSip()){
- mCallbacks.getAccount().getCredentials().get(0).setDetailString(preference.getKey(), newValue.toString());
+ if(acc.isSip()){
+ acc.getCredentials().get(0).setDetailString(preference.getKey(), newValue.toString());
}
preference.setSummary((CharSequence) newValue);
} else {
preference.setSummary((CharSequence) newValue);
}
-
- mCallbacks.getAccount().getBasicDetails().setDetailString(preference.getKey(), newValue.toString());
+
+ acc.getBasicDetails().setDetailString(preference.getKey(), newValue.toString());
}
- mCallbacks.getAccount().notifyObservers();
+ acc.notifyObservers();
return true;
}
};
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/HistoryFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/HistoryFragment.java
index c7221b3..6b05055 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/HistoryFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/HistoryFragment.java
@@ -46,7 +46,6 @@
import cx.ring.loaders.HistoryLoader;
import cx.ring.loaders.LoaderConstants;
import cx.ring.history.HistoryEntry;
-import cx.ring.service.ISipService;
import android.app.Activity;
import android.content.Context;
@@ -71,27 +70,16 @@
private Callbacks mCallbacks = sDummyCallbacks;
HistoryManager mHistoryManager;
- private static Callbacks sDummyCallbacks = new Callbacks() {
+ public interface Callbacks {
+ void onCallHistory(HistoryEntry to);
+ }
+ private static final Callbacks sDummyCallbacks = new Callbacks() {
@Override
- public void onCallHistory(HistoryEntry to) {
- }
-
- @Override
- public ISipService getService() {
- Log.i(TAG, "Dummy");
- return null;
- }
-
+ public void onCallHistory(HistoryEntry to) {}
};
public static String ARGS = "Bundle.args";
- public interface Callbacks {
- public void onCallHistory(HistoryEntry to);
-
- public ISipService getService();
-
- }
@Override
public void onAttach(Activity activity) {
@@ -216,11 +204,11 @@
// to the view objects
// SipCall call = (SipCall) mCallList.values().toArray()[position];
- entryView.displayName.setText(dataset.get(pos).getContact().getmDisplayName());
+ entryView.displayName.setText(dataset.get(pos).getContact().getDisplayName());
infos_fetcher.execute(new ContactPictureTask(mContext, entryView.photo, dataset.get(pos).getContact()));
- entryView.incoming.setText(getString(R.string.hist_in_calls, dataset.get(pos).getIncoming_sum()));
- entryView.outgoing.setText(getString(R.string.hist_out_calls, dataset.get(pos).getOutgoing_sum()));
+ entryView.incoming.setText(getString(R.string.hist_in_calls, dataset.get(pos).getIncomingSum()));
+ entryView.outgoing.setText(getString(R.string.hist_out_calls, dataset.get(pos).getOutgoingSum()));
/*if (dataset.get(pos).getCalls().lastEntry().getValue().getRecordPath().length() > 0) {
entryView.replay.setVisibility(View.VISIBLE);
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/IMFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/IMFragment.java
deleted file mode 100644
index 0769d63..0000000
--- a/ring-android/app/src/main/java/cx/ring/fragments/IMFragment.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2004-2014 Savoir-Faire Linux Inc.
- *
- * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Additional permission under GNU GPL version 3 section 7:
- *
- * If you modify this program, or any covered work, by linking or
- * combining it with the OpenSSL project's OpenSSL library (or a
- * modified version of that library), containing parts covered by the
- * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
- * grants you additional permission to convey the resulting work.
- * Corresponding Source for a non-source form of such a combination
- * shall include the source code for the parts of OpenSSL used as well
- * as that of the covered work.
- */
-package cx.ring.fragments;
-
-import android.widget.*;
-import cx.ring.R;
-import cx.ring.adapters.DiscussArrayAdapter;
-import cx.ring.model.Conference;
-import cx.ring.model.SipMessage;
-import cx.ring.service.ISipService;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.inputmethod.EditorInfo;
-import android.widget.TextView.OnEditorActionListener;
-
-public class IMFragment extends CallableWrapperFragment {
- static final String TAG = IMFragment.class.getSimpleName();
-
- private Callbacks mCallbacks = sDummyCallbacks;
-
- DiscussArrayAdapter mAdapter;
- ListView list;
-
- private EditText sendTextField;
-
- @Override
- public void onCreate(Bundle savedBundle) {
- super.onCreate(savedBundle);
-
- mAdapter = new DiscussArrayAdapter(getActivity(), getArguments());
-
- }
-
- @Override
- public void incomingText(Conference updated, String ID, String from, String msg) {
- mCallbacks.updateDisplayedConference(updated);
- if(updated.equals(mCallbacks.getDisplayedConference())){
- SipMessage sipMsg = new SipMessage(true, msg);
- putMessage(sipMsg);
- }
-
- }
-
-
- /**
- * A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity.
- */
- private static Callbacks sDummyCallbacks = new Callbacks() {
-
- @Override
- public ISipService getService() {
- return null;
- }
-
- @Override
- public Conference getDisplayedConference() {
- return null;
- }
-
- @Override
- public boolean sendIM(SipMessage msg) {
- return false;
- }
-
- @Override
- public void updateDisplayedConference(Conference c) {
-
- }
-
- };
-
- /**
- * The Activity calling this fragment has to implement this interface
- */
- public interface Callbacks {
- public ISipService getService();
-
- public Conference getDisplayedConference();
-
- public boolean sendIM(SipMessage msg);
-
- public void updateDisplayedConference(Conference c);
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
-
- if (!(activity instanceof Callbacks)) {
- throw new IllegalStateException("Activity must implement fragment's callbacks.");
- }
-
- mCallbacks = (Callbacks) activity;
- }
-
- @Override
- public void onDetach() {
- super.onDetach();
- mCallbacks = sDummyCallbacks;
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.frag_imessaging, container, false);
-
- list = (ListView) rootView.findViewById(R.id.message_list);
- list.setAdapter(mAdapter);
-
- sendTextField = (EditText) rootView.findViewById(R.id.send_im_edittext);
-
- sendTextField.setOnEditorActionListener(new OnEditorActionListener() {
-
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
-
- if (actionId == EditorInfo.IME_ACTION_SEND) {
- sendMessage();
- }
- return true;
- }
- });
-
- rootView.findViewById(R.id.send_im_button).setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- sendMessage();
- }
- });
-
-
- return rootView;
- }
-
- private void sendMessage() {
- if (sendTextField.getText().toString().length() > 0) {
- SipMessage toSend = new SipMessage(false, sendTextField.getText().toString());
- if (mCallbacks.sendIM(toSend)) {
- putMessage(toSend);
- sendTextField.setText("");
- } else {
- Toast.makeText(getActivity(), "Error sending message", Toast.LENGTH_SHORT).show();
- }
- }
- }
-
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- }
-
- public void putMessage(SipMessage msg) {
- mAdapter.add(msg);
- Log.i(TAG, "Messages" + mAdapter.getCount());
- }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/MenuFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/MenuFragment.java
index 42bc786..ad0ed23 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/MenuFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/MenuFragment.java
@@ -31,9 +31,13 @@
package cx.ring.fragments;
import android.app.Activity;
+import android.app.Fragment;
import android.app.LoaderManager;
import android.content.AsyncTaskLoader;
+import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.Loader;
import android.os.Bundle;
import android.os.RemoteException;
@@ -46,28 +50,25 @@
import cx.ring.R;
import cx.ring.adapters.AccountSelectionAdapter;
import cx.ring.adapters.ContactPictureTask;
+import cx.ring.client.HomeActivity;
import cx.ring.loaders.AccountsLoader;
import cx.ring.loaders.LoaderConstants;
import cx.ring.model.account.Account;
import cx.ring.model.CallContact;
+import cx.ring.service.ConfigurationManagerCallback;
import cx.ring.service.ISipService;
+import cx.ring.service.LocalService;
import java.util.ArrayList;
-public class MenuFragment extends AccountWrapperFragment implements LoaderManager.LoaderCallbacks<Bundle> {
+public class MenuFragment extends Fragment /*extends AccountWrapperFragment implements LoaderManager.LoaderCallbacks<Bundle>*/ {
@SuppressWarnings("unused")
private static final String TAG = MenuFragment.class.getSimpleName();
AccountSelectionAdapter mAccountAdapter;
private Spinner spinnerAccounts;
- private Callbacks mCallbacks = sDummyCallbacks;
- private static Callbacks sDummyCallbacks = new Callbacks() {
- @Override
- public ISipService getService() {
- return null;
- }
- };
+ private LocalService.Callbacks mCallbacks = LocalService.DUMMY_CALLBACKS;
public Account retrieveAccountById(String accountID) {
Account toReturn;
@@ -79,23 +80,20 @@
return toReturn;
}
- public interface Callbacks {
- ISipService getService();
- }
-
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
- if (!(activity instanceof Callbacks)) {
+ if (!(activity instanceof LocalService.Callbacks)) {
throw new IllegalStateException("Activity must implement fragment's callbacks.");
}
- mCallbacks = (Callbacks) activity;
+ mCallbacks = (LocalService.Callbacks) activity;
+ updateAllAccounts();
}
@Override
public void onDetach() {
super.onDetach();
- mCallbacks = sDummyCallbacks;
+ mCallbacks = LocalService.DUMMY_CALLBACKS;
}
@Override
@@ -103,25 +101,34 @@
super.onCreate(savedInstanceState);
}
- public void onResume() {
- super.onResume();
-
- Log.i(TAG, "Resuming");
- getLoaderManager().restartLoader(LoaderConstants.ACCOUNTS_LOADER, null, this);
-
- }
-
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
- getLoaderManager().restartLoader(LoaderConstants.ACCOUNTS_LOADER, null, this);
+ //getLoaderManager().restartLoader(LoaderConstants.ACCOUNTS_LOADER, null, this);
}
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().contentEquals(LocalService.ACTION_ACCOUNT_UPDATE)) {
+ updateAllAccounts();
+ }
+ }
+ };
+ @Override
+ public void onResume() {
+ Log.i(TAG, "Resuming");
+ super.onResume();
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(LocalService.ACTION_ACCOUNT_UPDATE);
+ getActivity().registerReceiver(mReceiver, intentFilter);
+ }
@Override
public void onPause() {
super.onPause();
+ getActivity().unregisterReceiver(mReceiver);
}
@Override
@@ -139,7 +146,7 @@
mAccountAdapter.setSelectedAccount(pos);
//view.findViewById(R.id.account_selected).setVisibility(View.GONE);
try {
- mCallbacks.getService().setAccountOrder(mAccountAdapter.getAccountOrder());
+ mCallbacks.getRemoteService().setAccountOrder(mAccountAdapter.getAccountOrder());
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -155,7 +162,9 @@
CallContact user = CallContact.ContactBuilder.buildUserContact(getActivity().getContentResolver());
new ContactPictureTask(getActivity(), (ImageView) inflatedView.findViewById(R.id.user_photo), user).run();
- ((TextView) inflatedView.findViewById(R.id.user_name)).setText(user.getmDisplayName());
+ ((TextView) inflatedView.findViewById(R.id.user_name)).setText(user.getDisplayName());
+
+ updateAllAccounts();
return inflatedView;
}
@@ -172,10 +181,15 @@
}
public void updateAllAccounts() {
- if (getActivity() != null)
- getLoaderManager().restartLoader(LoaderConstants.ACCOUNTS_LOADER, null, this);
+ /*if (getActivity() != null)
+ getLoaderManager().restartLoader(LoaderConstants.ACCOUNTS_LOADER, null, this);*/
+ if (mAccountAdapter != null && mCallbacks.getService() != null) {
+ mAccountAdapter.removeAll();
+ mAccountAdapter.addAll(mCallbacks.getService().getAccounts());
+ }
}
+ /*
@Override
public void accountsChanged() {
updateAllAccounts();
@@ -183,16 +197,16 @@
}
@Override
- public void accountStateChanged(String accoundID, String state, int code) {
- Log.w(TAG, "accountStateChanged " + accoundID + " " + state);
+ public void accountStateChanged(String accoundID, String State, int code) {
+ Log.w(TAG, "accountStateChanged " + accoundID + " " + State);
if (mAccountAdapter != null)
- mAccountAdapter.updateAccount(accoundID, state, code);
+ mAccountAdapter.updateAccount(accoundID, State, code);
}
@Override
public AsyncTaskLoader<Bundle> onCreateLoader(int arg0, Bundle arg1) {
- AccountsLoader l = new AccountsLoader(getActivity(), mCallbacks.getService());
+ AccountsLoader l = new AccountsLoader(getActivity(), mCallbacks.getRemoteService());
l.forceLoad();
return l;
}
@@ -208,6 +222,6 @@
@Override
public void onLoaderReset(Loader<Bundle> loader) {
- }
+ }*/
}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/NestedSettingsFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/NestedSettingsFragment.java
index 87e3581..6d192f1 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/NestedSettingsFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/NestedSettingsFragment.java
@@ -47,6 +47,7 @@
import cx.ring.model.account.TLSManager;
import cx.ring.model.account.Account;
import cx.ring.service.ISipService;
+import cx.ring.service.LocalService;
import java.util.ArrayList;
@@ -62,23 +63,24 @@
TLSManager mTlsManager;
private static Callbacks sDummyCallbacks = new Callbacks() {
-
+ @Override
+ public ISipService getRemoteService() {
+ return null;
+ }
+ @Override
+ public LocalService getService() {
+ return null;
+ }
@Override
public Account getAccount() {
return null;
}
-
- @Override
- public ISipService getService() {
- return null;
- }
-
};
public String[] getTlsMethods() {
ArrayList<String> methods = null;
try {
- methods = (ArrayList<String>) mCallbacks.getService().getTlsSupportedMethods();
+ methods = (ArrayList<String>) mCallbacks.getRemoteService().getTlsSupportedMethods();
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -105,12 +107,8 @@
return false;
}
- public interface Callbacks {
-
- public Account getAccount();
-
- public ISipService getService();
-
+ public interface Callbacks extends LocalService.Callbacks {
+ Account getAccount();
}
@Override
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountFragment.java
index 01042e6..f039995 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/SecurityAccountFragment.java
@@ -50,8 +50,7 @@
private static final String TAG = SecurityAccountFragment.class.getSimpleName();
private Callbacks mCallbacks = sDummyCallbacks;
- private static Callbacks sDummyCallbacks = new Callbacks() {
-
+ private static final Callbacks sDummyCallbacks = new Callbacks() {
@Override
public Account getAccount() {
return null;
@@ -68,19 +67,13 @@
@Override
public void displayTLSScreen() {
}
-
};
public interface Callbacks {
-
- public Account getAccount();
-
- public void displayCredentialsScreen();
-
- public void displaySRTPScreen();
-
- public void displayTLSScreen();
-
+ Account getAccount();
+ void displayCredentialsScreen();
+ void displaySRTPScreen();
+ void displayTLSScreen();
}
@Override
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/TransferDFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/TransferDFragment.java
index cb331de..a2d8a57 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/TransferDFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/TransferDFragment.java
@@ -70,7 +70,7 @@
import android.widget.TextView;
import android.widget.Toast;
-public class TransferDFragment extends DialogFragment implements LoaderManager.LoaderCallbacks<Bundle> {
+public class TransferDFragment extends DialogFragment implements LoaderManager.LoaderCallbacks<ContactsLoader.Result> {
public static final int RESULT_TRANSFER_CONF = Activity.RESULT_FIRST_USER + 1;
public static final int RESULT_TRANSFER_NUMBER = Activity.RESULT_FIRST_USER + 2;
@@ -124,7 +124,7 @@
mEditText.setAdapter(autoCompleteAdapter);
final AlertDialog a = new AlertDialog.Builder(getActivity()).setView(rootView)
- .setTitle("Transfer " + call_selected.getmContact().getmDisplayName())
+ .setTitle("Transfer " + call_selected.getContact().getDisplayName())
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
@@ -165,7 +165,7 @@
}
@Override
- public Loader<Bundle> onCreateLoader(int id, Bundle args) {
+ public Loader<ContactsLoader.Result> onCreateLoader(int id, Bundle args) {
Uri baseUri;
if (args != null) {
@@ -179,14 +179,14 @@
}
@Override
- public void onLoadFinished(Loader<Bundle> loader, Bundle data) {
+ public void onLoadFinished(Loader<ContactsLoader.Result> loader, ContactsLoader.Result data) {
// ArrayList<CallContact> tmp = data.getParcelableArrayList("Contacts");
}
@Override
- public void onLoaderReset(Loader<Bundle> loader) {
+ public void onLoaderReset(Loader<ContactsLoader.Result> loader) {
// Thi is called when the last Cursor provided to onLoadFinished
// mListAdapter.swapCursor(null);
}
@@ -281,7 +281,7 @@
tv = (TextView) mInflater.inflate(android.R.layout.simple_dropdown_item_1line, parent, false);
}
- tv.setText(calls.get(position).getParticipants().get(0).getmContact().getmDisplayName());
+ tv.setText(calls.get(position).getParticipants().get(0).getContact().getDisplayName());
return tv;
}
diff --git a/ring-android/app/src/main/java/cx/ring/history/DatabaseHelper.java b/ring-android/app/src/main/java/cx/ring/history/DatabaseHelper.java
index a623e98..43c116f 100644
--- a/ring-android/app/src/main/java/cx/ring/history/DatabaseHelper.java
+++ b/ring-android/app/src/main/java/cx/ring/history/DatabaseHelper.java
@@ -48,13 +48,12 @@
*/
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
- // name of the database file for your application -- change to something appropriate for your app
private static final String DATABASE_NAME = "history.db";
// any time you make changes to your database objects, you may have to increase the database version
- private static final int DATABASE_VERSION = 2;
+ private static final int DATABASE_VERSION = 4;
- // the DAO object we use to access the SimpleData table
private Dao<HistoryCall, Integer> historyDao = null;
+ private Dao<HistoryText, Integer> historyTextDao = null;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -67,8 +66,10 @@
@Override
public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
try {
+ //TableUtils.dropTable(connectionSource, HistoryCall.class, true);
Log.i(DatabaseHelper.class.getName(), "onCreate");
TableUtils.createTable(connectionSource, HistoryCall.class);
+ TableUtils.createTable(connectionSource, HistoryText.class);
} catch (SQLException e) {
Log.e(DatabaseHelper.class.getName(), "Can't create database", e);
throw new RuntimeException(e);
@@ -84,6 +85,7 @@
try {
Log.i(DatabaseHelper.class.getName(), "onUpgrade");
TableUtils.dropTable(connectionSource, HistoryCall.class, true);
+ TableUtils.dropTable(connectionSource, HistoryText.class, true);
// after we drop the old databases, we create the new ones
onCreate(db, connectionSource);
} catch (SQLException e) {
@@ -102,6 +104,12 @@
}
return historyDao;
}
+ public Dao<HistoryText, Integer> getTextHistoryDao() throws SQLException {
+ if (historyTextDao == null) {
+ historyTextDao = getDao(HistoryText.class);
+ }
+ return historyTextDao;
+ }
/**
* Close the database connections and clear any cached DAOs.
@@ -110,5 +118,6 @@
public void close() {
super.close();
historyDao = null;
+ historyTextDao = null;
}
}
diff --git a/ring-android/app/src/main/java/cx/ring/history/HistoryCall.java b/ring-android/app/src/main/java/cx/ring/history/HistoryCall.java
index 2e76729..0e818d0 100644
--- a/ring-android/app/src/main/java/cx/ring/history/HistoryCall.java
+++ b/ring-android/app/src/main/java/cx/ring/history/HistoryCall.java
@@ -46,11 +46,11 @@
public class HistoryCall implements Parcelable {
@DatabaseField(index = true, columnName="TIMESTAMP_START")
- long call_start;
+ public long call_start;
@DatabaseField
- long call_end;
+ public long call_end;
@DatabaseField
- String number;
+ public String number;
@DatabaseField
boolean missed;
@DatabaseField
@@ -62,6 +62,8 @@
@DatabaseField
long contactID;
@DatabaseField
+ String contactKey;
+ @DatabaseField
String callID;
public String getAccountID() {
@@ -73,14 +75,15 @@
}
public HistoryCall(SipCall call) {
- call_start = call.getTimestampStart_();
- call_end = call.getTimestampEnd_();
- accountID = call.getAccount().getAccountID();
- number = call.getmContact().getPhones().get(0).getNumber();
+ call_start = call.getTimestampStart();
+ call_end = call.getTimestampEnd();
+ accountID = call.getAccount();
+ number = call.getNumber();
missed = call.isRinging() && call.isIncoming();
direction = call.getCallType();
recordPath = call.getRecordPath();
- contactID = call.getmContact().getId();
+ contactID = call.getContact().getId();
+ contactKey = call.getContact().getKey();
callID = call.getCallId();
}
@@ -90,10 +93,10 @@
public String getDirection() {
switch (direction) {
- case SipCall.direction.CALL_TYPE_INCOMING:
- return "CALL_TYPE_INCOMING";
- case SipCall.direction.CALL_TYPE_OUTGOING:
- return "CALL_TYPE_OUTGOING";
+ case SipCall.Direction.INCOMING:
+ return "INCOMING";
+ case SipCall.Direction.OUTGOING:
+ return "OUTGOING";
default:
return "CALL_TYPE_UNDETERMINED";
}
@@ -102,9 +105,15 @@
public String getDate() {
return HistoryTimeModel.timeToHistoryConst(call_start);
}
+ public Date getStartDate() {
+ return new Date(call_start);
+ }
+ public Date getEndDate() {
+ return new Date(call_end);
+ }
public String getStartString(String format) {
- Timestamp stamp = new Timestamp(call_start * 1000); // in milliseconds
+ Timestamp stamp = new Timestamp(call_start); // in milliseconds
Date date = new Date(stamp.getTime());
SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.getDefault());
sdf.setTimeZone(TimeZone.getDefault());
@@ -114,7 +123,7 @@
public String getDurationString() {
- long duration = call_end - call_start;
+ long duration = (call_end - call_start)/1000;
if (duration < 60)
return String.format(Locale.getDefault(), "%02d secs", duration);
@@ -122,7 +131,6 @@
return String.format(Locale.getDefault(), "%02d mins %02d secs", (duration % 3600) / 60, (duration % 60));
return String.format(Locale.getDefault(), "%d h %02d mins %02d secs", duration / 3600, (duration % 3600) / 60, (duration % 60));
-
}
public long getDuration() {
@@ -152,6 +160,7 @@
dest.writeInt(direction);
dest.writeString(recordPath);
dest.writeLong(contactID);
+ dest.writeString(contactKey);
dest.writeString(callID);
}
@@ -174,6 +183,7 @@
direction = in.readInt();
recordPath = in.readString();
contactID = in.readLong();
+ contactKey = in.readString();
callID = in.readString();
}
@@ -182,11 +192,14 @@
}
public boolean isIncoming() {
- return direction == SipCall.direction.CALL_TYPE_INCOMING;
+ return direction == SipCall.Direction.INCOMING;
}
public boolean isMissed() {
return missed;
}
+ public CharSequence getCallId() {
+ return callID;
+ }
}
diff --git a/ring-android/app/src/main/java/cx/ring/history/HistoryEntry.java b/ring-android/app/src/main/java/cx/ring/history/HistoryEntry.java
index 385355c..433db3c 100644
--- a/ring-android/app/src/main/java/cx/ring/history/HistoryEntry.java
+++ b/ring-android/app/src/main/java/cx/ring/history/HistoryEntry.java
@@ -34,15 +34,18 @@
import android.os.Parcel;
import android.os.Parcelable;
import cx.ring.model.CallContact;
+import cx.ring.model.TextMessage;
import java.util.ArrayList;
+import java.util.Date;
import java.util.NavigableMap;
import java.util.TreeMap;
public class HistoryEntry implements Parcelable {
private CallContact contact;
- private NavigableMap<Long, HistoryCall> calls;
+ private final NavigableMap<Long, HistoryCall> calls = new TreeMap<>();
+ private final NavigableMap<Long, TextMessage> text_messages = new TreeMap<>();
private String accountID;
int missed_sum;
int outgoing_sum;
@@ -50,7 +53,6 @@
public HistoryEntry(String account, CallContact c) {
contact = c;
- calls = new TreeMap<Long, HistoryCall>();
accountID = account;
missed_sum = outgoing_sum = incoming_sum = 0;
}
@@ -66,6 +68,9 @@
public NavigableMap<Long, HistoryCall> getCalls() {
return calls;
}
+ public NavigableMap<Long, TextMessage> getTextMessages() {
+ return text_messages;
+ }
public CallContact getContact() {
return contact;
@@ -97,13 +102,24 @@
setContact(linkedTo);
}
+ public void addTextMessage(TextMessage text) {
+ text_messages.put(text.getTimestamp(), text);
+ if (contact.isUnknown() && !text.getContact().isUnknown())
+ setContact(text.getContact());
+ }
+ /*public void addTextMessage(HistoryText text) {
+ TextMessage txt = new TextMessage(text);
+ txt.setContact(contact);
+ text_messages.put(txt.getTimestamp(), txt);
+ }*/
+
public String getNumber() {
return calls.lastEntry().getValue().number;
}
public String getTotalDuration() {
int duration = 0;
- ArrayList<HistoryCall> all_calls = new ArrayList<HistoryCall>(calls.values());
+ ArrayList<HistoryCall> all_calls = new ArrayList<>(calls.values());
for (HistoryCall all_call : all_calls) {
duration += all_call.getDuration();
}
@@ -114,15 +130,60 @@
return duration / 60 + "min";
}
- public int getMissed_sum() {
+ public Date getLastCallDate() {
+ /*Date d = new Date(0);
+ for (Map.Entry<Long, HistoryCall> c : getCalls().entrySet()) {
+ Date nd = c.getValue().getStartDate();
+ if (d.compareTo(nd) < 0)
+ d = nd;
+ }
+ return d;*/
+ return new Date(calls.isEmpty() ? 0 : calls.lastEntry().getKey());
+ }
+ public Date getLastTextDate() {
+ return new Date(text_messages.isEmpty() ? 0 : text_messages.lastEntry().getKey());
+ }
+ public Date getLastInteraction() {
+ return new Date(Math.max(calls.isEmpty() ? 0 : calls.lastEntry().getKey(), text_messages.isEmpty() ? 0 : text_messages.lastEntry().getKey()));
+ }
+
+ public HistoryCall getLastOutgoingCall() {
+ for (HistoryCall c : calls.descendingMap().values())
+ if (!c.isIncoming())
+ return c;
+ return null;
+ }
+ public TextMessage getLastOutgoingText() {
+ for (TextMessage c : text_messages.descendingMap().values())
+ if (c.isOutgoing())
+ return c;
+ return null;
+ }
+
+ public String getLastNumberUsed() {
+ HistoryCall call = getLastOutgoingCall();
+ TextMessage text = getLastOutgoingText();
+ if (call == null && text == null)
+ return null;
+ if (call == null)
+ return text.getNumber();
+ if (text == null)
+ return call.getNumber();
+ if (call.call_start < text.getTimestamp())
+ return text.getNumber();
+ else
+ return call.getNumber();
+ }
+
+ public int getMissedSum() {
return missed_sum;
}
- public int getOutgoing_sum() {
+ public int getOutgoingSum() {
return outgoing_sum;
}
- public int getIncoming_sum() {
+ public int getIncomingSum() {
return incoming_sum;
}
@@ -136,8 +197,10 @@
dest.writeParcelable(contact, 0);
- dest.writeList(new ArrayList<HistoryCall>(calls.values()));
- dest.writeList(new ArrayList<Long>(calls.keySet()));
+ dest.writeList(new ArrayList<>(calls.values()));
+ dest.writeList(new ArrayList<>(calls.keySet()));
+ dest.writeList(new ArrayList<>(text_messages.values()));
+ dest.writeList(new ArrayList<>(text_messages.keySet()));
dest.writeString(accountID);
dest.writeInt(missed_sum);
@@ -159,21 +222,24 @@
private HistoryEntry(Parcel in) {
contact = in.readParcelable(CallContact.class.getClassLoader());
- ArrayList<HistoryCall> values = new ArrayList<HistoryCall>();
+ ArrayList<HistoryCall> values = new ArrayList<>();
in.readList(values, HistoryCall.class.getClassLoader());
-
- ArrayList<Long> keys = new ArrayList<Long>();
+ ArrayList<Long> keys = new ArrayList<>();
in.readList(keys, Long.class.getClassLoader());
-
- calls = new TreeMap<Long, HistoryCall>();
for (int i = 0; i < keys.size(); ++i) {
calls.put(keys.get(i), values.get(i));
}
+ ArrayList<TextMessage> tvalues = new ArrayList<>();
+ in.readList(tvalues, TextMessage.class.getClassLoader());
+ ArrayList<Long> tkeys = new ArrayList<>();
+ in.readList(tkeys, Long.class.getClassLoader());
+ for (int i = 0; i < keys.size(); ++i) {
+ text_messages.put(tkeys.get(i), tvalues.get(i));
+ }
accountID = in.readString();
missed_sum = in.readInt();
outgoing_sum = in.readInt();
incoming_sum = in.readInt();
}
-
}
diff --git a/ring-android/app/src/main/java/cx/ring/history/HistoryManager.java b/ring-android/app/src/main/java/cx/ring/history/HistoryManager.java
index 591cbf0..07355c2 100644
--- a/ring-android/app/src/main/java/cx/ring/history/HistoryManager.java
+++ b/ring-android/app/src/main/java/cx/ring/history/HistoryManager.java
@@ -32,6 +32,8 @@
package cx.ring.history;
import android.content.Context;
+import android.util.Log;
+
import com.j256.ormlite.android.apptools.OpenHelperManager;
import com.j256.ormlite.stmt.QueryBuilder;
import com.j256.ormlite.table.TableUtils;
@@ -53,9 +55,10 @@
public boolean insertNewEntry(Conference toInsert){
for (SipCall call : toInsert.getParticipants()) {
- call.setTimestampEnd_(System.currentTimeMillis());
+ call.setTimestampEnd(System.currentTimeMillis());
HistoryCall persistent = new HistoryCall(call);
try {
+ Log.w("HistoryManager", "HistoryDao().create() " + persistent.getNumber() + " " + persistent.getStartDate().toString() + " " + persistent.getEndDate());
getHelper().getHistoryDao().create(persistent);
} catch (SQLException e) {
e.printStackTrace();
@@ -65,6 +68,17 @@
return true;
}
+ public boolean insertNewTextMessage(HistoryText txt) {
+ try {
+ Log.w("HistoryManager", "HistoryDao().create() acc:" + txt.getAccountID() + " num:" + txt.getNumber() + " date:" + txt.getDate().toString() + " msg:" + txt.getMessage());
+ getHelper().getTextHistoryDao().create(txt);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
/*
* Necessary when user hang up a call in a Conference
* The call creates an HistoryCall, but the conference still goes on
@@ -84,16 +98,21 @@
}
public List<HistoryCall> getAll() throws SQLException {
-
QueryBuilder<HistoryCall, Integer> qb = getHelper().getHistoryDao().queryBuilder();
qb.orderBy("TIMESTAMP_START", true);
-
return getHelper().getHistoryDao().query(qb.prepare());
}
+ public List<HistoryText> getAllTextMessages() throws SQLException {
+ QueryBuilder<HistoryText, Integer> qb = getHelper().getTextHistoryDao().queryBuilder();
+ qb.orderBy("TIMESTAMP", true);
+ return getHelper().getTextHistoryDao().query(qb.prepare());
+ }
+
public boolean clearDB() {
try {
TableUtils.clearTable(getHelper().getConnectionSource(), HistoryCall.class);
+ TableUtils.clearTable(getHelper().getConnectionSource(), HistoryText.class);
} catch (SQLException e) {
e.printStackTrace();
return false;
diff --git a/ring-android/app/src/main/java/cx/ring/history/HistoryText.java b/ring-android/app/src/main/java/cx/ring/history/HistoryText.java
new file mode 100644
index 0000000..372c203
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/history/HistoryText.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2004-2014 Savoir-Faire Linux Inc.
+ *
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+
+
+package cx.ring.history;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.j256.ormlite.field.DatabaseField;
+
+import java.sql.Timestamp;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import cx.ring.model.CallContact;
+import cx.ring.model.TextMessage;
+
+public class HistoryText implements Parcelable {
+
+ @DatabaseField(index = true, columnName="id")
+ public String id;
+ @DatabaseField(index = true, columnName="TIMESTAMP")
+ public long time;
+ @DatabaseField
+ public String number;
+ @DatabaseField
+ int direction;
+ @DatabaseField
+ String accountID;
+ @DatabaseField
+ long contactID;
+ @DatabaseField
+ String contactKey;
+ @DatabaseField
+ String callID;
+ @DatabaseField
+ String message;
+
+ public HistoryText(TextMessage txt) {
+ id = txt.getId();
+ time = txt.getTimestamp();
+ accountID = txt.getAccount();
+ number = txt.getNumber();
+ direction = txt.getCallType();
+ message = txt.getMessage();
+ callID = txt.getCallId();
+ if (txt.getContact() != null) {
+ contactID = txt.getContact().getId();
+ contactKey = txt.getContact().getKey();
+ }
+ }
+
+ public String getAccountID() {
+ return accountID;
+ }
+
+ public long getContactID() {
+ return contactID;
+ }
+
+ /*
+ public HistoryText(String account, String from, String msg, CallContact contact, boolean incoming) {
+ time = System.currentTimeMillis();
+ accountID = account;
+ number = from;
+ direction = incoming ? TextMessage.direction.INCOMING : TextMessage.direction.OUTGOING;
+ if (contact != null) {
+ contactID = contact.getId();
+ contactKey = contact.getKey();
+ }
+ //callID = call.getCallId();
+ message = msg;
+ }*/
+
+ /* Needed by ORMLite */
+ public HistoryText() {
+ }
+
+ public String getDirection() {
+ switch (direction) {
+ case TextMessage.direction.INCOMING:
+ return "INCOMING";
+ case TextMessage.direction.OUTGOING:
+ return "OUTGOING";
+ default:
+ return "CALL_TYPE_UNDETERMINED";
+ }
+ }
+
+ public Date getDate() {
+ return new Date(time);
+ }
+
+ public String getTimeString(String format) {
+ Timestamp stamp = new Timestamp(time); // in milliseconds
+ Date date = new Date(stamp.getTime());
+ SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.getDefault());
+ sdf.setTimeZone(TimeZone.getDefault());
+ return sdf.format(date);
+
+ }
+
+ public String getNumber() {
+ return number;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(id);
+ dest.writeLong(time);
+ dest.writeString(accountID);
+ dest.writeString(number);
+ dest.writeInt(direction);
+ dest.writeLong(contactID);
+ dest.writeString(callID);
+ dest.writeString(message);
+ }
+
+ public static final Creator<HistoryText> CREATOR = new Creator<HistoryText>() {
+ public HistoryText createFromParcel(Parcel in) {
+ return new HistoryText(in);
+ }
+
+ public HistoryText[] newArray(int size) {
+ return new HistoryText[size];
+ }
+ };
+
+ public HistoryText(Parcel in) {
+ id = in.readString();
+ time = in.readLong();
+ accountID = in.readString();
+ number = in.readString();
+ direction = in.readInt();
+ contactID = in.readLong();
+ callID = in.readString();
+ message = in.readString();
+ }
+
+ public boolean isIncoming() {
+ return direction == TextMessage.direction.INCOMING;
+ }
+
+ public String getCallId() {
+ return callID;
+ }
+}
diff --git a/ring-android/app/src/main/java/cx/ring/loaders/AccountsLoader.java b/ring-android/app/src/main/java/cx/ring/loaders/AccountsLoader.java
index 32c2cc7..b96b666 100644
--- a/ring-android/app/src/main/java/cx/ring/loaders/AccountsLoader.java
+++ b/ring-android/app/src/main/java/cx/ring/loaders/AccountsLoader.java
@@ -76,24 +76,23 @@
Map<String, String> state;
for (String id : accountIDs) {
+ details = (Map<String, String>) service.getAccountDetails(id);
+ state = (Map<String, String>) service.getVolatileAccountDetails(id);
if (id.contentEquals(ACCOUNT_IP2IP)) {
- details = (HashMap<String, String>) service.getAccountDetails(id);
- state = (Map<String, String>) service.getVolatileAccountDetails(id);
IP2IP = new Account(ACCOUNT_IP2IP, details, new ArrayList<Map<String, String>>(), state); // Empty credentials
//accounts.add(IP2IP);
continue;
}
- details = (Map<String, String>) service.getAccountDetails(id);
+
credentials = (ArrayList<Map<String, String>>) service.getCredentials(id);
- state = (Map<String, String>) service.getVolatileAccountDetails(id);
- for (Map.Entry<String, String> entry : state.entrySet()) {
+ /*for (Map.Entry<String, String> entry : state.entrySet()) {
Log.i(TAG, "state:" + entry.getKey() + " -> " + entry.getValue());
- }
+ }*/
Account tmp = new Account(id, details, credentials, state);
accounts.add(tmp);
- Log.i(TAG, "account:" + tmp.getAlias() + " " + tmp.isEnabled());
+ // Log.i(TAG, "account:" + tmp.getAlias() + " " + tmp.isEnabled());
}
} catch (RemoteException | NullPointerException e) {
diff --git a/ring-android/app/src/main/java/cx/ring/loaders/ContactsLoader.java b/ring-android/app/src/main/java/cx/ring/loaders/ContactsLoader.java
index 3482d34..fd71e80 100644
--- a/ring-android/app/src/main/java/cx/ring/loaders/ContactsLoader.java
+++ b/ring-android/app/src/main/java/cx/ring/loaders/ContactsLoader.java
@@ -36,24 +36,32 @@
import cx.ring.model.CallContact;
import android.content.AsyncTaskLoader;
+import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
+import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.SipAddress;
import android.provider.ContactsContract.Contacts;
+import android.util.Log;
-public class ContactsLoader extends AsyncTaskLoader<Bundle> {
-
-// private static final String TAG = ContactsLoader.class.getSimpleName();
+public class ContactsLoader extends AsyncTaskLoader<ContactsLoader.Result>
+{
+ private static final String TAG = ContactsLoader.class.getSimpleName();
+
+ public class Result {
+ public final ArrayList<CallContact> contacts = new ArrayList<>();
+ public final ArrayList<CallContact> starred = new ArrayList<>();
+ }
// These are the Contacts rows that we will retrieve.
- static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY, Contacts.STARRED };
+ static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { Contacts._ID, Contacts.LOOKUP_KEY, Contacts.DISPLAY_NAME, Contacts.PHOTO_ID, Contacts.STARRED };
static final String[] CONTACTS_PHONES_PROJECTION = new String[] { Phone.NUMBER, Phone.TYPE };
static final String[] CONTACTS_SIP_PROJECTION = new String[] { SipAddress.SIP_ADDRESS, SipAddress.TYPE };
- String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + " != '' ))";
+ static private final String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + " != '' ))";
Uri baseUri;
public ContactsLoader(Context context, Uri u) {
@@ -62,52 +70,55 @@
}
@Override
- public Bundle loadInBackground() {
- ArrayList<CallContact> contacts = new ArrayList<CallContact>();
- ArrayList<CallContact> starred = new ArrayList<CallContact>();
+ public Result loadInBackground() {
+ Result res = new Result();
- Cursor result = getContext().getContentResolver().query(baseUri, CONTACTS_SUMMARY_PROJECTION, select, null,
- Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
+ ContentResolver cr = getContext().getContentResolver();
+ Cursor result = cr.query(baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
+ if (result == null)
+ return res;
+
int iID = result.getColumnIndex(Contacts._ID);
+ int iKey = result.getColumnIndex(ContactsContract.Data.LOOKUP_KEY);
int iName = result.getColumnIndex(Contacts.DISPLAY_NAME);
int iPhoto = result.getColumnIndex(Contacts.PHOTO_ID);
int iStarred = result.getColumnIndex(Contacts.STARRED);
CallContact.ContactBuilder builder = CallContact.ContactBuilder.getInstance();
-
+
while (result.moveToNext()) {
- builder.startNewContact(result.getLong(iID), result.getString(iName), result.getLong(iPhoto));
+ long cid = result.getLong(iID);
+ builder.startNewContact(cid, result.getString(iKey), result.getString(iName), result.getLong(iPhoto));
-// Cursor cPhones = getContext().getContentResolver().query(Phone.CONTENT_URI, CONTACTS_PHONES_PROJECTION,
-// Phone.CONTACT_ID + " =" + result.getLong(iID), null, null);
+ Cursor cPhones = cr.query(Phone.CONTENT_URI, CONTACTS_PHONES_PROJECTION, Phone.CONTACT_ID + " =" + cid, null, null);
+ if (cPhones != null) {
+ while (cPhones.moveToNext()) {
+ builder.addPhoneNumber(cPhones.getString(cPhones.getColumnIndex(Phone.NUMBER)), cPhones.getInt(cPhones.getColumnIndex(Phone.TYPE)));
+ Log.w(TAG,"Phone:"+cPhones.getString(cPhones.getColumnIndex(Phone.NUMBER)));
+ }
+ cPhones.close();
+ }
-// while (cPhones.moveToNext()) {
-// builder.addPhoneNumber(cPhones.getString(cPhones.getColumnIndex(Phone.NUMBER)), cPhones.getInt(cPhones.getColumnIndex(Phone.TYPE)));
-//// Log.i(TAG,"Phone:"+cPhones.getString(cPhones.getColumnIndex(Phone.NUMBER)));
-// }
-// cPhones.close();
-//
-// Cursor cSip = getContext().getContentResolver().query(Phone.CONTENT_URI, CONTACTS_SIP_PROJECTION,
-// Phone.CONTACT_ID + "=" + result.getLong(iID), null, null);
-//
-// while (cSip.moveToNext()) {
-// builder.addSipNumber(cSip.getString(cSip.getColumnIndex(SipAddress.SIP_ADDRESS)), cSip.getInt(cSip.getColumnIndex(SipAddress.TYPE)));
-//// Log.i(TAG,"Phone:"+cSip.getString(cSip.getColumnIndex(SipAddress.SIP_ADDRESS)));
-// }
-// cSip.close();
+ //Cursor cSip = cr.query(Phone.CONTENT_URI, CONTACTS_SIP_PROJECTION, Phone.CONTACT_ID + "=" + cid, null, null);
+ Cursor cSip = cr.query(ContactsContract.Data.CONTENT_URI,
+ CONTACTS_SIP_PROJECTION,
+ ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?",
+ new String[]{String.valueOf(cid), ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE}, null);
+ if (cSip != null) {
+ while (cSip.moveToNext()) {
+ builder.addSipNumber(cSip.getString(cSip.getColumnIndex(SipAddress.SIP_ADDRESS)), cSip.getInt(cSip.getColumnIndex(SipAddress.TYPE)));
+ Log.w(TAG, "SIP Phone for " + cid + " :" + cSip.getString(cSip.getColumnIndex(SipAddress.SIP_ADDRESS)));
+ }
+ cSip.close();
+ }
- contacts.add(builder.build());
+ res.contacts.add(builder.build());
if (result.getInt(iStarred) == 1) {
- starred.add(builder.build());
+ res.starred.add(builder.build());
}
- }
-
+ }
result.close();
- Bundle toReturn = new Bundle();
-
- toReturn.putParcelableArrayList("Contacts", contacts);
- toReturn.putParcelableArrayList("Starred", starred);
- return toReturn;
+ return res;
}
}
diff --git a/ring-android/app/src/main/java/cx/ring/loaders/HistoryLoader.java b/ring-android/app/src/main/java/cx/ring/loaders/HistoryLoader.java
index b6712ea..7606f2f 100644
--- a/ring-android/app/src/main/java/cx/ring/loaders/HistoryLoader.java
+++ b/ring-android/app/src/main/java/cx/ring/loaders/HistoryLoader.java
@@ -63,7 +63,7 @@
@Override
public ArrayList<HistoryEntry> loadInBackground() {
- HashMap<String,HistoryEntry> historyEntries = new HashMap<String, HistoryEntry>();
+ HashMap<String,HistoryEntry> historyEntries = new HashMap<>();
try {
List<HistoryCall> list = historyManager.getAll();
@@ -77,11 +77,12 @@
ContactsContract.Contacts._ID + " = ?",
new String[]{String.valueOf(call.getContactID())}, null);
int iID = result.getColumnIndex(ContactsContract.Contacts._ID);
+ int iKey = result.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
int iName = result.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
int iPhoto = result.getColumnIndex(ContactsContract.Contacts.PHOTO_ID);
if (result.moveToFirst()) {
- builder.startNewContact(result.getLong(iID), result.getString(iName), result.getLong(iPhoto));
+ builder.startNewContact(result.getLong(iID), result.getString(iKey), result.getString(iName), result.getLong(iPhoto));
builder.addPhoneNumber(call.getNumber(), 0);
contact = builder.build();
} else {
diff --git a/ring-android/app/src/main/java/cx/ring/model/Bubble.java b/ring-android/app/src/main/java/cx/ring/model/Bubble.java
index 3cf6e8a..3387427 100644
--- a/ring-android/app/src/main/java/cx/ring/model/Bubble.java
+++ b/ring-android/app/src/main/java/cx/ring/model/Bubble.java
@@ -143,7 +143,7 @@
}
protected Bitmap getContactPhoto(Context context, CallContact contact, int size) {
- if (contact.getPhoto_id() > 0) {
+ if (contact.getPhotoId() > 0) {
return ContactPictureTask.loadContactPhoto(context.getContentResolver(), contact.getId());
} else {
return ContactPictureTask.decodeSampledBitmapFromResource(context.getResources(), R.drawable.ic_contact_picture, size, size);
diff --git a/ring-android/app/src/main/java/cx/ring/model/BubbleContact.java b/ring-android/app/src/main/java/cx/ring/model/BubbleContact.java
index e21f98a..7c0ce6e 100644
--- a/ring-android/app/src/main/java/cx/ring/model/BubbleContact.java
+++ b/ring-android/app/src/main/java/cx/ring/model/BubbleContact.java
@@ -38,7 +38,7 @@
public SipCall associated_call;
public BubbleContact(Context context, SipCall call, float x, float y, float size) {
- super(context, call.getmContact(), x, y, size);
+ super(context, call.getContact(), x, y, size);
associated_call = call;
}
@@ -62,7 +62,7 @@
@Override
public String getName() {
- return associated_call.getmContact().getmDisplayName();
+ return associated_call.getContact().getDisplayName();
}
@Override
diff --git a/ring-android/app/src/main/java/cx/ring/model/BubblesView.java b/ring-android/app/src/main/java/cx/ring/model/BubblesView.java
index 8b465dd..890a2be 100644
--- a/ring-android/app/src/main/java/cx/ring/model/BubblesView.java
+++ b/ring-android/app/src/main/java/cx/ring/model/BubblesView.java
@@ -97,7 +97,7 @@
SurfaceHolder holder = getHolder();
holder.addCallback(this);
- this.setZOrderOnTop(true); // necessary
+ //this.setZOrderOnTop(true); // necessary
holder.setFormat(PixelFormat.TRANSLUCENT);
// create thread only; it's started in surfaceCreated()
createThread();
@@ -313,9 +313,9 @@
} else
canvas.drawBitmap(ic_bg, null, a.getBounds(showed * 2.f, b.getPos(), showed), action_paint);
canvas.drawBitmap(a.getBitmap(), null, a.getBounds(showed, b.getPos(), showed), null);
- float dist_raw = FloatMath.sqrt((b.pos.x - a.pos.x) * (b.pos.x - a.pos.x) + (b.pos.y - a.pos.y) * (b.pos.y - a.pos.y));
- float dist_min = a.radius + b.radius + bubbleActionTextDistMin;
- float dist = Math.max(0, dist_raw - dist_min);
+ double dist_raw = Math.sqrt((b.pos.x - a.pos.x) * (b.pos.x - a.pos.x) + (b.pos.y - a.pos.y) * (b.pos.y - a.pos.y));
+ double dist_min = a.radius + b.radius + bubbleActionTextDistMin;
+ double dist = Math.max(0, dist_raw - dist_min);
if (actions.enabled && dist < dist_range) {
white_name_paint.setAlpha(255 - (int) (255 * dist / dist_range));
canvas.drawText(a.name, a.getBounds().centerX(), a.getBounds().centerY() - a.radius * 2.2f, white_name_paint);
@@ -343,7 +343,7 @@
return true;
if (action == MotionEvent.ACTION_UP) {
- if (thread.suspendFlag) {
+ if (thread != null && thread.suspendFlag) {
Log.i(TAG, "Relaunch drawing thread");
thread.setPaused(false);
}
@@ -354,7 +354,7 @@
}
}
dragging_bubble = false;
- } else if (action != MotionEvent.ACTION_DOWN && !isDraggingBubble() && !thread.suspendFlag) {
+ } else if (action != MotionEvent.ACTION_DOWN && !isDraggingBubble() && thread != null && !thread.suspendFlag) {
Log.i(TAG, "Not dragging thread should be stopped");
thread.setPaused(true);
}
diff --git a/ring-android/app/src/main/java/cx/ring/model/CallContact.java b/ring-android/app/src/main/java/cx/ring/model/CallContact.java
index c067a75..e11861c 100644
--- a/ring-android/app/src/main/java/cx/ring/model/CallContact.java
+++ b/ring-android/app/src/main/java/cx/ring/model/CallContact.java
@@ -32,6 +32,9 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import android.content.ContentResolver;
import android.database.Cursor;
@@ -39,29 +42,70 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.ContactsContract.Profile;
+import android.support.annotation.NonNull;
+
+import cx.ring.client.ConversationActivity;
+import cx.ring.service.LocalService;
public class CallContact implements Parcelable {
+ static public final Pattern ANGLE_BRACKETS_PATTERN = Pattern.compile("(?:[^<>]+<)?([^<>]+)>?\\s*");
+ public static final Pattern RING_ID_PATTERN = Pattern.compile("^\\s*(?:ring(?:[\\s\\:]+))?(\\p{XDigit}{40})(?:@ring\\.dht)?\\s*$", Pattern.CASE_INSENSITIVE);
public static int DEFAULT_ID = 0;
private long id;
+ private String key;
private String mDisplayName;
private long photo_id;
- private ArrayList<Phone> phones, sip_phones;
+ private ArrayList<Phone> phones/*, sip_phones*/;
private String mEmail;
private boolean isUser;
- private WeakReference<Bitmap> contact_photo = new WeakReference<Bitmap>(null);
+ private WeakReference<Bitmap> contact_photo = new WeakReference<>(null);
- private CallContact(long cID, String displayName, long photoID, ArrayList<Phone> p, ArrayList<Phone> sip, String mail, boolean user) {
+ private CallContact(long cID, String k, String displayName, long photoID, ArrayList<Phone> p, String mail, boolean user) {
id = cID;
+ key = k;
mDisplayName = displayName;
phones = p;
- sip_phones = sip;
mEmail = mail;
photo_id = photoID;
isUser = user;
}
+ private static String nobracketsNumber(@NonNull String number) {
+ Matcher m = ANGLE_BRACKETS_PATTERN.matcher(number);
+ if (m.find())
+ return m.group(1);
+ return number;
+ }
+
+ public static String canonicalNumber(@NonNull String number) {
+ number = nobracketsNumber(number);
+ Matcher m = RING_ID_PATTERN.matcher(number);
+ if (m.find())
+ return "ring:"+m.group(1);
+ return number;
+ }
+
+ public ArrayList<String> getIds() {
+ ArrayList<String> ret = new ArrayList<>(1+phones.size());
+ if (id != -1)
+ ret.add("c:" + Long.toHexString(id));
+ for (Phone p : phones)
+ ret.add(canonicalNumber(p.getNumber()));
+ return ret;
+ }
+
+ public static long contactIdFromId(String id) {
+ if (!id.startsWith("c:"))
+ return -1;
+ try {
+ return Long.parseLong(id.substring(2), 16);
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
public CallContact(Parcel in) {
readFromParcel(in);
}
@@ -70,15 +114,21 @@
return id;
}
- public String getmDisplayName() {
- return mDisplayName;
+ public String getDisplayName() {
+ if (!mDisplayName.isEmpty())
+ return mDisplayName;
+ if (!phones.isEmpty())
+ return phones.get(0).getNumber();
+ /*if (!sip_phones.isEmpty())
+ return sip_phones.get(0).getNumber();*/
+ return "";
}
- public long getPhoto_id() {
+ public long getPhotoId() {
return photo_id;
}
- public void setPhoto_id(long photo_id) {
+ public void setPhotoId(long photo_id) {
this.photo_id = photo_id;
}
@@ -90,53 +140,56 @@
this.phones = phones;
}
- public ArrayList<Phone> getSip_phones() {
- return sip_phones;
- }
-
- public void setSip_phones(ArrayList<Phone> sip_phones) {
- this.sip_phones = sip_phones;
- }
-
- public Phone getSipPhone() {
- if (sip_phones.size() > 0) {
- return sip_phones.get(0);
- }
- if (phones.size() > 0) {
- return phones.get(0);
- }
- return null;
- }
-
- public String getmEmail() {
+ public String getEmail() {
return mEmail;
}
- public void setmEmail(String mEmail) {
+ public void setEmail(String mEmail) {
this.mEmail = mEmail;
}
+ public boolean hasNumber(String number) {
+ if (number == null || number.isEmpty())
+ return false;
+ number = canonicalNumber(number);
+ for (Phone p : phones)
+ if (canonicalNumber(p.getNumber()).equals(number))
+ return true;
+ return false;
+ }
+
@Override
public String toString() {
return mDisplayName;
}
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.mDisplayName = displayName;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
public static class ContactBuilder {
long contactID;
+ String key;
String contactName;
long contactPhoto;
ArrayList<Phone> phones;
- ArrayList<Phone> sip;
String contactMail;
- public ContactBuilder startNewContact(long id, String displayName, long photo_id) {
+ public ContactBuilder startNewContact(long id, String k, String displayName, long photo_id) {
contactID = id;
-
+ key = k;
contactName = displayName;
contactPhoto = photo_id;
- phones = new ArrayList<Phone>();
- sip = new ArrayList<Phone>();
+ phones = new ArrayList<>();
return this;
}
@@ -146,12 +199,12 @@
}
public ContactBuilder addSipNumber(String num, int type) {
- sip.add(new Phone(num, type));
+ phones.add(new Phone(num, type, NumberType.SIP));
return this;
}
public CallContact build() {
- return new CallContact(contactID, contactName, contactPhoto, phones, sip, contactMail, false);
+ return new CallContact(contactID, key, contactName, contactPhoto, phones, contactMail, false);
}
public static ContactBuilder getInstance() {
@@ -159,25 +212,30 @@
}
public static CallContact buildUnknownContact(String to) {
- ArrayList<Phone> phones = new ArrayList<Phone>();
+ ArrayList<Phone> phones = new ArrayList<>();
phones.add(new Phone(to, 0));
- return new CallContact(-1, to, 0, phones, new ArrayList<CallContact.Phone>(), "", false);
+ return new CallContact(-1, null, to, 0, phones, "", false);
+ }
+ public static CallContact buildUnknownContact(String to, int type) {
+ ArrayList<Phone> phones = new ArrayList<>();
+ phones.add(new Phone(to, type));
+ return new CallContact(-1, null, to, 0, phones, "", false);
}
public static CallContact buildUserContact(ContentResolver cr) {
- String[] mProjection = new String[] { Profile._ID, Profile.DISPLAY_NAME_PRIMARY, Profile.PHOTO_ID };
+ String[] mProjection = new String[] { Profile._ID, Profile.LOOKUP_KEY, Profile.DISPLAY_NAME_PRIMARY, Profile.PHOTO_ID };
Cursor mProfileCursor = cr.query(Profile.CONTENT_URI, mProjection, null, null, null);
CallContact result;
if (mProfileCursor.getCount() > 0) {
mProfileCursor.moveToFirst();
+ String key = mProfileCursor.getString(mProfileCursor.getColumnIndex(Profile.LOOKUP_KEY));
String displayName = mProfileCursor.getString(mProfileCursor.getColumnIndex(Profile.DISPLAY_NAME_PRIMARY));
- result = new CallContact(mProfileCursor.getLong(mProfileCursor.getColumnIndex(Profile._ID)), displayName,
- mProfileCursor.getLong(mProfileCursor.getColumnIndex(Profile.PHOTO_ID)), new ArrayList<Phone>(),
- new ArrayList<CallContact.Phone>(), "", true);
+ result = new CallContact(mProfileCursor.getLong(mProfileCursor.getColumnIndex(Profile._ID)), key, displayName,
+ mProfileCursor.getLong(mProfileCursor.getColumnIndex(Profile.PHOTO_ID)), new ArrayList<Phone>(), "", true);
} else {
- result = new CallContact(-1, "Me", 0, new ArrayList<Phone>(), new ArrayList<CallContact.Phone>(), "", true);
+ result = new CallContact(-1, null, "Me", 0, new ArrayList<Phone>(), "", true);
}
mProfileCursor.close();
return result;
@@ -193,26 +251,22 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(id);
+ dest.writeString(key);
dest.writeString(mDisplayName);
dest.writeLong(photo_id);
dest.writeTypedList(phones);
-
- dest.writeTypedList(sip_phones);
-
dest.writeString(mEmail);
dest.writeByte((byte) (isUser ? 1 : 0));
}
private void readFromParcel(Parcel in) {
-
id = in.readLong();
+ key = in.readString();
mDisplayName = in.readString();
photo_id = in.readLong();
- phones = new ArrayList<CallContact.Phone>();
- sip_phones = new ArrayList<CallContact.Phone>();
+ phones = new ArrayList<>();
in.readTypedList(phones, Phone.CREATOR);
- in.readTypedList(sip_phones, Phone.CREATOR);
mEmail = in.readString();
isUser = in.readByte() == 1;
}
@@ -229,15 +283,48 @@
}
};
+ public enum NumberType {
+ UNKNOWN(0),
+ TEL(1),
+ SIP(2),
+ IP(2),
+ RING(3);
+
+ private final int type;
+ NumberType(int t) {
+ type = t;
+ }
+ private static final NumberType[] VALS = NumberType.values();
+ public static NumberType fromInteger(int _id)
+ {
+ for (NumberType v : VALS)
+ if (v.type == _id)
+ return v;
+ return UNKNOWN;
+ }
+ /*public static NumberType guess(String num) {
+ String canon = canonicalNumber(num);
+ Matcher m = URI_NUMBER_REGEX.matcher(canon);
+
+ return UNKNOWN;
+ }*/
+ }
+
public static class Phone implements Parcelable {
-
- int type;
+ NumberType ntype;
String number;
+ int category; // Home, work, custom etc.
- public Phone(String num, int ty) {
- type = ty;
+ public Phone(String num, int cat) {
+ ntype = NumberType.UNKNOWN;
+ category = cat;
number = num;
}
+ public Phone(String num, int cat, NumberType nty) {
+ ntype = nty;
+ number = num;
+ category = cat;
+ }
public Phone(Parcel in) {
readFromParcel(in);
@@ -250,13 +337,15 @@
@Override
public void writeToParcel(Parcel dest, int arg1) {
- dest.writeInt(type);
+ dest.writeInt(ntype.type);
dest.writeString(number);
+ dest.writeInt(category);
}
private void readFromParcel(Parcel in) {
- type = in.readInt();
+ ntype = NumberType.fromInteger(in.readInt());
number = in.readString();
+ category = in.readInt();
}
public static final Parcelable.Creator<Phone> CREATOR = new Parcelable.Creator<Phone>() {
@@ -271,12 +360,12 @@
}
};
- public int getType() {
- return type;
+ public NumberType getType() {
+ return ntype;
}
public void setType(int type) {
- this.type = type;
+ this.ntype = NumberType.fromInteger(type);
}
public String getNumber() {
@@ -289,13 +378,12 @@
}
- public void addPhoneNumber(String tel, int type) {
- phones.add(new Phone(tel, type));
+ public void addPhoneNumber(String tel, int car) {
+ phones.add(new Phone(tel, car));
}
-
- public void addSipNumber(String tel, int type) {
- sip_phones.add(new Phone(tel, type));
+ public void addNumber(String tel, int cat, NumberType type) {
+ phones.add(new Phone(tel, cat, type));
}
@@ -314,7 +402,7 @@
}
public void setPhoto(Bitmap externalBMP) {
- contact_photo = new WeakReference<Bitmap>(externalBMP);
+ contact_photo = new WeakReference<>(externalBMP);
}
/**
diff --git a/ring-android/app/src/main/java/cx/ring/model/Conference.java b/ring-android/app/src/main/java/cx/ring/model/Conference.java
index db027f6..a604bb1 100644
--- a/ring-android/app/src/main/java/cx/ring/model/Conference.java
+++ b/ring-android/app/src/main/java/cx/ring/model/Conference.java
@@ -36,13 +36,20 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
public class Conference implements Parcelable {
private String id;
private int mConfState;
private ArrayList<SipCall> participants;
private boolean recording;
- private ArrayList<SipMessage> messages;
+ private ArrayList<TextMessage> messages;
+
+ public int notificationId;
+
+ private final static Random rand = new Random();
public static String DEFAULT_ID = "-1";
@@ -54,13 +61,13 @@
participants.remove(toRemove);
}
- public boolean useSecureLayer() {
+ /*public boolean useSecureLayer() {
for(SipCall call : participants){
if(call.getAccount().useSecureLayer())
return true;
}
return false;
- }
+ }*/
public interface state {
int ACTIVE_ATTACHED = 0;
@@ -104,8 +111,8 @@
public void writeToParcel(Parcel out, int flags) {
out.writeString(id);
out.writeInt(mConfState);
- ArrayList<SipCall> normal_calls = new ArrayList<SipCall>();
- ArrayList<SecureSipCall> secure_calls = new ArrayList<SecureSipCall>();
+ ArrayList<SipCall> normal_calls = new ArrayList<>();
+ ArrayList<SecureSipCall> secure_calls = new ArrayList<>();
for(SipCall part : participants){
if(part instanceof SecureSipCall)
@@ -117,6 +124,7 @@
out.writeTypedList(normal_calls);
out.writeByte((byte) (recording ? 1 : 0));
out.writeTypedList(messages);
+ out.writeInt(notificationId);
}
public static final Parcelable.Creator<Conference> CREATOR = new Parcelable.Creator<Conference>() {
@@ -131,36 +139,38 @@
private Conference(Parcel in) {
- participants = new ArrayList<SipCall>();
+ participants = new ArrayList<>();
id = in.readString();
mConfState = in.readInt();
- ArrayList<SecureSipCall> tmp = new ArrayList<SecureSipCall>();
+ ArrayList<SecureSipCall> tmp = new ArrayList<>();
in.readTypedList(tmp, SecureSipCall.CREATOR);
in.readTypedList(participants, SipCall.CREATOR);
participants.addAll(tmp);
recording = in.readByte() == 1;
- messages = new ArrayList<SipMessage>();
- in.readTypedList(messages, SipMessage.CREATOR);
+ messages = new ArrayList<>();
+ in.readTypedList(messages, TextMessage.CREATOR);
+ notificationId = in.readInt();
}
public Conference(SipCall call) {
this(DEFAULT_ID);
participants.add(call);
+ notificationId = rand.nextInt();
}
public Conference(String cID) {
id = cID;
- participants = new ArrayList<SipCall>();
+ participants = new ArrayList<>();
recording = false;
- messages = new ArrayList<SipMessage>();
+ messages = new ArrayList<>();
}
public Conference(Conference c) {
id = c.id;
mConfState = c.mConfState;
- participants = new ArrayList<SipCall>(c.participants);
+ participants = new ArrayList<>(c.participants);
recording = c.recording;
- messages = new ArrayList<SipMessage>();
+ messages = new ArrayList<>();
}
public String getId() {
@@ -275,11 +285,11 @@
return participants.size() == 1 && participants.get(0).isOngoing() || participants.size() > 1;
}
- public ArrayList<SipMessage> getMessages() {
+ public ArrayList<TextMessage> getMessages() {
return messages;
}
- public void addSipMessage(SipMessage sipMessage) {
+ public void addSipMessage(TextMessage sipMessage) {
messages.add(sipMessage);
}
diff --git a/ring-android/app/src/main/java/cx/ring/model/Conversation.java b/ring-android/app/src/main/java/cx/ring/model/Conversation.java
new file mode 100644
index 0000000..db311cd
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/model/Conversation.java
@@ -0,0 +1,232 @@
+package cx.ring.model;
+
+import android.database.ContentObservable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import cx.ring.history.HistoryCall;
+import cx.ring.history.HistoryEntry;
+import cx.ring.history.HistoryText;
+import cx.ring.model.account.Account;
+
+public class Conversation extends ContentObservable implements Parcelable
+{
+ static final String TAG = Conversation.class.getSimpleName();
+
+ public CallContact contact;
+ //private HistoryEntry history;
+ /** accountId -> histroy entries */
+ final private Map<String, HistoryEntry> history = new HashMap<>();
+
+ //private Conference current_call = null;
+ public final ArrayList<Conference> current_calls;
+
+ public String getLastNumberUsed(String accountID) {
+ HistoryEntry he = history.get(accountID);
+ if (he == null)
+ return null;
+ return he.getLastNumberUsed();
+ }
+
+ public Conference getConference(String id) {
+ for (Conference c : current_calls)
+ if (c.getId().contentEquals(id) || c.getCallById(id) != null)
+ return c;
+ return null;
+ }
+
+ public Pair<HistoryEntry, HistoryCall> findHistoryByCallId(String id) {
+ for (HistoryEntry e : history.values()) {
+ for (HistoryCall c : e.getCalls().values()) {
+ if (c.getCallId().equals(id))
+ return new Pair<>(e, c);
+ }
+ }
+ return null;
+ }
+
+ public class ConversationElement {
+ public HistoryCall call = null;
+ public TextMessage text = null;
+ public ConversationElement(HistoryCall c) {
+ call = c;
+ }
+ public ConversationElement(TextMessage t) {
+ text = t;
+ }
+ public long getDate() {
+ if (text != null)
+ return text.getTimestamp();
+ else if (call != null)
+ return call.call_start;
+ return 0;
+ }
+ }
+
+ public Conversation(CallContact c) {
+ contact = c;
+ current_calls = new ArrayList<>();
+ }
+
+ public static final Creator<Conversation> CREATOR = new Creator<Conversation>() {
+ @Override
+ public Conversation createFromParcel(Parcel in) {
+ return new Conversation(in);
+ }
+
+ @Override
+ public Conversation[] newArray(int size) {
+ return new Conversation[size];
+ }
+ };
+
+ public CallContact getContact() {
+ return contact;
+ }
+
+ public Date getLastInteraction() {
+ if (!current_calls.isEmpty()) {
+ return new Date();
+ }
+ Date d = new Date(0);
+
+ //for (Map.Entry<String, HistoryEntry> e : history.entrySet()) {
+ for (HistoryEntry e : history.values()) {
+ Date nd = e.getLastInteraction();
+ if (d.compareTo(nd) < 0)
+ d = nd;
+ }
+ return d;
+ }
+
+ public void addHistoryCall(HistoryCall c) {
+ String accountId = c.getAccountID();
+ if (history.containsKey(accountId))
+ history.get(accountId).addHistoryCall(c, contact);
+ else {
+ HistoryEntry e = new HistoryEntry(accountId, contact);
+ e.addHistoryCall(c, contact);
+ history.put(accountId, e);
+ }
+ }
+ public void addTextMessage(TextMessage txt) {
+ if (txt.getCallId() != null && !txt.getCallId().isEmpty()) {
+ Conference conf = getConference(txt.getCallId());
+ if (conf == null)
+ return;
+ conf.addSipMessage(txt);
+ }// else {
+ if (txt.getContact() == null)
+ txt.setContact(contact);
+ String accountId = txt.getAccount();
+ if (history.containsKey(accountId))
+ history.get(accountId).addTextMessage(txt);
+ else {
+ HistoryEntry e = new HistoryEntry(accountId, contact);
+ e.addTextMessage(txt);
+ history.put(accountId, e);
+ }
+ //}
+ }
+
+ /*public HistoryEntry getHistory(String account_id) {
+ return history.get(account_id);
+ }*/
+
+ public ArrayList<ConversationElement> getHistory() {
+ ArrayList<ConversationElement> all = new ArrayList<>();
+ for (HistoryEntry e : history.values()) {
+ for (HistoryCall c : e.getCalls().values())
+ all.add(new ConversationElement(c));
+ for (TextMessage t : e.getTextMessages().values())
+ all.add(new ConversationElement(t));
+ }
+ Collections.sort(all, new Comparator<ConversationElement>() {
+ @Override
+ public int compare(ConversationElement lhs, ConversationElement rhs) {
+ return (int)((lhs.getDate() - rhs.getDate())/1000l);
+ }
+ });
+ return all;
+ }
+
+ public Set<String> getAccountsUsed() {
+ return history.keySet();
+ }
+
+ public String getLastAccountUsed() {
+ String last = null;
+ Date d = new Date(0);
+ for (Map.Entry<String, HistoryEntry> e : history.entrySet()) {
+ Date nd = e.getValue().getLastInteraction();
+ if (d.compareTo(nd) < 0) {
+ d = nd;
+ last = e.getKey();
+ }
+ }
+ return last;
+ }
+
+ public Conference getCurrentCall() {
+ if (current_calls.isEmpty())
+ return null;
+ return current_calls.get(0);
+ }
+ public void setCurrentCall(Conference c) {
+ current_calls.add(c);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(contact, flags);
+ //dest.writeParcelable(current_call, flags);
+ dest.writeList(current_calls);
+ dest.writeList(new ArrayList<>(history.values()));
+ dest.writeList(new ArrayList<>(history.keySet()));
+ }
+
+ protected Conversation(Parcel in) {
+ contact = in.readParcelable(CallContact.class.getClassLoader());
+ //current_call = in.readParcelable(Conference.class.getClassLoader());
+ current_calls = in.readArrayList(Conference.class.getClassLoader());
+
+ ArrayList<HistoryEntry> values = new ArrayList<>();
+ in.readList(values, HistoryEntry.class.getClassLoader());
+ ArrayList<String> keys = new ArrayList<>();
+ in.readList(keys, String.class.getClassLoader());
+ for (int i = 0; i < keys.size(); ++i)
+ history.put(keys.get(i), values.get(i));
+ }
+
+ public ArrayList<TextMessage> getTextMessages() {
+ ArrayList<TextMessage> texts = new ArrayList<>();
+ for (HistoryEntry h : history.values()) {
+ texts.addAll(h.getTextMessages().values());
+ }
+ Collections.sort(texts, new Comparator<TextMessage>() {
+ @Override
+ public int compare(TextMessage lhs, TextMessage rhs) {
+ return (int)((lhs.getTimestamp() - rhs.getTimestamp())/1000l);
+ }
+ });
+ return texts;
+ }
+
+}
diff --git a/ring-android/app/src/main/java/cx/ring/model/SecureSipCall.java b/ring-android/app/src/main/java/cx/ring/model/SecureSipCall.java
index c606cc1..8ca7ef1 100644
--- a/ring-android/app/src/main/java/cx/ring/model/SecureSipCall.java
+++ b/ring-android/app/src/main/java/cx/ring/model/SecureSipCall.java
@@ -55,10 +55,10 @@
private boolean isInitialized;
- public SecureSipCall(SipCall call) {
+ public SecureSipCall(SipCall call, String keyExchange) {
super(call);
isInitialized = false;
- String keyExchange = getAccount().getSrtpDetails().getDetailString(AccountDetailSrtp.CONFIG_SRTP_KEY_EXCHANGE);
+ //String keyExchange = getAccount().getSrtpDetails().getDetailString(AccountDetailSrtp.CONFIG_SRTP_KEY_EXCHANGE);
/*if (keyExchange.contentEquals("zrtp")) {
mSecureLayerUsed = SecureLayer.ZRTP_LAYER;
} else */if (keyExchange.contentEquals("sdes")) {
@@ -124,7 +124,7 @@
}
/*
- * returns what state should be visible during call
+ * returns what State should be visible during call
*/
public int displayModule() {
/*if (isInitialized) {
diff --git a/ring-android/app/src/main/java/cx/ring/model/SipCall.java b/ring-android/app/src/main/java/cx/ring/model/SipCall.java
index 5524f20..0aa9c51 100644
--- a/ring-android/app/src/main/java/cx/ring/model/SipCall.java
+++ b/ring-android/app/src/main/java/cx/ring/model/SipCall.java
@@ -35,7 +35,6 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
-import cx.ring.model.account.Account;
public class SipCall implements Parcelable {
@@ -43,24 +42,34 @@
public static String ACCOUNT = "account";
public static String CONTACT = "contact";
public static String TYPE = "type";
- public static String STATE = "state";
+ public static String STATE = "State";
+ public static String NUMBER = "number";
private static final String TAG = SipCall.class.getSimpleName();
private String mCallID = "";
- private Account mAccount = null;
+ private String mAccount = "";
private CallContact mContact = null;
+ private String mNumber = "";
private boolean isRecording = false;
private long timestampStart_ = 0;
private long timestampEnd_ = 0;
private int mCallType;
- private int mCallState = state.CALL_STATE_NONE;
+ private int mCallState = State.NONE;
+
+ public SipCall(String id, String account, String number, int direction) {
+ mCallID = id;
+ mAccount = account;
+ mNumber = number;
+ mCallType = direction;
+ }
public SipCall(SipCall call) {
mCallID = call.mCallID;
mAccount = call.mAccount;
mContact = call.mContact;
+ mNumber = call.mNumber;
isRecording = call.isRecording;
timestampStart_ = call.timestampStart_;
timestampEnd_ = call.timestampEnd_;
@@ -75,10 +84,10 @@
*/
protected SipCall(Parcel in) {
-
mCallID = in.readString();
- mAccount = in.readParcelable(Account.class.getClassLoader());
+ mAccount = in.readString();
mContact = in.readParcelable(CallContact.class.getClassLoader());
+ mNumber = in.readString();
isRecording = in.readByte() == 1;
mCallType = in.readInt();
mCallState = in.readInt();
@@ -88,14 +97,11 @@
public SipCall(Bundle args) {
mCallID = args.getString(ID);
- mAccount = args.getParcelable(ACCOUNT);
+ mAccount = args.getString(ACCOUNT);
mCallType = args.getInt(TYPE);
mCallState = args.getInt(STATE);
mContact = args.getParcelable(CONTACT);
- }
-
- public long getTimestampEnd_() {
- return timestampEnd_;
+ mNumber = args.getString(NUMBER);
}
public String getRecordPath() {
@@ -109,29 +115,29 @@
public Bundle getBundle() {
Bundle args = new Bundle();
args.putString(SipCall.ID, mCallID);
- args.putParcelable(SipCall.ACCOUNT, mAccount);
+ args.putString(SipCall.ACCOUNT, mAccount);
args.putInt(SipCall.STATE, mCallState);
args.putInt(SipCall.TYPE, mCallType);
args.putParcelable(SipCall.CONTACT, mContact);
+ args.putString(SipCall.NUMBER, mNumber);
return args;
}
-
- public interface direction {
- int CALL_TYPE_INCOMING = 1;
- int CALL_TYPE_OUTGOING = 2;
+ public interface Direction {
+ int INCOMING = 1;
+ int OUTGOING = 2;
}
- public interface state {
- int CALL_STATE_NONE = 0;
- int CALL_STATE_CONNECTING = 1;
- int CALL_STATE_RINGING = 2;
- int CALL_STATE_CURRENT = 3;
- int CALL_STATE_HUNGUP = 4;
- int CALL_STATE_BUSY = 5;
- int CALL_STATE_FAILURE = 6;
- int CALL_STATE_HOLD = 7;
- int CALL_STATE_UNHOLD = 8;
+ public interface State {
+ int NONE = 0;
+ int CONNECTING = 1;
+ int RINGING = 2;
+ int CURRENT = 3;
+ int HUNGUP = 4;
+ int BUSY = 5;
+ int FAILURE = 6;
+ int HOLD = 7;
+ int UNHOLD = 8;
}
@Override
@@ -141,11 +147,10 @@
@Override
public void writeToParcel(Parcel out, int flags) {
-
out.writeString(mCallID);
- out.writeParcelable(mAccount, 0);
-
+ out.writeString(mAccount);
out.writeParcelable(mContact, 0);
+ out.writeString(mNumber);
out.writeByte((byte) (isRecording ? 1 : 0));
out.writeInt(mCallType);
out.writeInt(mCallState);
@@ -171,32 +176,36 @@
return mCallID;
}
- public long getTimestampStart_() {
+ public long getTimestampStart() {
return timestampStart_;
}
- public void setTimestampStart_(long timestampStart_) {
- this.timestampStart_ = timestampStart_;
+ public void setTimestampStart(long timestampStart) {
+ this.timestampStart_ = timestampStart;
}
- public void setTimestampEnd_(long timestampEnd_) {
- this.timestampEnd_ = timestampEnd_;
+ public long getTimestampEnd() {
+ return timestampEnd_;
}
- public void setAccount(Account account) {
+ public void setTimestampEnd(long timestampEnd) {
+ this.timestampEnd_ = timestampEnd;
+ }
+
+ public void setAccount(String account) {
mAccount = account;
}
- public Account getAccount() {
+ public String getAccount() {
return mAccount;
}
public String getCallTypeString() {
switch (mCallType) {
- case direction.CALL_TYPE_INCOMING:
- return "CALL_TYPE_INCOMING";
- case direction.CALL_TYPE_OUTGOING:
- return "CALL_TYPE_OUTGOING";
+ case Direction.INCOMING:
+ return "INCOMING";
+ case Direction.OUTGOING:
+ return "OUTGOING";
default:
return "CALL_TYPE_UNDETERMINED";
}
@@ -206,37 +215,49 @@
mCallState = callState;
}
- public CallContact getmContact() {
+ public void setContact(CallContact c) {
+ mContact = c;
+ }
+
+ public CallContact getContact() {
return mContact;
}
+ public void setNumber(String n) {
+ mNumber = n;
+ }
+
+ public String getNumber() {
+ return mNumber;
+ }
+
public String getCallStateString() {
String text_state;
switch (mCallState) {
- case state.CALL_STATE_NONE:
+ case State.NONE:
text_state = "NONE";
break;
- case state.CALL_STATE_RINGING:
+ case State.RINGING:
text_state = "RINGING";
break;
- case state.CALL_STATE_CURRENT:
+ case State.CURRENT:
text_state = "CURRENT";
break;
- case state.CALL_STATE_HUNGUP:
+ case State.HUNGUP:
text_state = "HUNGUP";
break;
- case state.CALL_STATE_BUSY:
+ case State.BUSY:
text_state = "BUSY";
break;
- case state.CALL_STATE_FAILURE:
+ case State.FAILURE:
text_state = "FAILURE";
break;
- case state.CALL_STATE_HOLD:
+ case State.HOLD:
text_state = "HOLD";
break;
- case state.CALL_STATE_UNHOLD:
+ case State.UNHOLD:
text_state = "UNHOLD";
break;
default:
@@ -256,7 +277,7 @@
public void printCallInfo() {
Log.i(TAG, "CallInfo: CallID: " + mCallID);
- Log.i(TAG, " AccountID: " + mAccount.getAccountID());
+ Log.i(TAG, " AccountID: " + mAccount);
Log.i(TAG, " CallState: " + mCallState);
Log.i(TAG, " CallType: " + mCallType);
}
@@ -270,29 +291,29 @@
}
public boolean isOutGoing() {
- return mCallType == direction.CALL_TYPE_OUTGOING;
+ return mCallType == Direction.OUTGOING;
}
public boolean isRinging() {
- return mCallState == state.CALL_STATE_RINGING || mCallState == state.CALL_STATE_NONE;
+ return mCallState == State.RINGING || mCallState == State.NONE;
}
public boolean isIncoming() {
- return mCallType == direction.CALL_TYPE_INCOMING;
+ return mCallType == Direction.INCOMING;
}
public boolean isOngoing() {
- return !(mCallState == state.CALL_STATE_RINGING || mCallState == state.CALL_STATE_NONE || mCallState == state.CALL_STATE_FAILURE
- || mCallState == state.CALL_STATE_BUSY || mCallState == state.CALL_STATE_HUNGUP);
+ return !(mCallState == State.RINGING || mCallState == State.NONE || mCallState == State.FAILURE
+ || mCallState == State.BUSY || mCallState == State.HUNGUP);
}
public boolean isOnHold() {
- return mCallState == state.CALL_STATE_HOLD;
+ return mCallState == State.HOLD;
}
public boolean isCurrent() {
- return mCallState == state.CALL_STATE_CURRENT;
+ return mCallState == State.CURRENT;
}
diff --git a/ring-android/app/src/main/java/cx/ring/model/SipMessage.aidl b/ring-android/app/src/main/java/cx/ring/model/SipMessage.aidl
deleted file mode 100644
index 0729146..0000000
--- a/ring-android/app/src/main/java/cx/ring/model/SipMessage.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package cx.ring.model;
-
-
-parcelable SipMessage;
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/model/SipMessage.java b/ring-android/app/src/main/java/cx/ring/model/SipMessage.java
deleted file mode 100644
index f8a43cc..0000000
--- a/ring-android/app/src/main/java/cx/ring/model/SipMessage.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2004-2014 Savoir-Faire Linux Inc.
- *
- * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Additional permission under GNU GPL version 3 section 7:
- *
- * If you modify this program, or any covered work, by linking or
- * combining it with the OpenSSL project's OpenSSL library (or a
- * modified version of that library), containing parts covered by the
- * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
- * grants you additional permission to convey the resulting work.
- * Corresponding Source for a non-source form of such a combination
- * shall include the source code for the parts of OpenSSL used as well
- * as that of the covered work.
- */
-package cx.ring.model;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public class SipMessage implements Parcelable {
- public boolean left;
- public String comment;
-
- public SipMessage(boolean left, String comment) {
- super();
- this.left = left;
- this.comment = comment;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeByte((byte) (left ? 1 : 0));
- out.writeString(comment);
- }
-
- public static final Parcelable.Creator<SipMessage> CREATOR = new Parcelable.Creator<SipMessage>() {
- public SipMessage createFromParcel(Parcel in) {
- return new SipMessage(in);
- }
-
- public SipMessage[] newArray(int size) {
- return new SipMessage[size];
- }
- };
-
- private SipMessage(Parcel in) {
- left = (in.readByte() == 1);
- comment = in.readString();
- }
-
-}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/model/TextMessage.aidl b/ring-android/app/src/main/java/cx/ring/model/TextMessage.aidl
new file mode 100644
index 0000000..1af4f25
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/model/TextMessage.aidl
@@ -0,0 +1,4 @@
+package cx.ring.model;
+
+
+parcelable TextMessage;
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/model/TextMessage.java b/ring-android/app/src/main/java/cx/ring/model/TextMessage.java
new file mode 100644
index 0000000..e7d44df
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/model/TextMessage.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2004-2014 Savoir-Faire Linux Inc.
+ *
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux>
+ * Alexandre Savard <alexandre.savard@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+package cx.ring.model;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import cx.ring.history.HistoryText;
+
+public class TextMessage implements Parcelable {
+
+ public static String ID = "id";
+ public static String ACCOUNT = "account";
+ public static String CONTACT = "contact";
+ public static String NUMBER = "number";
+ public static String CALL = "call";
+ public static String TYPE = "type";
+ public static String STATE = "State";
+ public static String MESSAGE = "message";
+ public static String TIME = "time";
+
+ private static final String TAG = TextMessage.class.getSimpleName();
+
+ private String mID = "";
+ private String mAccount = null;
+ private CallContact mContact = null;
+ private String mNumber = null;
+ private long mTimestamp = 0;
+
+ private int mType;
+ private int mState = state.NONE;
+ private String mMessage;
+ private String mCallID = "";
+
+ public TextMessage(TextMessage msg) {
+ mID = msg.mID;
+ mAccount = msg.mAccount;
+ mContact = msg.mContact;
+ mNumber = msg.mNumber;
+ mTimestamp = msg.mTimestamp;
+ mType = msg.mType;
+ mState = msg.mState;
+ mMessage = msg.mMessage;
+ mCallID = msg.mCallID;
+ }
+
+ /**
+ * *********************
+ * Construtors
+ * *********************
+ */
+
+ /*public TextMessage(String account, String number, String message, boolean in) {
+ mAccount = account;
+ mNumber = number;
+ mMessage = message;
+ mTimestamp = System.currentTimeMillis();
+ mType = in ? direction.INCOMING : direction.OUTGOING;
+ }*/
+
+ public TextMessage(boolean in, String message) {
+ mMessage = message;
+ mType = in ? direction.INCOMING : direction.OUTGOING;
+ mTimestamp = System.currentTimeMillis();
+ }
+
+ public TextMessage(boolean in, String message, String number, String callid, String account) {
+ mAccount = account;
+ mNumber = number;
+ mMessage = message;
+ mTimestamp = System.currentTimeMillis();
+ mCallID = callid;
+ mType = in ? direction.INCOMING : direction.OUTGOING;
+ }
+
+ public TextMessage(HistoryText h) {
+ mID = h.id;
+ mAccount = h.getAccountID();
+ mNumber = h.getNumber();
+ mMessage = h.getMessage();
+ mTimestamp = h.getDate().getTime();
+ mType = h.isIncoming() ? direction.INCOMING : direction.OUTGOING;
+ mCallID = h.getCallId();
+ }
+
+ protected TextMessage(Parcel in) {
+ mID = in.readString();
+ mAccount = in.readString();
+ mContact = in.readParcelable(CallContact.class.getClassLoader());
+ mNumber = in.readString();
+ mCallID = in.readString();
+ mType = in.readInt();
+ mState = in.readInt();
+ mTimestamp = in.readLong();
+ mMessage = in.readString();
+ }
+
+ public TextMessage(Bundle args) {
+ mID = args.getString(ID);
+ mAccount = args.getParcelable(ACCOUNT);
+ mNumber = args.getString(NUMBER);
+ mCallID = args.getString(CALL);
+ mType = args.getInt(TYPE);
+ mState = args.getInt(STATE);
+ mContact = args.getParcelable(CONTACT);
+ mMessage = args.getString(MESSAGE);
+ mTimestamp = args.getLong(TIME);
+ }
+
+ public String getRecordPath() {
+ return "";
+ }
+
+ public int getCallType() {
+ return mType;
+ }
+
+ public Bundle getBundle() {
+ Bundle args = new Bundle();
+ args.putString(ID, mID);
+ args.putString(ACCOUNT, mAccount);
+ args.putString(NUMBER, mNumber);
+ args.putString(CALL, mCallID);
+ args.putInt(STATE, mState);
+ args.putInt(TYPE, mType);
+ args.putParcelable(CONTACT, mContact);
+ args.putString(MESSAGE, mMessage);
+ args.putLong(TIME, mTimestamp);
+ return args;
+ }
+
+ public String getMessage() {
+ return mMessage;
+ }
+
+ public void setContact(CallContact contact) {
+ mContact = contact;
+ }
+
+ public void setCallId(String callId) {
+ this.mCallID = callId;
+ }
+
+ public String getCallId() {
+ return mCallID;
+ }
+
+ public void setNumber(String number) {
+ this.mNumber = number;
+ }
+
+ public interface direction {
+ int INCOMING = 1;
+ int OUTGOING = 2;
+ }
+
+ public interface state {
+ int NONE = 0;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mID);
+ out.writeString(mAccount);
+ out.writeParcelable(mContact, 0);
+ out.writeString(mNumber);
+ out.writeString(mCallID);
+ out.writeInt(mType);
+ out.writeInt(mState);
+ out.writeLong(mTimestamp);
+ out.writeString(mMessage);
+ }
+
+ public static final Creator<TextMessage> CREATOR = new Creator<TextMessage>() {
+ public TextMessage createFromParcel(Parcel in) {
+ return new TextMessage(in);
+ }
+
+ public TextMessage[] newArray(int size) {
+ return new TextMessage[size];
+ }
+ };
+
+ public void setID(String id) {
+ mID = id;
+ }
+
+ public String getId() {
+ return mID;
+ }
+
+ public long getTimestamp() {
+ return mTimestamp;
+ }
+
+ public void setTimestamp(long timestamp) {
+ this.mTimestamp = timestamp;
+ }
+
+ public void setAccount(String account) {
+ mAccount = account;
+ }
+
+ public String getAccount() {
+ return mAccount;
+ }
+
+ public String getTypeString() {
+ switch (mType) {
+ case direction.INCOMING:
+ return "INCOMING";
+ case direction.OUTGOING:
+ return "OUTGOING";
+ default:
+ return "UNDETERMINED";
+ }
+ }
+
+ public void setState(int callState) {
+ mState = callState;
+ }
+
+ public CallContact getContact() {
+ return mContact;
+ }
+
+ public String getNumber() {
+ return mNumber;
+ }
+
+ public String getStateString() {
+ String text_state;
+ switch (mState) {
+ case state.NONE:
+ text_state = "NONE";
+ break;
+ default:
+ text_state = "NULL";
+ }
+ return text_state;
+ }
+
+ /**
+ * Compare sip calls based on call ID
+ */
+ @Override
+ public boolean equals(Object c) {
+ return c instanceof TextMessage && ((TextMessage) c).mID.contentEquals((mID));
+ }
+
+ public boolean isOutgoing() {
+ return mType == direction.OUTGOING;
+ }
+
+ public boolean isIncoming() {
+ return mType == direction.INCOMING;
+ }
+
+}
diff --git a/ring-android/app/src/main/java/cx/ring/model/account/Account.java b/ring-android/app/src/main/java/cx/ring/model/account/Account.java
index 0f938c1..4c05299 100644
--- a/ring-android/app/src/main/java/cx/ring/model/account/Account.java
+++ b/ring-android/app/src/main/java/cx/ring/model/account/Account.java
@@ -57,10 +57,13 @@
advancedDetails = new AccountDetailAdvanced(details);
srtpDetails = new AccountDetailSrtp(details);
tlsDetails = new AccountDetailTls(details);
- volatileDetails = new AccountDetailVolatile(volatile_details);
- credentialsDetails = new ArrayList<>();
- for (int i = 0; i < credentials.size(); ++i) {
- credentialsDetails.add(new AccountCredentials(credentials.get(i)));
+ if (volatile_details != null)
+ volatileDetails = new AccountDetailVolatile(volatile_details);
+ if (credentials != null) {
+ credentialsDetails = new ArrayList<>();
+ for (int i = 0; i < credentials.size(); ++i) {
+ credentialsDetails.add(new AccountCredentials(credentials.get(i)));
+ }
}
}
@@ -84,8 +87,8 @@
return volatileDetails.getDetailString(AccountDetailVolatile.CONFIG_ACCOUNT_REGISTRATION_STATUS);
}
- public void setRegistered_state(String registered_state, int code) {
- Log.i(TAG, "setRegistered_state " + registered_state + " " + code);
+ public void setRegistrationState(String registered_state, int code) {
+ Log.i(TAG, "setRegistrationState " + registered_state + " " + code);
volatileDetails.setDetailString(AccountDetailVolatile.CONFIG_ACCOUNT_REGISTRATION_STATUS, registered_state);
volatileDetails.setDetailString(AccountDetailVolatile.CONFIG_ACCOUNT_REGISTRATION_STATE_CODE, Integer.toString(code));
}
@@ -141,7 +144,7 @@
advancedDetails = new AccountDetailAdvanced((HashMap<String, String>) in.readSerializable());
srtpDetails = new AccountDetailSrtp((HashMap<String, String>) in.readSerializable());
tlsDetails = new AccountDetailTls((HashMap<String, String>) in.readSerializable());
- credentialsDetails = new ArrayList<AccountCredentials>();
+ credentialsDetails = new ArrayList<>();
int cred_count = in.readInt();
for (int i = 0; i < cred_count; ++i) {
credentialsDetails.add(new AccountCredentials((HashMap<String, String>) in.readSerializable()));
@@ -202,7 +205,7 @@
}
public HashMap<String, String> getDetails() {
- HashMap<String, String> results = new HashMap<String, String>();
+ HashMap<String, String> results = new HashMap<>();
results.putAll(basicDetails.getDetailsHashMap());
results.putAll(advancedDetails.getDetailsHashMap());
diff --git a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetail.java b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetail.java
index 54fce72..9ec2ef2 100644
--- a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetail.java
+++ b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetail.java
@@ -26,10 +26,12 @@
public interface AccountDetail {
- public static final String TRUE_STR = "true";
- public static final String FALSE_STR = "false";
+ String TAG = AccountDetail.class.getSimpleName();
- public static class PreferenceEntry {
+ String TRUE_STR = "true";
+ String FALSE_STR = "false";
+
+ class PreferenceEntry {
public String mKey;
public boolean isTwoState;
public String mValue;
@@ -47,20 +49,19 @@
}
public boolean isChecked() {
- return mValue.contentEquals("true");
+ return mValue.contentEquals(TRUE_STR);
}
}
- public static final String TAG = "PreferenceHashMap";
- public ArrayList<PreferenceEntry> getDetailValues();
+ ArrayList<PreferenceEntry> getDetailValues();
- public ArrayList<String> getValuesOnly();
+ ArrayList<String> getValuesOnly();
- public HashMap<String, String> getDetailsHashMap();
+ HashMap<String, String> getDetailsHashMap();
- public String getDetailString(String key);
+ String getDetailString(String key);
- public void setDetailString(String key, String newValue);
+ void setDetailString(String key, String newValue);
}
diff --git a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailAdvanced.java b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailAdvanced.java
index 5875505..0691d16 100644
--- a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailAdvanced.java
+++ b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailAdvanced.java
@@ -22,8 +22,11 @@
package cx.ring.model.account;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import android.util.Log;
@@ -51,12 +54,26 @@
public static final String CONFIG_AUDIO_PORT_MIN = "Account.audioPortMin";
public static final String CONFIG_AUDIO_PORT_MAX = "Account.audioPortMax";
+ private static final Set<String> CONFIG_KEYS = new HashSet<>(Arrays.asList(
+ CONFIG_ACCOUNT_MAILBOX,
+ CONFIG_ACCOUNT_REGISTRATION_EXPIRE,
+ CONFIG_CREDENTIAL_NUMBER,
+ CONFIG_ACCOUNT_DTMF_TYPE,
+ CONFIG_RINGTONE_PATH, CONFIG_RINGTONE_ENABLED,
+ CONFIG_KEEP_ALIVE_ENABLED,
+ CONFIG_LOCAL_INTERFACE, CONFIG_PUBLISHED_SAMEAS_LOCAL, CONFIG_LOCAL_PORT,
+ CONFIG_PUBLISHED_PORT, CONFIG_PUBLISHED_ADDRESS,
+ CONFIG_STUN_SERVER, CONFIG_STUN_ENABLE,
+ CONFIG_AUDIO_PORT_MIN, CONFIG_AUDIO_PORT_MAX));
+
private ArrayList<AccountDetail.PreferenceEntry> privateArray;
public AccountDetailAdvanced(Map<String, String> pref) {
- privateArray = new ArrayList<AccountDetail.PreferenceEntry>();
+ privateArray = new ArrayList<>();
for (String key : pref.keySet()) {
+ if (!CONFIG_KEYS.contains(key))
+ continue;
PreferenceEntry p = new PreferenceEntry(key);
p.mValue = pref.get(key);
@@ -75,7 +92,7 @@
}
public ArrayList<String> getValuesOnly() {
- ArrayList<String> valueList = new ArrayList<String>();
+ ArrayList<String> valueList = new ArrayList<>();
for (AccountDetail.PreferenceEntry p : privateArray) {
Log.i(TAG, "" + p.mValue);
diff --git a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailBasic.java b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailBasic.java
index 6e95f12..9170ed4 100644
--- a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailBasic.java
+++ b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailBasic.java
@@ -45,10 +45,25 @@
public static final String CONFIG_ACCOUNT_ENABLE = "Account.enable";
public static final String CONFIG_PRESENCE_ENABLE = "Account.presenceEnabled";
+ public static final String ACCOUNT_TYPE_RING = "RING";
+ public static final String ACCOUNT_TYPE_SIP = "SIP";
+ public static final String ACCOUNT_TYPE_IAX = "IAX";
+
private ArrayList<AccountDetail.PreferenceEntry> privateArray;
+ public String getAlias() {
+ return getDetailString(CONFIG_ACCOUNT_ALIAS);
+ }
+ public String getUsername() {
+ return getDetailString(CONFIG_ACCOUNT_USERNAME);
+ }
+ public String getHostname() {
+ return getDetailString(CONFIG_ACCOUNT_HOSTNAME);
+ }
+
+
public AccountDetailBasic(Map<String, String> pref) {
- privateArray = new ArrayList<AccountDetail.PreferenceEntry>();
+ privateArray = new ArrayList<>();
for (String key : pref.keySet()) {
PreferenceEntry p = new PreferenceEntry(key);
@@ -66,7 +81,7 @@
}
public ArrayList<String> getValuesOnly() {
- ArrayList<String> valueList = new ArrayList<String>();
+ ArrayList<String> valueList = new ArrayList<>();
for (AccountDetail.PreferenceEntry p : privateArray) {
Log.i(TAG, "" + p.mValue);
@@ -77,7 +92,7 @@
}
public HashMap<String, String> getDetailsHashMap() {
- HashMap<String, String> map = new HashMap<String, String>();
+ HashMap<String, String> map = new HashMap<>();
for (AccountDetail.PreferenceEntry p : privateArray) {
map.put(p.mKey, p.mValue);
@@ -100,13 +115,14 @@
}
public void setDetailString(String key, String newValue) {
- for (int i = 0; i < privateArray.size(); ++i) {
- PreferenceEntry p = privateArray.get(i);
+ for (PreferenceEntry p : privateArray) {
if (p.mKey.equals(key)) {
- privateArray.get(i).mValue = newValue;
+ p.mValue = newValue;
+ Log.w(TAG, "setDetailString " + key + " -> " + newValue);
+ //return;
}
}
-
+ Log.w(TAG, "setDetailString FAIL" + key + " -> " + newValue);
}
}
diff --git a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailSrtp.java b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailSrtp.java
index a709423..81d1bc7 100644
--- a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailSrtp.java
+++ b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailSrtp.java
@@ -41,7 +41,7 @@
private ArrayList<AccountDetail.PreferenceEntry> privateArray;
public static ArrayList<AccountDetail.PreferenceEntry> getPreferenceEntries() {
- ArrayList<AccountDetail.PreferenceEntry> preference = new ArrayList<AccountDetail.PreferenceEntry>();
+ ArrayList<AccountDetail.PreferenceEntry> preference = new ArrayList<>();
preference.add(new PreferenceEntry(CONFIG_SRTP_ENABLE, true));
preference.add(new PreferenceEntry(CONFIG_SRTP_KEY_EXCHANGE, false));
diff --git a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailTls.java b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailTls.java
index 595f9d8..9ebfa24 100644
--- a/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailTls.java
+++ b/ring-android/app/src/main/java/cx/ring/model/account/AccountDetailTls.java
@@ -46,7 +46,7 @@
private ArrayList<AccountDetail.PreferenceEntry> privateArray;
public static ArrayList<AccountDetail.PreferenceEntry> getPreferenceEntries() {
- ArrayList<AccountDetail.PreferenceEntry> preference = new ArrayList<AccountDetail.PreferenceEntry>();
+ ArrayList<AccountDetail.PreferenceEntry> preference = new ArrayList<>();
preference.add(new PreferenceEntry(CONFIG_TLS_LISTENER_PORT));
preference.add(new PreferenceEntry(CONFIG_TLS_ENABLE, true));
diff --git a/ring-android/app/src/main/java/cx/ring/service/CallManagerCallBack.java b/ring-android/app/src/main/java/cx/ring/service/CallManagerCallBack.java
index 66bc4ae..aaf7480 100644
--- a/ring-android/app/src/main/java/cx/ring/service/CallManagerCallBack.java
+++ b/ring-android/app/src/main/java/cx/ring/service/CallManagerCallBack.java
@@ -1,28 +1,33 @@
package cx.ring.service;
+import android.support.v4.app.NotificationCompat;
+import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
+
+import cx.ring.R;
import cx.ring.client.CallActivity;
+import cx.ring.history.HistoryText;
+import cx.ring.model.CallContact;
+import cx.ring.model.TextMessage;
import cx.ring.model.account.Account;
+import cx.ring.model.account.AccountDetailSrtp;
import cx.ring.utils.SwigNativeConverter;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.Map;
-import cx.ring.model.CallContact;
import cx.ring.model.Conference;
import cx.ring.model.SecureSipCall;
import cx.ring.model.SipCall;
-import cx.ring.model.SipMessage;
public class CallManagerCallBack extends Callback {
private static final String TAG = "CallManagerCallBack";
private SipService mService;
- static public final String CALL_STATE_CHANGED = "call-state-changed";
+ static public final String CALL_STATE_CHANGED = "call-State-changed";
static public final String INCOMING_CALL = "incoming-call";
static public final String INCOMING_TEXT = "incoming-text";
static public final String CONF_CREATED = "conf_created";
@@ -46,11 +51,12 @@
@Override
public void callStateChanged(String callID, String newState, int detail_code) {
- Log.d(TAG, "on_call_state_changed : (" + callID + ", " + newState + ")");
+ Log.w(TAG, "on_call_state_changed : (" + callID + ", " + newState + ")");
Conference toUpdate = mService.findConference(callID);
if (toUpdate == null) {
+ Log.w(TAG, "callStateChanged: can't find call " + callID);
return;
}
@@ -59,38 +65,52 @@
intent.putExtra("State", newState);
intent.putExtra("DetailCode", detail_code);
- if (newState.equals("RINGING")) {
- toUpdate.setCallState(callID, SipCall.state.CALL_STATE_RINGING);
- } else if (newState.equals("CURRENT")) {
- if (toUpdate.isRinging()) {
- toUpdate.getCallById(callID).setTimestampStart_(System.currentTimeMillis());
- }
- toUpdate.setCallState(callID, SipCall.state.CALL_STATE_CURRENT);
- } else if (newState.equals("HUNGUP")) {
- Log.d(TAG, "Hanging up " + callID);
- SipCall call = toUpdate.getCallById(callID);
- if (!toUpdate.hasMultipleParticipants()) {
- if (toUpdate.isRinging() && toUpdate.isIncoming()) {
- mService.mNotificationManager.publishMissedCallNotification(mService.getConferences().get(callID));
+ if (toUpdate.isRinging() && !newState.equals("RINGING")) {
+ Log.w(TAG, "Setting call start date " + callID);
+ toUpdate.getCallById(callID).setTimestampStart(System.currentTimeMillis());
+ }
+
+ switch (newState) {
+ case "CONNECTING":
+ toUpdate.setCallState(callID, SipCall.State.CONNECTING); break;
+ case "RINGING":
+ toUpdate.setCallState(callID, SipCall.State.RINGING); break;
+ case "CURRENT":
+ toUpdate.setCallState(callID, SipCall.State.CURRENT); break;
+ case "HOLD":
+ toUpdate.setCallState(callID, SipCall.State.HOLD); break;
+ case "UNHOLD":
+ toUpdate.setCallState(callID, SipCall.State.CURRENT); break;
+ case "HUNGUP":
+ case "INACTIVE":
+ Log.d(TAG, "Hanging up " + callID);
+ Log.w("CallNotification ", "Canceling " + toUpdate.notificationId);
+ mService.mNotificationManager.notificationManager.cancel(toUpdate.notificationId);
+ SipCall call = toUpdate.getCallById(callID);
+ if (!toUpdate.hasMultipleParticipants()) {
+ if (toUpdate.isRinging() && toUpdate.isIncoming()) {
+ mService.mNotificationManager.publishMissedCallNotification(mService.getConferences().get(callID));
+ }
+ toUpdate.setCallState(callID, SipCall.State.HUNGUP);
+ mService.mHistoryManager.insertNewEntry(toUpdate);
+ mService.getConferences().remove(toUpdate.getId());
+ } else {
+ toUpdate.setCallState(callID, SipCall.State.HUNGUP);
+ mService.mHistoryManager.insertNewEntry(call);
}
- toUpdate.setCallState(callID, SipCall.state.CALL_STATE_HUNGUP);
- mService.mHistoryManager.insertNewEntry(toUpdate);
+ break;
+ case "BUSY":
+ mService.mNotificationManager.notificationManager.cancel(toUpdate.notificationId);
+ toUpdate.setCallState(callID, SipCall.State.BUSY);
mService.getConferences().remove(toUpdate.getId());
- } else {
- toUpdate.setCallState(callID, SipCall.state.CALL_STATE_HUNGUP);
- mService.mHistoryManager.insertNewEntry(call);
- }
- } else if (newState.equals("BUSY")) {
- toUpdate.setCallState(callID, SipCall.state.CALL_STATE_BUSY);
- mService.getConferences().remove(toUpdate.getId());
- } else if (newState.equals("FAILURE")) {
- toUpdate.setCallState(callID, SipCall.state.CALL_STATE_FAILURE);
- mService.getConferences().remove(toUpdate.getId());
- Ringservice.hangUp(callID);
- } else if (newState.equals("HOLD")) {
- toUpdate.setCallState(callID, SipCall.state.CALL_STATE_HOLD);
- } else if (newState.equals("UNHOLD")) {
- toUpdate.setCallState(callID, SipCall.state.CALL_STATE_CURRENT);
+ break;
+ case "FAILURE":
+ Log.w("CallNotification ", "Canceling " + toUpdate.notificationId);
+ mService.mNotificationManager.notificationManager.cancel(toUpdate.notificationId);
+ toUpdate.setCallState(callID, SipCall.State.FAILURE);
+ mService.getConferences().remove(toUpdate.getId());
+ Ringservice.hangUp(callID);
+ break;
}
intent.putExtra("conference", toUpdate);
mService.sendBroadcast(intent);
@@ -99,32 +119,28 @@
@Override
public void incomingCall(String accountID, String callID, String from) {
- Log.d(TAG, "on_incoming_call(" + accountID + ", " + callID + ", " + from + ")");
+ Log.w(TAG, "on_incoming_call(" + accountID + ", " + callID + ", " + from + ")");
try {
StringMap details = Ringservice.getAccountDetails(accountID);
- VectMap credentials = Ringservice.getCredentials(accountID);
- StringMap state = Ringservice.getVolatileAccountDetails(accountID);
- Account acc = new Account(accountID, details.toNative(), credentials.toNative(), state.toNative());
-
- Bundle args = new Bundle();
- args.putString(SipCall.ID, callID);
- args.putParcelable(SipCall.ACCOUNT, acc);
- args.putInt(SipCall.STATE, SipCall.state.CALL_STATE_RINGING);
- args.putInt(SipCall.TYPE, SipCall.direction.CALL_TYPE_INCOMING);
-
- CallContact unknow = CallContact.ContactBuilder.buildUnknownContact(from);
- args.putParcelable(SipCall.CONTACT, unknow);
+ //VectMap credentials = Ringservice.getCredentials(accountID);
+ //StringMap state = Ringservice.getVolatileAccountDetails(accountID);
+ Account acc = new Account(accountID, details.toNative(), null, null);
Intent toSend = new Intent(CallManagerCallBack.INCOMING_CALL);
toSend.setClass(mService, CallActivity.class);
toSend.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- SipCall newCall = new SipCall(args);
- newCall.setTimestampStart_(System.currentTimeMillis());
+
+ CallContact unknown = CallContact.ContactBuilder.buildUnknownContact(from);
+
+ SipCall newCall = new SipCall(callID, accountID, from, SipCall.Direction.INCOMING);
+ newCall.setContact(unknown);
+ newCall.setCallState(SipCall.State.RINGING);
+ newCall.setTimestampStart(System.currentTimeMillis());
Conference toAdd;
if (acc.useSecureLayer()) {
- SecureSipCall secureCall = new SecureSipCall(newCall);
+ SecureSipCall secureCall = new SecureSipCall(newCall, acc.getSrtpDetails().getDetailString(AccountDetailSrtp.CONFIG_SRTP_KEY_EXCHANGE));
toAdd = new Conference(secureCall);
} else {
toAdd = new Conference(newCall);
@@ -132,6 +148,22 @@
mService.getConferences().put(toAdd.getId(), toAdd);
+ NotificationCompat.Builder noti = new NotificationCompat.Builder(mService)
+ .setContentTitle("Incoming call with " + from)
+ .setContentText("incoming call")
+ .setOngoing(true)
+ .setSmallIcon(R.drawable.ic_launcher)
+ .addAction(R.drawable.ic_call_end_white_24dp, "End call",
+ PendingIntent.getService(mService, 4278,
+ new Intent(mService, SipService.class)
+ .setAction(SipService.ACTION_CALL_END)
+ .putExtra("conf", toAdd.getId()),
+ PendingIntent.FLAG_ONE_SHOT));
+
+ //mService.startForeground(toAdd.notificationId, noti);
+ Log.w("CallNotification ", "Adding for incoming " + toAdd.notificationId);
+ mService.mNotificationManager.notificationManager.notify(toAdd.notificationId, noti.build());
+
Bundle bundle = new Bundle();
bundle.putParcelable("conference", toAdd);
toSend.putExtra("resuming", false);
@@ -175,33 +207,37 @@
}
@Override
- public void incomingMessage(String ID, String from, StringMap messages) {
+ public void incomingMessage(String id, String from, StringMap messages) {
Log.w(TAG, "on_incoming_message:" + messages);
String msg = messages.get("text/plain");
if (msg == null)
return;
- Intent intent = new Intent(INCOMING_TEXT);
- intent.putExtra("CallID", ID);
- intent.putExtra("From", from);
- intent.putExtra("Msg", msg);
-
- if (mService.getConferences().get(ID) != null) {
- mService.getConferences().get(ID).addSipMessage(new SipMessage(true, msg));
- intent.putExtra("conference", mService.getConferences().get(ID));
- } else {
- for (Map.Entry<String, Conference> stringConferenceEntry : mService.getConferences().entrySet()) {
- Conference tmp = stringConferenceEntry.getValue();
- for (SipCall c : tmp.getParticipants()) {
- if (c.getCallId().contentEquals(ID)) {
- mService.getConferences().get(tmp.getId()).addSipMessage(new SipMessage(true, msg));
- intent.putExtra("conference", tmp);
- }
+ Conference conf = mService.getConferences().get(id);
+ if (conf == null) {
+ for (Conference tmp : mService.getConferences().values())
+ if (tmp.getCallById(id) != null) {
+ conf = tmp;
+ break;
}
+ if (conf == null) {
+ Log.w(TAG, "Discarding message for unknown call " + id);
+ return;
}
-
}
+
+ TextMessage message = new TextMessage(true, msg, from, id, conf.hasMultipleParticipants() ? null : conf.getParticipants().get(0).getAccount());
+ if (!conf.hasMultipleParticipants())
+ message.setContact(conf.getParticipants().get(0).getContact());
+
+ conf.addSipMessage(message);
+
+ mService.mHistoryManager.insertNewTextMessage(new HistoryText(message));
+
+ Intent intent = new Intent(INCOMING_TEXT);
+ intent.putExtra("txt", message);
+ intent.putExtra("conference", conf.getId());
mService.sendBroadcast(intent);
}
@@ -215,10 +251,21 @@
for (SipCall call : toReInsert.getParticipants()) {
mService.getConferences().put(call.getCallId(), new Conference(call));
}
- intent.putExtra("conference", mService.getConferences().get(confID));
+
+ Conference conf = mService.getConferences().get(confID);
+
+ Log.w("CallNotification ", "Canceling " + conf.notificationId);
+ //NotificationManager mNotifyMgr = (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
+ mService.mNotificationManager.notificationManager.cancel(conf.notificationId);
+
+ intent.putExtra("conference", conf);
mService.getConferences().remove(confID);
mService.sendBroadcast(intent);
+ if (mService.getConferences().size() == 0) {
+ mService.stopForeground(true);
+ }
+
}
@Override
diff --git a/ring-android/app/src/main/java/cx/ring/service/ConfigurationManagerCallback.java b/ring-android/app/src/main/java/cx/ring/service/ConfigurationManagerCallback.java
index ceb4969..e852a0d 100644
--- a/ring-android/app/src/main/java/cx/ring/service/ConfigurationManagerCallback.java
+++ b/ring-android/app/src/main/java/cx/ring/service/ConfigurationManagerCallback.java
@@ -26,13 +26,17 @@
import android.content.Intent;
import android.util.Log;
+import cx.ring.history.HistoryText;
+import cx.ring.model.TextMessage;
+
public class ConfigurationManagerCallback extends ConfigurationCallback {
private SipService mService;
private static final String TAG = "ConfigurationManagerCb";
static public final String ACCOUNTS_CHANGED = "accounts-changed";
- static public final String ACCOUNT_STATE_CHANGED = "account-state-changed";
+ static public final String ACCOUNT_STATE_CHANGED = "account-State-changed";
+ static public final String INCOMING_TEXT = "incoming--txt-msg";
public ConfigurationManagerCallback(SipService context) {
super();
@@ -58,7 +62,19 @@
@Override
public void registrationStateChanged(String account_id, String state, int code, String detail_str) {
- sendAccountStateChangedMessage(account_id, state, 0);
+ Log.w(getClass().getName(), "registrationStateChanged: " + account_id + " " + state + " " + code + " " + detail_str);
+ sendAccountStateChangedMessage(account_id, state, code);
+ }
+
+ @Override
+ public void incomingAccountMessage(String accountID, String from, String msg) {
+ Log.w(TAG, "incomingAccountMessage : " + accountID + " " + from + " " + msg);
+
+ TextMessage message = new TextMessage(true, msg, from, null, accountID);
+ mService.mHistoryManager.insertNewTextMessage(new HistoryText(message));
+ Intent intent = new Intent(INCOMING_TEXT);
+ intent.putExtra("txt", message);
+ mService.sendBroadcast(intent);
}
@Override
@@ -69,7 +85,7 @@
private void sendAccountStateChangedMessage(String accoundID, String state, int code) {
Intent intent = new Intent(ACCOUNT_STATE_CHANGED);
intent.putExtra("Account", accoundID);
- intent.putExtra("state", state);
+ intent.putExtra("State", state);
intent.putExtra("code", code);
mService.sendBroadcast(intent);
}
@@ -79,6 +95,7 @@
OpenSlParams audioParams = OpenSlParams.createInstance(mService);
ret.add(audioParams.getSampleRate());
ret.add(audioParams.getBufferSize());
+ Log.w(getClass().getName(), "getHardwareAudioFormat: " + audioParams.getSampleRate() + " " + audioParams.getBufferSize());
}
@Override
diff --git a/ring-android/app/src/main/java/cx/ring/service/ISipService.aidl b/ring-android/app/src/main/java/cx/ring/service/ISipService.aidl
index 4708062..5281c5e 100644
--- a/ring-android/app/src/main/java/cx/ring/service/ISipService.aidl
+++ b/ring-android/app/src/main/java/cx/ring/service/ISipService.aidl
@@ -1,8 +1,8 @@
package cx.ring.service;
import cx.ring.model.SipCall;
+import cx.ring.model.TextMessage;
import cx.ring.model.Conference;
-import cx.ring.model.SipMessage;
interface ISipService {
@@ -57,7 +57,7 @@
void playDtmf(in String key);
/* IM */
- void sendTextMessage(in String callID, in SipMessage message);
+ void sendTextMessage(in String callID, in TextMessage message);
void sendAccountTextMessage(in String accountid, in String to, in String msg);
diff --git a/ring-android/app/src/main/java/cx/ring/service/LocalService.java b/ring-android/app/src/main/java/cx/ring/service/LocalService.java
new file mode 100644
index 0000000..02c22f2
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/service/LocalService.java
@@ -0,0 +1,813 @@
+package cx.ring.service;
+
+import android.app.Service;
+import android.content.AsyncTaskLoader;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.Loader;
+import android.content.ServiceConnection;
+import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.provider.ContactsContract;
+import android.support.annotation.NonNull;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.Pair;
+
+import java.lang.ref.WeakReference;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import cx.ring.BuildConfig;
+import cx.ring.history.HistoryCall;
+import cx.ring.history.HistoryEntry;
+import cx.ring.history.HistoryManager;
+import cx.ring.history.HistoryText;
+import cx.ring.model.CallContact;
+import cx.ring.model.Conference;
+import cx.ring.model.Conversation;
+import cx.ring.model.SipCall;
+import cx.ring.model.TextMessage;
+import cx.ring.model.account.Account;
+import cx.ring.utils.Utilities;
+
+
+public class LocalService extends Service {
+ static final String TAG = LocalService.class.getSimpleName();
+ static public final String ACTION_CONF_UPDATE = BuildConfig.APPLICATION_ID + ".action.CONF_UPDATE";
+ static public final String ACTION_ACCOUNT_UPDATE = BuildConfig.APPLICATION_ID + ".action.ACCOUNT_UPDATE";
+
+ public static final String AUTHORITY = "cx.ring";
+ public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+ private ISipService mService = null;
+
+ // Binder given to clients
+ private final IBinder mBinder = new LocalBinder();
+
+ private Map<String, Conversation> conversations = new HashMap<>();
+ private ArrayList<Account> all_accounts = new ArrayList<>();
+ private List<Account> accounts = all_accounts;
+ private List<Account> ip2ip_account = all_accounts;
+
+ private HistoryManager historyManager;
+
+ AccountsLoader mAccountLoader = null;
+
+ public interface Callbacks {
+ ISipService getRemoteService();
+ LocalService getService();
+ }
+ public static class DummyCallbacks implements Callbacks {
+ @Override
+ public ISipService getRemoteService() {
+ return null;
+ }
+ @Override
+ public LocalService getService() {
+ return null;
+ }
+ }
+ public static final Callbacks DUMMY_CALLBACKS = new DummyCallbacks();
+
+ @Override
+ public void onCreate() {
+ Log.e(TAG, "onCreate");
+ super.onCreate();
+ historyManager = new HistoryManager(this);
+ Intent intent = new Intent(this, SipService.class);
+ startService(intent);
+ bindService(intent, mConnection, BIND_AUTO_CREATE | BIND_IMPORTANT | BIND_ABOVE_CLIENT );
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.e(TAG, "onDestroy");
+ super.onDestroy();
+ stopListener();
+ }
+
+ private final Loader.OnLoadCompleteListener<ArrayList<Account>> onAccountsLoaded = new Loader.OnLoadCompleteListener<ArrayList<Account>>() {
+ @Override
+ public void onLoadComplete(Loader<ArrayList<Account>> loader, ArrayList<Account> data) {
+ Log.w(TAG, "AccountsLoader Loader.OnLoadCompleteListener");
+ all_accounts = data;
+ accounts = all_accounts.subList(0,data.size()-1);
+ ip2ip_account = all_accounts.subList(data.size()-1,data.size());
+ sendBroadcast(new Intent(ACTION_ACCOUNT_UPDATE));
+ }
+ };
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Log.w(TAG, "onServiceConnected " + className.getClassName());
+ mService = ISipService.Stub.asInterface(service);
+ //mBound = true;
+ mAccountLoader = new AccountsLoader(LocalService.this);
+ mAccountLoader.registerListener(1, onAccountsLoaded);
+ mAccountLoader.startLoading();
+ mAccountLoader.forceLoad();
+ startListener();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ Log.w(TAG, "onServiceDisconnected " + arg0.getClassName());
+ if (mAccountLoader != null) {
+ mAccountLoader.unregisterListener(onAccountsLoaded);
+ mAccountLoader.cancelLoad();
+ mAccountLoader.stopLoading();
+ }
+ //mBound = false;
+ mService = null;
+ }
+ };
+
+ /**
+ * Class used for the client Binder. Because we know this service always
+ * runs in the same process as its clients, we don't need to deal with IPC.
+ */
+ public class LocalBinder extends Binder {
+ public LocalService getService() {
+ // Return this instance of LocalService so clients can call public methods
+ return LocalService.this;
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ Log.e(TAG, "onUnbind");
+ if (mConnection != null) {
+ unbindService(mConnection);
+ mConnection = null;
+ }
+ return super.onUnbind(intent);
+ }
+
+ public ISipService getRemoteService() {
+ return mService;
+ }
+
+ public List<Account> getAccounts() { return accounts; }
+ public List<Account> getIP2IPAccount() { return ip2ip_account; }
+ public Account getAccount(String account_id) {
+ for (Account acc : all_accounts)
+ if (acc.getAccountID().equals(account_id))
+ return acc;
+ return null;
+ }
+
+ public ArrayList<Conversation> getConversations() {
+ ArrayList<Conversation> convs = new ArrayList<>(conversations.values());
+ Collections.sort(convs, new Comparator<Conversation>() {
+ @Override
+ public int compare(Conversation lhs, Conversation rhs) {
+ return (int) ((rhs.getLastInteraction().getTime() - lhs.getLastInteraction().getTime())/1000l);
+ }
+ });
+ return convs;
+ }
+
+ public Conversation getConversation(String id) {
+ return conversations.get(id);
+ }
+
+ public Conference getConference(String id) {
+ for (Conversation conv : conversations.values()) {
+ Conference conf = conv.getConference(id);
+ if (conf != null)
+ return conf;
+ }
+ return null;
+ }
+
+ public Conversation getByContact(CallContact contact) {
+ ArrayList<String> keys = contact.getIds();
+ for (String k : keys) {
+ Conversation c = conversations.get(k);
+ if (c != null)
+ return c;
+ }
+ Log.w(TAG, "getByContact failed");
+ return null;
+ }
+ public Conversation getByCallId(String callId) {
+ for (Conversation conv : conversations.values()) {
+ Conference conf = conv.getConference(callId);
+ if (conf != null)
+ return conv;
+ }
+ return null;
+ }
+
+ public Conversation startConversation(CallContact contact) {
+ Conversation c = getByContact(contact);
+ if (c == null) {
+ c = new Conversation(contact);
+ conversations.put(contact.getIds().get(0), c);
+ }
+ return c;
+ }
+
+ public CallContact findContactByNumber(String number) {
+ for (Conversation conv : conversations.values()) {
+ if (conv.contact.hasNumber(number))
+ return conv.contact;
+ }
+ return findContactByNumber(getContentResolver(), number);
+ }
+
+ public CallContact findContactById(long id) {
+ return findById(getContentResolver(), id);
+ }
+
+ public Account guessAccount(CallContact c, String number) {
+ number = CallContact.canonicalNumber(number);
+ if (Utilities.isIpAddress(number))
+ return ip2ip_account.get(0);
+ /*Conversation conv = getByContact(c);
+ if (conv != null) {
+ return
+ }*/
+ return accounts.get(0);
+ }
+
+ public static final String[] DATA_PROJECTION = {
+ ContactsContract.Data._ID,
+ ContactsContract.RawContacts.CONTACT_ID,
+ ContactsContract.Data.LOOKUP_KEY,
+ ContactsContract.Data.DISPLAY_NAME_PRIMARY,
+ ContactsContract.Data.PHOTO_ID,
+ ContactsContract.Data.PHOTO_THUMBNAIL_URI
+ };
+ public static final String[] CONTACT_PROJECTION = {
+ ContactsContract.Data._ID,
+ ContactsContract.Data.LOOKUP_KEY,
+ ContactsContract.Data.DISPLAY_NAME_PRIMARY,
+ ContactsContract.Data.PHOTO_ID,
+ };
+
+ public static final String[] PHONELOOKUP_PROJECTION = {
+ ContactsContract.PhoneLookup._ID,
+ ContactsContract.PhoneLookup.LOOKUP_KEY,
+ ContactsContract.PhoneLookup.PHOTO_ID,
+ ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
+ };
+
+ private static final String[] CONTACTS_PHONES_PROJECTION = {
+ ContactsContract.CommonDataKinds.Phone.NUMBER,
+ ContactsContract.CommonDataKinds.Phone.TYPE
+ };
+ private static final String[] CONTACTS_SIP_PROJECTION = {
+ ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS,
+ ContactsContract.CommonDataKinds.SipAddress.TYPE
+ };
+ /* private static final String[] CONTACTS_PHOTO_PROJECTION = {
+ ContactsContract.CommonDataKinds.Photo.,
+ ContactsContract.CommonDataKinds.SipAddress.TYPE
+ };
+*/
+ private static final String ID_SELECTION = ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?";
+ private static final String KEY_SELECTION = ContactsContract.Contacts.LOOKUP_KEY + "=?";
+
+ private static void lookupDetails(@NonNull ContentResolver res, @NonNull CallContact c) {
+ Log.w(TAG, "lookupDetails " + c.getKey());
+
+ Cursor cPhones = res.query(
+ ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
+ CONTACTS_PHONES_PROJECTION,
+ ID_SELECTION,
+ new String[]{String.valueOf(c.getId())}, null);
+ if (cPhones != null) {
+ final int iNum = cPhones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
+ final int iType = cPhones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE);
+ while (cPhones.moveToNext()) {
+ c.addNumber(cPhones.getString(iNum), cPhones.getInt(iType), CallContact.NumberType.TEL);
+ Log.w(TAG,"Phone:"+cPhones.getString(cPhones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
+ }
+ cPhones.close();
+ }
+
+ Uri baseUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, c.getId());
+ Uri targetUri = Uri.withAppendedPath(baseUri, ContactsContract.Contacts.Data.CONTENT_DIRECTORY);
+ Cursor cSip = res.query(targetUri,
+ CONTACTS_SIP_PROJECTION,
+ ContactsContract.Data.MIMETYPE + "=?",
+ new String[]{ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE}, null);
+ if (cSip != null) {
+ final int iSip = cSip.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS);
+ final int iType = cSip.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.TYPE);
+ while (cSip.moveToNext()) {
+ c.addNumber(cSip.getString(iSip), cSip.getInt(iType), CallContact.NumberType.SIP);
+ Log.w(TAG, "SIP phone:" + cSip.getString(iSip));
+ }
+ cSip.close();
+ }
+
+ /*Cursor cPhoto = res.query(targetUri,
+ CONTACTS_SIP_PROJECTION,
+ ContactsContract.Data.MIMETYPE + "=?",
+ new String[]{ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE}, null);
+ if (cSip != null) {
+ final int iSip = cSip.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS);
+ final int iType = cSip.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.TYPE);
+ while (cSip.moveToNext()) {
+ c.addNumber(cSip.getString(iSip), cSip.getInt(iType), CallContact.NumberType.SIP);
+ Log.w(TAG, "SIP phone:" + cSip.getString(iSip));
+ }
+ cSip.close();
+ }*/
+ }
+
+ public static CallContact findByKey(@NonNull ContentResolver res, String key) {
+ Log.e(TAG, "findByKey " + key);
+
+ final CallContact.ContactBuilder builder = CallContact.ContactBuilder.getInstance();
+ Cursor result = res.query( Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, key), CONTACT_PROJECTION,
+ null, null, null);
+
+ CallContact contact = null;
+ if (result != null && result.moveToFirst()) {
+ int iID = result.getColumnIndex(ContactsContract.Data._ID);
+ int iKey = result.getColumnIndex(ContactsContract.Data.LOOKUP_KEY);
+ int iName = result.getColumnIndex(ContactsContract.Data.DISPLAY_NAME);
+ int iPhoto = result.getColumnIndex(ContactsContract.Data.PHOTO_ID);
+ long cid = result.getLong(iID);
+
+ Log.w(TAG, "Contact id:" + cid + " key:" + result.getString(iKey));
+
+ builder.startNewContact(cid, result.getString(iKey), result.getString(iName), result.getLong(iPhoto));
+ result.close();
+
+ contact = builder.build();
+ lookupDetails(res, contact);
+ }
+ return contact;
+ }
+
+ public static CallContact findById(@NonNull ContentResolver res, long id) {
+ Log.e(TAG, "findById " + id);
+
+ final CallContact.ContactBuilder builder = CallContact.ContactBuilder.getInstance();
+ Cursor result = res.query(ContactsContract.Contacts.CONTENT_URI, CONTACT_PROJECTION,
+ ContactsContract.Contacts._ID + " = ?",
+ new String[]{String.valueOf(id)}, null);
+ if (result == null)
+ return null;
+
+ CallContact contact = null;
+ if (result.moveToFirst()) {
+ int iID = result.getColumnIndex(ContactsContract.Data._ID);
+ int iKey = result.getColumnIndex(ContactsContract.Data.LOOKUP_KEY);
+ int iName = result.getColumnIndex(ContactsContract.Data.DISPLAY_NAME);
+ int iPhoto = result.getColumnIndex(ContactsContract.Data.PHOTO_ID);
+ long cid = result.getLong(iID);
+
+ Log.w(TAG, "Contact id:" + cid + " key:" + result.getString(iKey));
+
+ builder.startNewContact(cid, result.getString(iKey), result.getString(iName), result.getLong(iPhoto));
+ contact = builder.build();
+ lookupDetails(res, contact);
+ }
+ result.close();
+ return contact;
+ }
+ /*
+ public static int getRawContactId(@NonNull ContentResolver res, long contactId)
+ {
+ Cursor c= res.query(
+ ContactsContract.RawContacts.CONTENT_URI,
+ new String[]{ContactsContract.RawContacts._ID},
+ ContactsContract.RawContacts.CONTACT_ID+"=?",
+ new String[]{String.valueOf(contactId)},
+ null);
+ if (c == null)
+ return -1;
+ if (c.moveToFirst()) {
+ int rawContactId = c.getInt(c.getColumnIndex(ContactsContract.RawContacts._ID));
+ Log.d(TAG, "Contact Id: " + contactId + " Raw Contact Id: " + rawContactId);
+ return rawContactId;
+ }
+ c.close();
+ return -1;
+ }
+*/
+ @NonNull
+ public static CallContact findContactBySipNumber(@NonNull ContentResolver res, String number) {
+ final CallContact.ContactBuilder builder = CallContact.ContactBuilder.getInstance();
+ Cursor result = res.query(ContactsContract.Data.CONTENT_URI,
+ DATA_PROJECTION,
+ ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + "=?" + " AND " + ContactsContract.Data.MIMETYPE + "=?",
+ new String[]{number, ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE}, null);
+ if (result == null) {
+ Log.w(TAG, "findContactBySipNumber " + number + " can't find contact.");
+ return CallContact.ContactBuilder.buildUnknownContact(number);
+ }
+ int iID = result.getColumnIndex(ContactsContract.Data._ID);
+ int icID = result.getColumnIndex(ContactsContract.RawContacts.CONTACT_ID);
+ int iKey = result.getColumnIndex(ContactsContract.Data.LOOKUP_KEY);
+ int iName = result.getColumnIndex(ContactsContract.Data.DISPLAY_NAME);
+ int iPhoto = result.getColumnIndex(ContactsContract.Data.PHOTO_ID);
+ int iPhotoThumb = result.getColumnIndex(ContactsContract.Data.PHOTO_THUMBNAIL_URI);
+
+ ArrayList<CallContact> contacts = new ArrayList<>(1);
+ while (result.moveToNext()) {
+ long cid = result.getLong(icID);
+ long id = result.getLong(iID);
+ builder.startNewContact(cid, result.getString(iKey), result.getString(iName), result.getLong(iPhoto));
+ CallContact contact = builder.build();
+ //Log.w(TAG, "findContactBySipNumber " + number + " found name:" + contact.getDisplayName() + " id:" + contact.getId() + " key:" + contact.getKey() + " rawid:"+getRawContactId(res, contact.getId()) + " rid:"+id + " photo:"+result.getLong(iPhoto) + " thumb:" + result.getString(iPhotoThumb));
+ lookupDetails(res, contact);
+ contacts.add(contact);
+ }
+ result.close();
+
+ //lookupDetails(res, contact);
+ /*if (contact == null) {
+ Log.w(TAG, "Can't find contact with number " + number);
+ contact = CallContact.ContactBuilder.buildUnknownContact(number);
+ }*/
+ if (contacts.isEmpty()) {
+ Log.w(TAG, "findContactBySipNumber " + number + " can't find contact.");
+ return CallContact.ContactBuilder.buildUnknownContact(number);
+ }
+ return contacts.get(0);
+ }
+
+ @NonNull
+ public static CallContact findContactByNumber(@NonNull ContentResolver res, String number) {
+ //Log.w(TAG, "findContactByNumber " + number);
+
+ final CallContact.ContactBuilder builder = CallContact.ContactBuilder.getInstance();
+ Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
+ Cursor result = res.query(uri, PHONELOOKUP_PROJECTION, null, null, null);
+ if (result == null) {
+ Log.w(TAG, "findContactByNumber " + number + " can't find contact.");
+ return findContactBySipNumber(res, number);
+ }
+ if (!result.moveToFirst()) {
+ result.close();
+ Log.w(TAG, "findContactByNumber " + number + " can't find contact.");
+ return findContactBySipNumber(res, number);
+ }
+ int iID = result.getColumnIndex(ContactsContract.Contacts._ID);
+ int iKey = result.getColumnIndex(ContactsContract.Data.LOOKUP_KEY);
+ int iName = result.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
+ int iPhoto = result.getColumnIndex(ContactsContract.Contacts.PHOTO_ID);
+ builder.startNewContact(result.getLong(iID), result.getString(iKey), result.getString(iName), result.getLong(iPhoto));
+ result.close();
+ CallContact contact = builder.build();
+ lookupDetails(res, contact);
+ /*if (contact == null) {
+ Log.w(TAG, "Can't find contact with number " + number);
+ contact = CallContact.ContactBuilder.buildUnknownContact(number);
+ }*/
+ Log.w(TAG, "findContactByNumber " + number + " found " + contact.getDisplayName());
+
+ return contact;
+ }
+
+ private class ConversationLoader extends AsyncTask<Void, Void, Map<String, Conversation>> {
+ private final ContentResolver cr;
+
+ public ConversationLoader(Context c) {
+ cr = c.getContentResolver();
+ }
+
+ private CallContact getByNumber(HashMap<String, CallContact> cache, String number) {
+ if (number == null || number.isEmpty())
+ return null;
+ number = CallContact.canonicalNumber(number);
+ CallContact c = cache.get(number);
+ if (c == null) {
+ c = findContactByNumber(cr, number);
+ //if (c != null)
+ cache.put(number, c);
+ }
+ return c;
+ }
+
+ Pair<HistoryEntry, HistoryCall> findHistoryByCallId(final Map<String, Conversation> confs, String id) {
+ for (Conversation c : confs.values()) {
+ Pair<HistoryEntry, HistoryCall> h = c.findHistoryByCallId(id);
+ if (h != null)
+ return h;
+ }
+ return null;
+ }
+
+ @Override
+ protected Map<String, Conversation> doInBackground(Void... params) {
+ List<HistoryCall> history = null;
+ List<HistoryText> historyTexts = null;
+ Map<String, Conference> confs = null;
+ final Map<String, Conversation> ret = new HashMap<>();
+ final LongSparseArray<CallContact> localContactCache = new LongSparseArray<>(64);
+ final HashMap<String, CallContact> localNumberCache = new HashMap<>(64);
+
+
+ try {
+ history = historyManager.getAll();
+ historyTexts = historyManager.getAllTextMessages();
+ confs = mService.getConferenceList();
+ } catch (RemoteException | SQLException e) {
+ e.printStackTrace();
+ }
+
+ for (HistoryCall call : history) {
+ //Log.w(TAG, "History call : " + call.getNumber() + " " + call.call_start + " " + call.call_end + " " + call.getEndDate().toString());
+ CallContact contact;
+ if (call.getContactID() <= CallContact.DEFAULT_ID) {
+ contact = getByNumber(localNumberCache, call.getNumber());
+ } else {
+ contact = localContactCache.get(call.getContactID());
+ if (contact == null) {
+ contact = findById(cr, call.getContactID());
+ if (contact != null)
+ contact.addPhoneNumber(call.getNumber(), 0);
+ else {
+ Log.w(TAG, "Can't find contact with id " + call.getContactID());
+ contact = getByNumber(localNumberCache, call.getNumber());
+ }
+ localContactCache.put(contact.getId(), contact);
+ }
+ }
+
+ Map.Entry<String, Conversation> merge = null;
+ for (Map.Entry<String, Conversation> ce : ret.entrySet()) {
+ Conversation c = ce.getValue();
+ if ((contact.getId() > 0 && contact.getId() == c.contact.getId()) || c.contact.hasNumber(call.getNumber())) {
+ merge = ce;
+ break;
+ }
+ }
+ if (merge != null) {
+ Conversation c = merge.getValue();
+ //Log.w(TAG, " Join to " + merge.getKey() + " " + c.getContact().getDisplayName() + " " + call.getNumber());
+ if (c.getContact().getId() <= 0 && contact.getId() > 0) {
+ c.contact = contact;
+ ret.remove(merge.getKey());
+ ret.put(contact.getIds().get(0), c);
+ }
+ c.addHistoryCall(call);
+ continue;
+ }
+ String key = contact.getIds().get(0);
+ if (ret.containsKey(key)) {
+ ret.get(key).addHistoryCall(call);
+ } else {
+ Conversation c = new Conversation(contact);
+ c.addHistoryCall(call);
+ ret.put(key, c);
+ }
+ }
+
+ for (HistoryText htext : historyTexts) {
+ CallContact contact;
+
+ if (htext.getContactID() <= CallContact.DEFAULT_ID) {
+ contact = getByNumber(localNumberCache, htext.getNumber());
+ } else {
+ contact = localContactCache.get(htext.getContactID());
+ if (contact == null) {
+ contact = findById(cr, htext.getContactID());
+ if (contact != null)
+ contact.addPhoneNumber(htext.getNumber(), 0);
+ else {
+ Log.w(TAG, "Can't find contact with id " + htext.getContactID());
+ contact = getByNumber(localNumberCache, htext.getNumber());
+ }
+ localContactCache.put(contact.getId(), contact);
+ }
+ }
+
+ Pair<HistoryEntry, HistoryCall> p = findHistoryByCallId(ret, htext.getCallId());
+
+ if (contact == null && p != null)
+ contact = p.first.getContact();
+ if (contact == null)
+ continue;
+
+ TextMessage msg = new TextMessage(htext);
+ msg.setContact(contact);
+
+ if (p != null) {
+ if (msg.getNumber() == null || msg.getNumber().isEmpty())
+ msg.setNumber(p.second.getNumber());
+ p.first.addTextMessage(msg);
+ }
+
+ String key = contact.getIds().get(0);
+ if (ret.containsKey(key)) {
+ ret.get(key).addTextMessage(msg);
+ } else {
+ Conversation c = new Conversation(contact);
+ c.addTextMessage(msg);
+ ret.put(key, c);
+ }
+ }
+
+ /*context.clear();
+ ctx = null;*/
+ for (Map.Entry<String, Conference> c : confs.entrySet()) {
+ //Log.w(TAG, "ConversationLoader handling " + c.getKey() + " " + c.getValue().getId());
+ Conference conf = c.getValue();
+ ArrayList<SipCall> calls = conf.getParticipants();
+ if (calls.size() >= 1) {
+ CallContact contact = calls.get(0).getContact();
+ //Log.w(TAG, "Contact : " + contact.getId() + " " + contact.getDisplayName());
+ Conversation conv = null;
+ ArrayList<String> ids = contact.getIds();
+ for (String id : ids) {
+ //Log.w(TAG, " uri attempt : " + id);
+ conv = ret.get(id);
+ if (conv != null) break;
+ }
+ if (conv != null) {
+ //Log.w(TAG, "Adding conference to existing conversation ");
+ conv.current_calls.add(conf);
+ } else {
+ conv = new Conversation(contact);
+ conv.current_calls.add(conf);
+ ret.put(ids.get(0), conv);
+ }
+ }
+ }
+ for (Conversation c : ret.values())
+ Log.w(TAG, "Conversation : " + c.getContact().getId() + " " + c.getContact().getDisplayName() + " " + c.getContact().getPhones().get(0).getNumber() + " " + c.getLastInteraction().toString());
+ return ret;
+ }
+ }
+
+ private void updated(Map<String, Conversation> res) {
+ Log.w(TAG, "Conversation list updated");
+ conversations = res;
+ sendBroadcast(new Intent(ACTION_CONF_UPDATE));
+ }
+
+ public class AccountsLoader extends AsyncTaskLoader<ArrayList<Account>> {
+ public static final String ACCOUNTS = "accounts";
+ public static final String ACCOUNT_IP2IP = "IP2IP";
+ public AccountsLoader(Context context) {
+ super(context);
+ Log.w(TAG, "AccountsLoader constructor");
+ }
+ @SuppressWarnings("unchecked")
+ @Override
+ public ArrayList<Account> loadInBackground() {
+ Log.w(TAG, "AccountsLoader loadInBackground");
+ ArrayList<Account> accounts = new ArrayList<>();
+ Account IP2IP = null;
+ try {
+ ArrayList<String> accountIDs = (ArrayList<String>) mService.getAccountList();
+ Map<String, String> details;
+ ArrayList<Map<String, String>> credentials;
+ Map<String, String> state;
+ for (String id : accountIDs) {
+ details = (Map<String, String>) mService.getAccountDetails(id);
+ state = (Map<String, String>) mService.getVolatileAccountDetails(id);
+ if (id.contentEquals(ACCOUNT_IP2IP)) {
+ IP2IP = new Account(ACCOUNT_IP2IP, details, new ArrayList<Map<String, String>>(), state); // Empty credentials
+ //accounts.add(IP2IP);
+ continue;
+ }
+ credentials = (ArrayList<Map<String, String>>) mService.getCredentials(id);
+ /*for (Map.Entry<String, String> entry : State.entrySet()) {
+ Log.i(TAG, "State:" + entry.getKey() + " -> " + entry.getValue());
+ }*/
+ Account tmp = new Account(id, details, credentials, state);
+ accounts.add(tmp);
+ // Log.i(TAG, "account:" + tmp.getAlias() + " " + tmp.isEnabled());
+
+ }
+ } catch (RemoteException | NullPointerException e) {
+ Log.e(TAG, e.toString());
+ }
+ accounts.add(IP2IP);
+ return accounts;
+ }
+ }
+
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch(intent.getAction()) {
+ case ConnectivityManager.CONNECTIVITY_ACTION:
+ Log.w(TAG, "ConnectivityManager.CONNECTIVITY_ACTION " + " " + intent.getStringExtra(ConnectivityManager.EXTRA_EXTRA_INFO) + " " + intent.getStringExtra(ConnectivityManager.EXTRA_EXTRA_INFO));
+ ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo ni = cm.getActiveNetworkInfo();
+ Log.w(TAG, "ActiveNetworkInfo: " + (ni == null ? "null" : ni.toString()));
+ break;
+ case ConfigurationManagerCallback.ACCOUNT_STATE_CHANGED:
+ Log.w(TAG, "Received " + intent.getAction() + " " + intent.getStringExtra("Account") + " " + intent.getStringExtra("State") + " " + intent.getIntExtra("code", 0));
+ //accountStateChanged(intent.getStringExtra("Account"), intent.getStringExtra("State"), intent.getIntExtra("code", 0));
+ for (Account a : accounts) {
+ if (a.getAccountID().contentEquals(intent.getStringExtra("Account"))) {
+ a.setRegistrationState(intent.getStringExtra("State"), intent.getIntExtra("code", 0));
+ //notifyDataSetChanged();
+ sendBroadcast(new Intent(ACTION_ACCOUNT_UPDATE));
+ break;
+ }
+ }
+ break;
+ case ConfigurationManagerCallback.ACCOUNTS_CHANGED:
+ Log.w(TAG, "Received" + intent.getAction());
+ //accountsChanged();
+ mAccountLoader.onContentChanged();
+ mAccountLoader.startLoading();
+ break;
+ case CallManagerCallBack.INCOMING_TEXT:
+ case ConfigurationManagerCallback.INCOMING_TEXT:
+ TextMessage txt = intent.getParcelableExtra("txt");
+ String call = txt.getCallId();
+ if (call != null && !call.isEmpty()) {
+ Conversation conv = getByCallId(call);
+ conv.addTextMessage(txt);
+ /*Conference conf = conv.getConference(call);
+ conf.addSipMessage(txt);
+ Conversation conv = getByContact(conf.)*/
+ } else {
+ CallContact contact = findContactByNumber(txt.getNumber());
+ Conversation conv = startConversation(contact);
+ txt.setContact(conv.getContact());
+ Log.w(TAG, "New text messsage " + txt.getAccount() + " " + txt.getContact().getId() + " " + txt.getMessage());
+ conv.addTextMessage(txt);
+ }
+ sendBroadcast(new Intent(ACTION_CONF_UPDATE));
+ break;
+ default:
+ Log.w(TAG, "onReceive " + intent.getAction() + " " + intent.getDataString());
+ new ConversationLoader(context){
+ @Override
+ protected void onPostExecute(Map<String, Conversation> res) {
+ updated(res);
+ }
+ }.execute();
+ }
+ }
+ };
+
+ public void startListener() {
+ final WeakReference<LocalService> self = new WeakReference<>(this);
+ new ConversationLoader(this){
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ Log.w(TAG, "onPreExecute");
+ }
+
+ @Override
+ protected void onPostExecute(Map<String, Conversation> res) {
+ Log.w(TAG, "onPostExecute");
+ LocalService this_ = self.get();
+ if (this_ != null)
+ this_.updated(res);
+ else
+ Log.e(TAG, "AsyncTask finished but parent is destroyed..");
+ }
+ }.execute();
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ConfigurationManagerCallback.ACCOUNT_STATE_CHANGED);
+ intentFilter.addAction(ConfigurationManagerCallback.ACCOUNTS_CHANGED);
+ intentFilter.addAction(ConfigurationManagerCallback.INCOMING_TEXT);
+
+ intentFilter.addAction(CallManagerCallBack.INCOMING_CALL);
+ intentFilter.addAction(CallManagerCallBack.INCOMING_TEXT);
+ intentFilter.addAction(CallManagerCallBack.CALL_STATE_CHANGED);
+ intentFilter.addAction(CallManagerCallBack.CONF_CREATED);
+ intentFilter.addAction(CallManagerCallBack.CONF_CHANGED);
+ intentFilter.addAction(CallManagerCallBack.CONF_REMOVED);
+
+ intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+
+ registerReceiver(receiver, intentFilter);
+ }
+
+ public void stopListener() {
+ unregisterReceiver(receiver);
+ }
+
+}
diff --git a/ring-android/app/src/main/java/cx/ring/service/SipService.java b/ring-android/app/src/main/java/cx/ring/service/SipService.java
index 8f9caae..1e3ea96 100644
--- a/ring-android/app/src/main/java/cx/ring/service/SipService.java
+++ b/ring-android/app/src/main/java/cx/ring/service/SipService.java
@@ -24,6 +24,7 @@
*/
package cx.ring.service;
+import android.app.Notification;
import android.os.Handler;
import java.util.ArrayList;
@@ -36,12 +37,21 @@
import android.app.Service;
import android.content.Intent;
import android.os.*;
+import android.telecom.Call;
import android.util.Log;
+
+import cx.ring.BuildConfig;
+import cx.ring.R;
import cx.ring.history.HistoryManager;
+import cx.ring.history.HistoryText;
import cx.ring.model.Codec;
import cx.ring.model.Conference;
import cx.ring.model.SecureSipCall;
-import cx.ring.model.SipMessage;
+import cx.ring.model.TextMessage;
+import cx.ring.model.account.AccountDetail;
+import cx.ring.model.account.AccountDetailBasic;
+import cx.ring.model.account.AccountDetailSrtp;
+import cx.ring.model.account.AccountDetailTls;
import cx.ring.utils.MediaManager;
import cx.ring.utils.SipNotifications;
import cx.ring.utils.SwigNativeConverter;
@@ -54,8 +64,14 @@
private SipServiceExecutor mExecutor;
private static HandlerThread executorThread;
+ static public final String ACTION_CALL_ACCEPT = BuildConfig.APPLICATION_ID + ".action.CALL_ACCEPT";
+ static public final String ACTION_CALL_REFUSE = BuildConfig.APPLICATION_ID + ".action.CALL_REFUSE";
+ //static public final String ACTION_CALL_REFUSE = BuildConfig.APPLICATION_ID + ".action.CALL_REFUSE";
+
+ static public final String ACTION_CALL_END = BuildConfig.APPLICATION_ID + ".action.CALL_END";
+
private Handler handler = new Handler();
- private static int POLLING_TIMEOUT = 500;
+ private static int POLLING_TIMEOUT = 50;
private Runnable pollEvents = new Runnable() {
@Override
public void run() {
@@ -74,7 +90,7 @@
protected HistoryManager mHistoryManager;
protected MediaManager mMediaManager;
- private HashMap<String, Conference> mConferences = new HashMap<>();
+ private final HashMap<String, Conference> mConferences = new HashMap<>();
private ConfigurationManagerCallback configurationCallback;
private CallManagerCallBack callManagerCallBack;
@@ -141,8 +157,36 @@
/* called for each startService() */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- Log.i(TAG, "onStarted " + (intent == null ? "null" : intent.getAction()) + " " + flags);
- super.onStartCommand(intent, flags, startId);
+ Log.i(TAG, "onStartCommand " + (intent == null ? "null" : intent.getAction()) + " " + flags + " " + startId);
+ String action = intent == null ? null : intent.getAction();
+ try {
+ if (action != null) {
+ if (action.equals(ACTION_CALL_END)) {
+ Conference c = findConference(intent.getStringExtra("conf"));
+ if (c != null) {
+ for (SipCall call : c.getParticipants()) {
+ mBinder.hangUp(call.getCallId());
+ }
+ mBinder.hangUpConference(c.getId());
+ Log.w("CallNotification ", "Canceling " + c.notificationId);
+ mNotificationManager.notificationManager.cancel(c.notificationId);
+ }
+ } else if (action.equals(ACTION_CALL_ACCEPT)) {
+ Conference c = findConference(intent.getStringExtra("conf"));
+ if (c != null) {
+ mBinder.accept(c.getParticipants().get(0).getCallId());
+ }
+ } else if (action.equals(ACTION_CALL_REFUSE)) {
+ Conference c = findConference(intent.getStringExtra("conf"));
+ if (c != null) {
+ mBinder.refuse(c.getParticipants().get(0).getCallId());
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
return START_STICKY; /* started and stopped explicitly */
}
@@ -265,6 +309,8 @@
public void run() {
try {
mTask.run();
+ } catch(Exception e){
+ e.printStackTrace();
} finally {
synchronized (this) {
mDone = true;
@@ -419,27 +465,59 @@
protected String doRun() throws SameThreadException {
Log.i(TAG, "SipService.placeCall() thread running...");
Conference toAdd;
- if(call.getAccount().useSecureLayer()){
- SecureSipCall secureCall = new SecureSipCall(call);
- toAdd = new Conference(secureCall);
- } else {
- toAdd = new Conference(call);
- }
- mConferences.put(toAdd.getId(), toAdd);
+ //mConferences.put(toAdd.getId(), toAdd);
mMediaManager.obtainAudioFocus(false);
- return Ringservice.placeCall(call.getAccount().getAccountID(), call.getmContact().getPhones().get(0).getNumber());
+
+ String number = call.getNumber();
+ if ((number == null || number.isEmpty()) && call.getContact() != null) {
+ number = call.getContact().getPhones().get(0).getNumber();
+ }
+
+ Log.i(TAG, "SipService.placeCall() calling... " + number);
+ String call_id = Ringservice.placeCall(call.getAccount(), number);
+ call.setCallID(call_id);
+ if (!call_id.isEmpty()) {
+ final Map<String, String> details = getAccountDetails(call.getAccount());
+ if(details.get(AccountDetailBasic.CONFIG_ACCOUNT_TYPE).contentEquals(AccountDetailBasic.ACCOUNT_TYPE_RING)
+ || details.get(AccountDetailSrtp.CONFIG_SRTP_ENABLE).contentEquals(AccountDetail.TRUE_STR)
+ || details.get(AccountDetailTls.CONFIG_TLS_ENABLE).contentEquals(AccountDetail.TRUE_STR)) {
+ Log.i(TAG, "SipService.placeCall() call is secure");
+ SecureSipCall secureCall = new SecureSipCall(call, details.get(AccountDetailSrtp.CONFIG_SRTP_KEY_EXCHANGE));
+ toAdd = new Conference(secureCall);
+ } else {
+ toAdd = new Conference(call);
+ }
+ Log.i(TAG, "SipService.placeCall() returned with call id " + call_id);
+ mConferences.put(call_id, toAdd);
+ Notification noti = new Notification.Builder(SipService.this)
+ .setContentTitle("Ongoing call with " + call.getContact().getDisplayName())
+ .setContentText("outgoing call")
+ .setOngoing(true)
+ .setSmallIcon(R.drawable.ic_launcher)
+ //.setContentIntent()
+ /*.setContentText(subject)
+ .setSmallIcon(R.drawable.new_mail)
+ .setLargeIcon(aBitmap)*/
+ .build();
+ //startForeground(toAdd.notificationId, noti);
+ Log.w("CallNotification ", "Adding for outgoing " + toAdd.notificationId);
+ mNotificationManager.notificationManager.notify(toAdd.notificationId, noti);
+ }
+ return call_id;
}
});
}
@Override
public void refuse(final String callID) {
-
+ mMediaManager.stopRing();
+ Log.e(TAG, "REFUSE");
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException {
Log.i(TAG, "SipService.refuse() thread running...");
Ringservice.refuse(callID);
+ Ringservice.hangUp(callID);
}
});
}
@@ -447,6 +525,7 @@
@Override
public void accept(final String callID) {
mMediaManager.stopRing();
+ Log.e(TAG, "ACCEPT");
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException {
@@ -925,28 +1004,38 @@
}
@Override
- public void sendTextMessage(final String callID, final SipMessage message) throws RemoteException {
+ public void sendTextMessage(final String callID, final TextMessage message) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.sendTextMessage() thread running...");
+ message.setCallId(callID);
+ //Conference conf = findConference(callID);
+ mHistoryManager.insertNewTextMessage(new HistoryText(message));
StringMap messages = new StringMap();
- messages.set("text/plain", message.comment);
+ messages.set("text/plain", message.getMessage());
Ringservice.sendTextMessage(callID, messages, "", false);
if (getConferences().get(callID) != null)
getConferences().get(callID).addSipMessage(message);
+ Intent intent = new Intent(CallManagerCallBack.INCOMING_TEXT);
+ intent.putExtra("txt", message);
+ sendBroadcast(intent);
}
});
-
}
@Override
- public void sendAccountTextMessage(final String accountid, final String to, final String msg) {
+ public void sendAccountTextMessage(final String accountID, final String to, final String msg) {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
- Log.i(TAG, "SipService.sendAccountTextMessage() thread running... " + accountid + " " + to + " " + msg);
- Ringservice.sendAccountTextMessage(accountid, to, msg);
+ Log.i(TAG, "SipService.sendAccountTextMessage() thread running... " + accountID + " " + to + " " + msg);
+ TextMessage message = new TextMessage(false, msg, to, null, accountID);
+ mHistoryManager.insertNewTextMessage(new HistoryText(message));
+ Ringservice.sendAccountTextMessage(accountID, to, msg);
+ Intent intent = new Intent(ConfigurationManagerCallback.INCOMING_TEXT);
+ intent.putExtra("txt", message);
+ sendBroadcast(intent);
}
});
}
@@ -1271,22 +1360,22 @@
getConferences().remove(conf.getId());
else
conf.removeParticipant(conf.getCallById(callID));
+ Log.w("CallNotification ", "Canceling " + conf.notificationId);
+ mNotificationManager.notificationManager.cancel(conf.notificationId);
}
protected Conference findConference(String callID) {
- Conference result = null;
- if (getConferences().get(callID) != null) {
- result = getConferences().get(callID);
- } else {
- for (Entry<String, Conference> stringConferenceEntry : getConferences().entrySet()) {
- Conference tmp = stringConferenceEntry.getValue();
- for (SipCall c : tmp.getParticipants()) {
- if (c.getCallId().contentEquals(callID)) {
- result = tmp;
- }
+ Conference result = getConferences().get(callID);
+ if (result != null)
+ return result;
+ for (Entry<String, Conference> stringConferenceEntry : getConferences().entrySet()) {
+ Conference tmp = stringConferenceEntry.getValue();
+ for (SipCall c : tmp.getParticipants()) {
+ if (c.getCallId() != null && callID.contentEquals(c.getCallId())) {
+ return tmp;
}
}
}
- return result;
+ return null;
}
}
diff --git a/ring-android/app/src/main/java/cx/ring/utils/MediaManager.java b/ring-android/app/src/main/java/cx/ring/utils/MediaManager.java
index e1abe4e..c9b59cb 100644
--- a/ring-android/app/src/main/java/cx/ring/utils/MediaManager.java
+++ b/ring-android/app/src/main/java/cx/ring/utils/MediaManager.java
@@ -115,7 +115,7 @@
}
- /**
+ /**5
* Start ringing announce for a given contact.
* It will also focus audio for us.
* @param remoteContact the contact to ring for. May resolve the contact ringtone if any.
diff --git a/ring-android/app/src/main/java/cx/ring/utils/SipNotifications.java b/ring-android/app/src/main/java/cx/ring/utils/SipNotifications.java
index 356552b..76fd929 100644
--- a/ring-android/app/src/main/java/cx/ring/utils/SipNotifications.java
+++ b/ring-android/app/src/main/java/cx/ring/utils/SipNotifications.java
@@ -41,8 +41,6 @@
import cx.ring.model.Conference;
import cx.ring.model.SipCall;
-import android.app.Notification;
-import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -51,6 +49,7 @@
import android.net.sip.SipProfile;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Builder;
+import android.support.v4.app.NotificationManagerCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
@@ -58,7 +57,7 @@
public class SipNotifications {
- private final NotificationManager notificationManager;
+ public final NotificationManagerCompat notificationManager;
private final Context context;
public static final String NOTIF_CREATION = "notif_creation";
@@ -76,7 +75,8 @@
public SipNotifications(Context aContext) {
context = aContext;
- notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager = NotificationManagerCompat.from(aContext);
+ ;//(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (!isInit) {
cancelAll();
@@ -158,7 +158,7 @@
nb.setTicker(tickerText);
nb.setWhen(when);
nb.setContentTitle(context.getString(R.string.notif_missed_call_title));
- nb.setContentText(context.getString(R.string.notif_missed_call_content, missedConf.getParticipants().get(0).getmContact().getmDisplayName()));
+ nb.setContentText(context.getString(R.string.notif_missed_call_content, missedConf.getParticipants().get(0).getContact().getDisplayName()));
Intent notificationIntent = new Intent(context, HomeActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
@@ -168,12 +168,11 @@
nb.setOnlyAlertOnce(true);
nb.setContentIntent(contentIntent);
- Notification notification = nb.build();
// We have to re-write content view because getNotification setLatestEventInfo implicitly
// notification.contentView = contentView;
// startForegroundCompat(CALL_NOTIF_ID, notification);
- notificationManager.notify(CALL_NOTIF_ID, notification);
+ notificationManager.notify(CALL_NOTIF_ID, nb.build());
}
public void makeNotification(HashMap<String, SipCall> calls) {
@@ -183,21 +182,20 @@
Intent notificationIntent = new Intent(context, HomeActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(context, 007, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- nm.cancel(NOTIFICATION_ID); // clear previous notifications.
+ //NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.cancel(NOTIFICATION_ID); // clear previous notifications.
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setContentIntent(contentIntent).setOngoing(true).setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(calls.size() + " ongoing calls").setTicker("Pending calls").setWhen(System.currentTimeMillis()).setAutoCancel(false);
builder.setPriority(NotificationCompat.PRIORITY_MAX);
- Notification n = builder.build();
- nm.notify(NOTIFICATION_ID, n);
+ notificationManager.notify(NOTIFICATION_ID, builder.build());
}
public void removeNotification() {
- NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- nm.cancel(NOTIFICATION_ID);
+ //NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.cancel(NOTIFICATION_ID);
}
}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/utils/Utilities.java b/ring-android/app/src/main/java/cx/ring/utils/Utilities.java
new file mode 100644
index 0000000..1ac1337
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/utils/Utilities.java
@@ -0,0 +1,35 @@
+package cx.ring.utils;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * This class provides a variety of basic utility methods that are not
+ * dependent on any other classes within the org.jamwiki package structure.
+ */
+public class Utilities {
+ private static final String ipv4Pattern = "(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])";
+ private static final String ipv6Pattern = "([0-9a-f]{1,4}:){7}([0-9a-f]){1,4}";
+ private static final Pattern VALID_IPV4_PATTERN = Pattern.compile(ipv4Pattern, Pattern.CASE_INSENSITIVE);
+ private static final Pattern VALID_IPV6_PATTERN = Pattern.compile(ipv6Pattern, Pattern.CASE_INSENSITIVE);
+
+ /**
+ * Determine if the given string is a valid IPv4 or IPv6 address. This method
+ * uses pattern matching to see if the given string could be a valid IP address.
+ *
+ * @param ipAddress A string that is to be examined to verify whether or not
+ * it could be a valid IP address.
+ * @return <code>true</code> if the string is a value that is a valid IP address,
+ * <code>false</code> otherwise.
+ */
+ public static boolean isIpAddress(String ipAddress) {
+
+ Matcher m1 = VALID_IPV4_PATTERN.matcher(ipAddress);
+ if (m1.matches()) {
+ return true;
+ }
+ Matcher m2 = VALID_IPV6_PATTERN.matcher(ipAddress);
+ return m2.matches();
+ }
+}
diff --git a/ring-android/app/src/main/java/cx/ring/views/CallPaneLayout.java b/ring-android/app/src/main/java/cx/ring/views/CallPaneLayout.java
index c9bd686..cd2afcf 100644
--- a/ring-android/app/src/main/java/cx/ring/views/CallPaneLayout.java
+++ b/ring-android/app/src/main/java/cx/ring/views/CallPaneLayout.java
@@ -60,6 +60,7 @@
super(context, attrs, defStyle);
}
+ /*
@Override
public boolean onInterceptTouchEvent(MotionEvent event)
{
@@ -68,6 +69,6 @@
}
return super.onInterceptTouchEvent(event);
- }
+ }*/
}
diff --git a/ring-android/app/src/main/java/cx/ring/views/HalfCircleImageView.java b/ring-android/app/src/main/java/cx/ring/views/HalfCircleImageView.java
index e9b0d6a..46ed0d6 100644
--- a/ring-android/app/src/main/java/cx/ring/views/HalfCircleImageView.java
+++ b/ring-android/app/src/main/java/cx/ring/views/HalfCircleImageView.java
@@ -76,7 +76,7 @@
private void setup()
{
backgroundPaint = new Paint();
- backgroundPaint.setColor(getResources().getColor(R.color.sfl_dark_blue));
+ backgroundPaint.setColor(getResources().getColor(R.color.color_primary_dark));
backgroundPaint.setAntiAlias(true);
// init paint
paint = new Paint();
diff --git a/ring-android/app/src/main/java/cx/ring/views/NumberPickerPreference.java b/ring-android/app/src/main/java/cx/ring/views/NumberPickerPreference.java
index bbf7342..cc6b156 100644
--- a/ring-android/app/src/main/java/cx/ring/views/NumberPickerPreference.java
+++ b/ring-android/app/src/main/java/cx/ring/views/NumberPickerPreference.java
@@ -92,7 +92,7 @@
throw new RuntimeException("mNumberPicker is null!");
}
- // Initialize state
+ // Initialize State
mNumberPicker.setWrapSelectorWheel(false);
mNumberPicker.setMaxValue(max);
mNumberPicker.setMinValue(min);
diff --git a/ring-android/app/src/main/java/cx/ring/views/QuadNumberPickerPreference.java b/ring-android/app/src/main/java/cx/ring/views/QuadNumberPickerPreference.java
index 49bf9d3..3604fd2 100644
--- a/ring-android/app/src/main/java/cx/ring/views/QuadNumberPickerPreference.java
+++ b/ring-android/app/src/main/java/cx/ring/views/QuadNumberPickerPreference.java
@@ -127,7 +127,7 @@
throw new RuntimeException("mNumberPicker1 or mNumberPicker2 is null!");
}
- // Initialize state
+ // Initialize State
mNumberPicker1.setWrapSelectorWheel(false);
mNumberPicker1.setMaxValue(mMax1);
mNumberPicker1.setMinValue(mMin1);
diff --git a/ring-android/app/src/main/java/cx/ring/views/SwipeListViewTouchListener.java b/ring-android/app/src/main/java/cx/ring/views/SwipeListViewTouchListener.java
deleted file mode 100644
index 8e05466..0000000
--- a/ring-android/app/src/main/java/cx/ring/views/SwipeListViewTouchListener.java
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * Copyright (C) 2004-2014 Savoir-Faire Linux Inc.
- *
- * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Additional permission under GNU GPL version 3 section 7:
- *
- * If you modify this program, or any covered work, by linking or
- * combining it with the OpenSSL project's OpenSSL library (or a
- * modified version of that library), containing parts covered by the
- * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
- * grants you additional permission to convey the resulting work.
- * Corresponding Source for a non-source form of such a combination
- * shall include the source code for the parts of OpenSSL used as well
- * as that of the covered work.
- */
-
-package cx.ring.views;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import cx.ring.R;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.graphics.Rect;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.ListView;
-
-public class SwipeListViewTouchListener implements View.OnTouchListener {
- // Cached ViewConfiguration and system-wide constant values
- private int mSlop;
- private int mMinFlingVelocity;
- private int mMaxFlingVelocity;
- private long mAnimationTime;
-
- private static final String TAG = SwipeListViewTouchListener.class.getSimpleName();
-
- // Fixed properties
- private ListView mListView;
- private OnSwipeCallback mCallback;
- private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero
- private boolean dismissLeft = true;
- private boolean dismissRight = true;
-
- // Transient properties
- private List<PendingSwipeData> mPendingSwipes = new ArrayList<PendingSwipeData>();
- private int mDismissAnimationRefCount = 0;
- private float mDownX;
- private float mDownY;
- private boolean mSwiping;
- private VelocityTracker mVelocityTracker;
- private int mDownPosition;
- private View mDownView, mUnderDownView;
- private boolean mPaused;
-
- /**
- * The callback interface used by {@link SwipeListViewTouchListener} to inform its client about a successful swipe of one or more list item
- * positions.
- */
- public interface OnSwipeCallback {
- /**
- * Called when the user has swiped the list item to the left.
- *
- * @param listView
- * The originating {@link ListView}.
- * @param reverseSortedPositions
- * An array of positions to dismiss, sorted in descending order for convenience.
- */
- void onSwipeLeft(ListView listView, int[] reverseSortedPositions);
-
- void onSwipeRight(ListView listView, View downView);
- }
-
- /**
- * Constructs a new swipe-to-action touch listener for the given list view.
- *
- * @param listView
- * The list view whose items should be dismissable.
- * @param callback
- * The callback to trigger when the user has indicated that she would like to dismiss one or more list items.
- */
- public SwipeListViewTouchListener(ListView listView, OnSwipeCallback callback) {
- ViewConfiguration vc = ViewConfiguration.get(listView.getContext());
- mSlop = vc.getScaledTouchSlop();
- mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
- mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
- mAnimationTime = listView.getContext().getResources().getInteger(android.R.integer.config_shortAnimTime);
- mListView = listView;
- mCallback = callback;
- }
-
- /**
- * Constructs a new swipe-to-action touch listener for the given list view.
- *
- * @param listView
- * The list view whose items should be dismissable.
- * @param callback
- * The callback to trigger when the user has indicated that she would like to dismiss one or more list items.
- * @param dismissLeft
- * set if the dismiss animation is up when the user swipe to the left
- * @param dismissRight
- * set if the dismiss animation is up when the user swipe to the right
- * @see #SwipeListViewTouchListener(ListView, OnSwipeCallback, boolean, boolean)
- */
- public SwipeListViewTouchListener(ListView listView, OnSwipeCallback callback, boolean dismissLeft, boolean dismissRight) {
- this(listView, callback);
- this.dismissLeft = dismissLeft;
- this.dismissRight = dismissRight;
- }
-
- /**
- * Enables or disables (pauses or resumes) watching for swipe-to-dismiss gestures.
- *
- * @param enabled
- * Whether or not to watch for gestures.
- */
- public void setEnabled(boolean enabled) {
- mPaused = !enabled;
- }
-
- /**
- * Returns an {@link android.widget.AbsListView.OnScrollListener} to be added to the {@link ListView} using
- * {@link ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener)}. If a scroll listener is already assigned, the caller should
- * still pass scroll changes through to this listener. This will ensure that this {@link SwipeListViewTouchListener} is paused during list view
- * scrolling.</p>
- *
- * @see {@link SwipeListViewTouchListener}
- */
- public AbsListView.OnScrollListener makeScrollListener() {
- return new AbsListView.OnScrollListener() {
- @Override
- public void onScrollStateChanged(AbsListView absListView, int scrollState) {
- setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
- }
-
- @Override
- public void onScroll(AbsListView absListView, int i, int i1, int i2) {
- }
- };
- }
-
- @Override
- public boolean onTouch(View item, MotionEvent motionEvent) {
- if (mViewWidth < 2) {
- mViewWidth = mListView.getWidth();
- }
-
- switch (motionEvent.getActionMasked()) {
- case MotionEvent.ACTION_DOWN: {
- if (mPaused) {
- return false;
- }
-
- // TODO: ensure this is a finger, and set a flag
-
- // Find the child view that was touched (perform a hit test)
- Rect rect = new Rect();
- int childCount = mListView.getChildCount();
- int[] listViewCoords = new int[2];
- mListView.getLocationOnScreen(listViewCoords);
- int x = (int) motionEvent.getRawX() - listViewCoords[0];
- int y = (int) motionEvent.getRawY() - listViewCoords[1];
- View child;
- for (int i = 0; i < childCount; i++) {
- child = mListView.getChildAt(i);
- child.getHitRect(rect);
- if (rect.contains(x, y)) {
- mDownView = child.findViewById(R.id.contactview);
- mUnderDownView = child.findViewById(R.id.contact_underview);
- break;
- }
- }
-
- if (mDownView != null) {
-
- mDownX = motionEvent.getRawX() - mDownView.getTranslationX();
- mDownY = motionEvent.getRawY();
- mDownPosition = mListView.getPositionForView(mDownView);
-
- mVelocityTracker = VelocityTracker.obtain();
- mVelocityTracker.addMovement(motionEvent);
- }
- item.onTouchEvent(motionEvent);
- return true;
- }
-
- case MotionEvent.ACTION_UP: {
- if (mVelocityTracker == null) {
- break;
- }
-
- float deltaX = motionEvent.getRawX() - mDownX;
-
- mVelocityTracker.addMovement(motionEvent);
- mVelocityTracker.computeCurrentVelocity(500); // 1000 by defaut but it was too much
- float velocityX = Math.abs(mVelocityTracker.getXVelocity());
- float velocityY = Math.abs(mVelocityTracker.getYVelocity());
- boolean swipe = false;
- boolean swipeRight = false;
-
- if (mDownView.getTranslationX() > mViewWidth / 2) {
- swipe = true;
- swipeRight = deltaX > 0;
- } else if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity && velocityY < velocityX) {
- swipe = true;
- swipeRight = mVelocityTracker.getXVelocity() > 0;
- }
- if (swipe) {
- // sufficent swipe value
- final View downView = mDownView; // mDownView gets null'd before animation ends
- final int downPosition = mDownPosition;
- final boolean toTheRight = swipeRight;
- ++mDismissAnimationRefCount;
-
- if (toTheRight) {
- mDownView.animate().translationX(mViewWidth / 2).alpha(1).setDuration(mAnimationTime).setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mListView.requestDisallowInterceptTouchEvent(false);
- // mCallback.onSwipeRight(mListView, mUnderDownView);
- toggleUnderLayerState(true);
- // performSwipeAction(downView, downPosition, toTheRight,dismissRight);
- }
- });
- } else {
- mDownView.animate().translationX(0).alpha(1).setDuration(mAnimationTime);
- }
-
- } else {
- // cancel
- mDownView.animate().translationX(0).alpha(1).setDuration(mAnimationTime).setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- toggleUnderLayerState(false);
- mUnderDownView = null;
- }
- });
- }
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- mDownX = 0;
- mDownView = null;
- mDownPosition = ListView.INVALID_POSITION;
- mSwiping = false;
- break;
- }
-
- case MotionEvent.ACTION_MOVE: {
- if (mVelocityTracker == null || mPaused) {
- break;
- }
-
- mVelocityTracker.addMovement(motionEvent);
- mVelocityTracker.computeCurrentVelocity(500);
- float deltaX = motionEvent.getRawX() - mDownX;
- float deltaY = motionEvent.getRawY() - mDownY;
-
- if (Math.abs(deltaX) < Math.abs(deltaY)) {
- mListView.requestDisallowInterceptTouchEvent(false);
- return false;
- }
-
- if (Math.abs(deltaX) > mSlop) {
- mSwiping = true;
- mListView.requestDisallowInterceptTouchEvent(true);
-
- // Cancel ListView's touch (un-highlighting the item)
- MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
- cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
- mListView.onTouchEvent(cancelEvent);
- cancelEvent.recycle();
- }
- if (deltaX < 0)
- return true;
-
- if (mSwiping) {
- mDownView.setTranslationX(deltaX);
- // mDownView.setAlpha(Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / mViewWidth)));
- return true;
- }
- break;
- }
- }
- return false;
- }
-
- private void toggleUnderLayerState(boolean b) {
- if (mUnderDownView == null)
- return;
- mUnderDownView.findViewById(R.id.quick_edit).setClickable(b);
- mUnderDownView.findViewById(R.id.quick_discard).setClickable(b);
- mUnderDownView.findViewById(R.id.quick_starred).setClickable(b);
- }
-
- class PendingSwipeData implements Comparable<PendingSwipeData> {
- public int position;
- public View view;
-
- public PendingSwipeData(int position, View view) {
- this.position = position;
- this.view = view;
- }
-
- @Override
- public int compareTo(PendingSwipeData other) {
- // Sort by descending position
- return other.position - position;
- }
- }
-
- private void performSwipeAction(final View swipeView, final int swipePosition, boolean toTheRight, boolean dismiss) {
- // Animate the dismissed list item to zero-height and fire the dismiss callback when
- // all dismissed list item animations have completed. This triggers layout on each animation
- // frame; in the future we may want to do something smarter and more performant.
-
- final ViewGroup.LayoutParams lp = swipeView.getLayoutParams();
- final int originalHeight = swipeView.getHeight();
- final boolean swipeRight = toTheRight;
-
- ValueAnimator animator;
- if (dismiss)
- animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime);
- else
- animator = ValueAnimator.ofInt(originalHeight, originalHeight - 1).setDuration(mAnimationTime);
-
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- --mDismissAnimationRefCount;
- if (mDismissAnimationRefCount == 0) {
- // No active animations, process all pending dismisses.
- // Sort by descending position
- Collections.sort(mPendingSwipes);
-
- int[] swipePositions = new int[mPendingSwipes.size()];
- for (int i = mPendingSwipes.size() - 1; i >= 0; i--) {
- swipePositions[i] = mPendingSwipes.get(i).position;
- }
- // if (swipeRight)
- // mCallback.onSwipeRight(mListView, swipePositions);
- // else
- // mCallback.onSwipeLeft(mListView, swipePositions);
-
- ViewGroup.LayoutParams lp;
- for (PendingSwipeData pendingDismiss : mPendingSwipes) {
- // Reset view presentation
- pendingDismiss.view.setAlpha(1f);
- pendingDismiss.view.setTranslationX(0);
- lp = pendingDismiss.view.getLayoutParams();
- lp.height = originalHeight;
- pendingDismiss.view.setLayoutParams(lp);
- }
-
- mPendingSwipes.clear();
- }
- }
- });
-
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- lp.height = (Integer) valueAnimator.getAnimatedValue();
- swipeView.setLayoutParams(lp);
- }
- });
-
- mPendingSwipes.add(new PendingSwipeData(swipePosition, swipeView));
- animator.start();
- }
-
- public void openItem(View child, int pos, long id) {
-
- mDownView = child.findViewById(R.id.contactview);
- mUnderDownView = child.findViewById(R.id.contact_underview);
- if (mDownView.getTranslationX() > 0)
- return;
- mDownView.animate().translationX(mViewWidth / 2).setDuration(mAnimationTime).setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mListView.requestDisallowInterceptTouchEvent(false);
- toggleUnderLayerState(true);
- // performSwipeAction(downView, downPosition, toTheRight,dismissRight);
- }
- });
- }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/AdapterWrapper.java b/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/AdapterWrapper.java
deleted file mode 100644
index 22b02a8..0000000
--- a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/AdapterWrapper.java
+++ /dev/null
@@ -1,225 +0,0 @@
-package cx.ring.views.stickylistheaders;
-
-import java.util.LinkedList;
-import java.util.List;
-
-import android.content.Context;
-import android.database.DataSetObserver;
-import android.graphics.drawable.Drawable;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.Checkable;
-import android.widget.ListAdapter;
-
-/**
- * A {@link ListAdapter} which wraps a {@link StickyListHeadersAdapter} and
- * automatically handles wrapping the result of
- * {@link StickyListHeadersAdapter#getView(int, android.view.View, android.view.ViewGroup)}
- * and
- * {@link StickyListHeadersAdapter#getHeaderView(int, android.view.View, android.view.ViewGroup)}
- * appropriately.
- *
- * @author Jake Wharton (jakewharton@gmail.com)
- */
-class AdapterWrapper extends BaseAdapter implements StickyListHeadersAdapter {
-
- interface OnHeaderClickListener {
- public void onHeaderClick(View header, int itemPosition, long headerId);
- }
-
- final StickyListHeadersAdapter mDelegate;
- private final List<View> mHeaderCache = new LinkedList<View>();
- private final Context mContext;
- private Drawable mDivider;
- private int mDividerHeight;
- private OnHeaderClickListener mOnHeaderClickListener;
- private DataSetObserver mDataSetObserver = new DataSetObserver() {
-
- @Override
- public void onInvalidated() {
- mHeaderCache.clear();
- AdapterWrapper.super.notifyDataSetInvalidated();
- }
-
- @Override
- public void onChanged() {
- AdapterWrapper.super.notifyDataSetChanged();
- }
- };
-
- AdapterWrapper(Context context,
- StickyListHeadersAdapter delegate) {
- this.mContext = context;
- this.mDelegate = delegate;
- delegate.registerDataSetObserver(mDataSetObserver);
- }
-
- void setDivider(Drawable divider, int dividerHeight) {
- this.mDivider = divider;
- this.mDividerHeight = dividerHeight;
- notifyDataSetChanged();
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return mDelegate.areAllItemsEnabled();
- }
-
- @Override
- public boolean isEnabled(int position) {
- return mDelegate.isEnabled(position);
- }
-
- @Override
- public int getCount() {
- return mDelegate.getCount();
- }
-
- @Override
- public Object getItem(int position) {
- return mDelegate.getItem(position);
- }
-
- @Override
- public long getItemId(int position) {
- return mDelegate.getItemId(position);
- }
-
- @Override
- public boolean hasStableIds() {
- return mDelegate.hasStableIds();
- }
-
- @Override
- public int getItemViewType(int position) {
- return mDelegate.getItemViewType(position);
- }
-
- @Override
- public int getViewTypeCount() {
- return mDelegate.getViewTypeCount();
- }
-
- @Override
- public boolean isEmpty() {
- return mDelegate.isEmpty();
- }
-
- /**
- * Will recycle header from {@link WrapperView} if it exists
- */
- private void recycleHeaderIfExists(WrapperView wv) {
- View header = wv.mHeader;
- if (header != null) {
- // reset the headers visibility when adding it to the cache
- header.setVisibility(View.VISIBLE);
- mHeaderCache.add(header);
- }
- }
-
- /**
- * Get a header view. This optionally pulls a header from the supplied
- * {@link WrapperView} and will also recycle the divider if it exists.
- */
- private View configureHeader(WrapperView wv, final int position) {
- View header = wv.mHeader == null ? popHeader() : wv.mHeader;
- header = mDelegate.getHeaderView(position, header, wv);
- if (header == null) {
- throw new NullPointerException("Header view must not be null.");
- }
- //if the header isn't clickable, the listselector will be drawn on top of the header
- header.setClickable(true);
- header.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if(mOnHeaderClickListener != null){
- long headerId = mDelegate.getHeaderId(position);
- mOnHeaderClickListener.onHeaderClick(v, position, headerId);
- }
- }
- });
- return header;
- }
-
- private View popHeader() {
- if(mHeaderCache.size() > 0) {
- return mHeaderCache.remove(0);
- }
- return null;
- }
-
- /** Returns {@code true} if the previous position has the same header ID. */
- private boolean previousPositionHasSameHeader(int position) {
- return position != 0
- && mDelegate.getHeaderId(position) == mDelegate
- .getHeaderId(position - 1);
- }
-
- @Override
- public WrapperView getView(int position, View convertView, ViewGroup parent) {
- WrapperView wv = (convertView == null) ? new WrapperView(mContext) : (WrapperView) convertView;
- View item = mDelegate.getView(position, wv.mItem, parent);
- View header = null;
- if (previousPositionHasSameHeader(position)) {
- recycleHeaderIfExists(wv);
- } else {
- header = configureHeader(wv, position);
- }
- if((item instanceof Checkable) && !(wv instanceof CheckableWrapperView)) {
- // Need to create Checkable subclass of WrapperView for ListView to work correctly
- wv = new CheckableWrapperView(mContext);
- } else if(!(item instanceof Checkable) && (wv instanceof CheckableWrapperView)) {
- wv = new WrapperView(mContext);
- }
- wv.update(item, header, mDivider, mDividerHeight);
- return wv;
- }
-
- public void setOnHeaderClickListener(OnHeaderClickListener onHeaderClickListener){
- this.mOnHeaderClickListener = onHeaderClickListener;
- }
-
- @Override
- public boolean equals(Object o) {
- return mDelegate.equals(o);
- }
-
- @Override
- public View getDropDownView(int position, View convertView, ViewGroup parent) {
- return ((BaseAdapter) mDelegate).getDropDownView(position, convertView, parent);
- }
-
- @Override
- public int hashCode() {
- return mDelegate.hashCode();
- }
-
- @Override
- public void notifyDataSetChanged() {
- ((BaseAdapter) mDelegate).notifyDataSetChanged();
- }
-
- @Override
- public void notifyDataSetInvalidated() {
- ((BaseAdapter) mDelegate).notifyDataSetInvalidated();
- }
-
- @Override
- public String toString() {
- return mDelegate.toString();
- }
-
- @Override
- public View getHeaderView(int position, View convertView, ViewGroup parent) {
- return mDelegate.getHeaderView(position, convertView, parent);
- }
-
- @Override
- public long getHeaderId(int position) {
- return mDelegate.getHeaderId(position);
- }
-
-}
diff --git a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/ApiLevelTooLowException.java b/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/ApiLevelTooLowException.java
deleted file mode 100644
index 075dfb4..0000000
--- a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/ApiLevelTooLowException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package cx.ring.views.stickylistheaders;
-
-public class ApiLevelTooLowException extends RuntimeException {
-
- private static final long serialVersionUID = -5480068364264456757L;
-
- public ApiLevelTooLowException(int versionCode) {
- super("Requires API level " + versionCode);
- }
-
-}
diff --git a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/CheckableWrapperView.java b/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/CheckableWrapperView.java
deleted file mode 100644
index e22204f..0000000
--- a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/CheckableWrapperView.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package cx.ring.views.stickylistheaders;
-
-import android.content.Context;
-import android.widget.Checkable;
-
-/**
- * A WrapperView that implements the checkable interface
- *
- * @author Emil Sjölander
- */
-class CheckableWrapperView extends WrapperView implements Checkable {
-
- public CheckableWrapperView(final Context context) {
- super(context);
- }
-
- @Override
- public boolean isChecked() {
- return ((Checkable) mItem).isChecked();
- }
-
- @Override
- public void setChecked(final boolean checked) {
- ((Checkable) mItem).setChecked(checked);
- }
-
- @Override
- public void toggle() {
- setChecked(!isChecked());
- }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/SectionIndexerAdapterWrapper.java b/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/SectionIndexerAdapterWrapper.java
deleted file mode 100644
index dd823ff..0000000
--- a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/SectionIndexerAdapterWrapper.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package cx.ring.views.stickylistheaders;
-
-import android.content.Context;
-import android.widget.SectionIndexer;
-
-class SectionIndexerAdapterWrapper extends
- AdapterWrapper implements SectionIndexer {
-
- final SectionIndexer mSectionIndexerDelegate;
-
- SectionIndexerAdapterWrapper(Context context,
- StickyListHeadersAdapter delegate) {
- super(context, delegate);
- mSectionIndexerDelegate = (SectionIndexer) delegate;
- }
-
- @Override
- public int getPositionForSection(int section) {
- return mSectionIndexerDelegate.getPositionForSection(section);
- }
-
- @Override
- public int getSectionForPosition(int position) {
- return mSectionIndexerDelegate.getSectionForPosition(position);
- }
-
- @Override
- public Object[] getSections() {
- return mSectionIndexerDelegate.getSections();
- }
-
-}
diff --git a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/StickyListHeadersAdapter.java b/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/StickyListHeadersAdapter.java
deleted file mode 100644
index 236338d..0000000
--- a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/StickyListHeadersAdapter.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package cx.ring.views.stickylistheaders;
-
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ListAdapter;
-
-public interface StickyListHeadersAdapter extends ListAdapter {
- /**
- * Get a View that displays the header data at the specified position in the
- * set. You can either create a View manually or inflate it from an XML layout
- * file.
- *
- * @param position
- * The position of the item within the adapter's data set of the item whose
- * header view we want.
- * @param convertView
- * The old view to reuse, if possible. Note: You should check that this view is
- * non-null and of an appropriate type before using. If it is not possible to
- * convert this view to display the correct data, this method can create a new
- * view.
- * @param parent
- * The parent that this view will eventually be attached to.
- * @return
- * A View corresponding to the data at the specified position.
- */
- View getHeaderView(int position, View convertView, ViewGroup parent);
-
- /**
- * Get the header id associated with the specified position in the list.
- *
- * @param position
- * The position of the item within the adapter's data set whose header id we
- * want.
- * @return
- * The id of the header at the specified position.
- */
- long getHeaderId(int position);
-}
diff --git a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/StickyListHeadersListView.java b/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/StickyListHeadersListView.java
deleted file mode 100644
index 72780e9..0000000
--- a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/StickyListHeadersListView.java
+++ /dev/null
@@ -1,993 +0,0 @@
-package cx.ring.views.stickylistheaders;
-
-import cx.ring.R;
-import cx.ring.adapters.ContactsAdapter;
-
-import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.util.SparseBooleanArray;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.AbsListView.OnScrollListener;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemLongClickListener;
-import android.widget.FrameLayout;
-import android.widget.ListView;
-import android.widget.SectionIndexer;
-
-/**
- * Even though this is a FrameLayout subclass we it is called a ListView. This
- * is because of 2 reasons. 1. It acts like as ListView 2. It used to be a
- * ListView subclass and i did not was to change to name causing compatibility
- * errors.
- *
- * @author Emil Sjölander
- */
-public class StickyListHeadersListView extends FrameLayout {
-
- public interface OnHeaderClickListener {
- public void onHeaderClick(StickyListHeadersListView l, View header,
- int itemPosition, long headerId, boolean currentlySticky);
- }
-
- /* --- Children --- */
- private WrapperViewList mList;
- private View mHeader;
-
- /* --- Header state --- */
- private Long mHeaderId;
- // used to not have to call getHeaderId() all the time
- private Integer mHeaderPosition;
- private Integer mHeaderOffset;
-
- /* --- Delegates --- */
- private OnScrollListener mOnScrollListenerDelegate;
-
- /* --- Settings --- */
- private boolean mAreHeadersSticky = true;
- private boolean mClippingToPadding = true;
- private boolean mIsDrawingListUnderStickyHeader = true;
- private int mPaddingLeft = 0;
- private int mPaddingTop = 0;
- private int mPaddingRight = 0;
- private int mPaddingBottom = 0;
-
- /* --- Other --- */
- private AdapterWrapper mAdapter;
- private OnHeaderClickListener mOnHeaderClickListener;
- private Drawable mDivider;
- private int mDividerHeight;
- private AdapterWrapperDataSetObserver mDataSetObserver;
-
- public StickyListHeadersListView(Context context) {
- this(context, null);
- }
-
- public StickyListHeadersListView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- public StickyListHeadersListView(Context context, AttributeSet attrs,
- int defStyle) {
- super(context, attrs, defStyle);
-
- // Initialize the list
- mList = new WrapperViewList(context, attrs);
- mDivider = mList.getDivider();
- mDividerHeight = mList.getDividerHeight();
-
- // null out divider, dividers are handled by adapter so they look good
- // with headers
- mList.setDivider(null);
- mList.setDividerHeight(0);
-
- mList.setLifeCycleListener(new WrapperViewListLifeCycleListener());
- mList.setOnScrollListener(new WrapperListScrollListener());
- addView(mList);
-
- if (attrs != null) {
- TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
- R.styleable.StickyListHeadersListView, 0, 0);
-
- try {
- // Android attributes
- if (a.hasValue(R.styleable.StickyListHeadersListView_android_padding)) {
- int padding = a
- .getDimensionPixelSize(
- R.styleable.StickyListHeadersListView_android_padding,
- 0);
- mPaddingLeft = padding;
- mPaddingTop = padding;
- mPaddingRight = padding;
- mPaddingBottom = padding;
- } else {
- mPaddingLeft = a
- .getDimensionPixelSize(
- R.styleable.StickyListHeadersListView_android_paddingLeft,
- 0);
- mPaddingTop = a
- .getDimensionPixelSize(
- R.styleable.StickyListHeadersListView_android_paddingTop,
- 0);
- mPaddingRight = a
- .getDimensionPixelSize(
- R.styleable.StickyListHeadersListView_android_paddingRight,
- 0);
- mPaddingBottom = a
- .getDimensionPixelSize(
- R.styleable.StickyListHeadersListView_android_paddingBottom,
- 0);
- }
- setPadding(mPaddingLeft, mPaddingTop, mPaddingRight,
- mPaddingBottom);
-
- // Set clip to padding on the list and reset value to default on
- // wrapper
- mClippingToPadding = a
- .getBoolean(
- R.styleable.StickyListHeadersListView_android_clipToPadding,
- true);
- super.setClipToPadding(true);
- mList.setClipToPadding(mClippingToPadding);
-
- // ListView attributes
- mList.setFadingEdgeLength(a
- .getDimensionPixelSize(
- R.styleable.StickyListHeadersListView_android_fadingEdgeLength,
- mList.getVerticalFadingEdgeLength()));
- final int fadingEdge = a
- .getInt(R.styleable.StickyListHeadersListView_android_requiresFadingEdge,
- 0);
- if (fadingEdge == 0x00001000) {
- mList.setVerticalFadingEdgeEnabled(false);
- mList.setHorizontalFadingEdgeEnabled(true);
- } else if (fadingEdge == 0x00002000) {
- mList.setVerticalFadingEdgeEnabled(true);
- mList.setHorizontalFadingEdgeEnabled(false);
- } else {
- mList.setVerticalFadingEdgeEnabled(false);
- mList.setHorizontalFadingEdgeEnabled(false);
- }
- mList.setCacheColorHint(a
- .getColor(
- R.styleable.StickyListHeadersListView_android_cacheColorHint,
- mList.getCacheColorHint()));
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- mList.setChoiceMode(a
- .getInt(R.styleable.StickyListHeadersListView_android_choiceMode,
- mList.getChoiceMode()));
- }
- mList.setDrawSelectorOnTop(a
- .getBoolean(
- R.styleable.StickyListHeadersListView_android_drawSelectorOnTop,
- false));
- mList.setFastScrollEnabled(a
- .getBoolean(
- R.styleable.StickyListHeadersListView_android_fastScrollEnabled,
- mList.isFastScrollEnabled()));
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- mList.setFastScrollAlwaysVisible(a
- .getBoolean(
- R.styleable.StickyListHeadersListView_android_fastScrollAlwaysVisible,
- mList.isFastScrollAlwaysVisible()));
- }
- mList.setScrollBarStyle(a
- .getInt(R.styleable.StickyListHeadersListView_android_scrollbarStyle,
- 0));
- final Drawable selector = a
- .getDrawable(R.styleable.StickyListHeadersListView_android_listSelector);
- if (selector != null) {
- mList.setSelector(selector);
- }
- mList.setScrollingCacheEnabled(a
- .getBoolean(
- R.styleable.StickyListHeadersListView_android_scrollingCache,
- mList.isScrollingCacheEnabled()));
- final Drawable divider = a
- .getDrawable(R.styleable.StickyListHeadersListView_android_divider);
- if (divider != null) {
- mDivider = divider;
- }
- mDividerHeight = a
- .getDimensionPixelSize(
- R.styleable.StickyListHeadersListView_android_dividerHeight,
- mDividerHeight);
-
- // StickyListHeaders attributes
- mAreHeadersSticky = a.getBoolean(
- R.styleable.StickyListHeadersListView_hasStickyHeaders,
- true);
- mIsDrawingListUnderStickyHeader = a
- .getBoolean(
- R.styleable.StickyListHeadersListView_isDrawingListUnderStickyHeader,
- true);
- } finally {
- a.recycle();
- }
- }
-
- mList.setVerticalScrollBarEnabled(isVerticalScrollBarEnabled());
- mList.setHorizontalScrollBarEnabled(isHorizontalScrollBarEnabled());
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- measureHeader(mHeader);
- }
-
- private void ensureHeaderHasCorrectLayoutParams(View header) {
- ViewGroup.LayoutParams lp = header.getLayoutParams();
- if (lp == null) {
- lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
- } else if (lp.height == LayoutParams.MATCH_PARENT) {
- lp.height = LayoutParams.WRAP_CONTENT;
- }
- header.setLayoutParams(lp);
- }
-
- private void measureHeader(View header) {
- if (header != null) {
- final int width = getMeasuredWidth() - mPaddingLeft - mPaddingRight;
- final int parentWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
- width, MeasureSpec.EXACTLY);
- final int parentHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0,
- MeasureSpec.UNSPECIFIED);
- measureChild(header, parentWidthMeasureSpec,
- parentHeightMeasureSpec);
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right,
- int bottom) {
- mList.layout(0, 0, mList.getMeasuredWidth(), getHeight());
- if (mHeader != null) {
- MarginLayoutParams lp = (MarginLayoutParams) mHeader
- .getLayoutParams();
- int headerTop = lp.topMargin
- + (mClippingToPadding ? mPaddingTop : 0);
- // The left parameter must for some reason be set to 0.
- // I think it should be set to mPaddingLeft but apparently not
- mHeader.layout(mPaddingLeft, headerTop, mHeader.getMeasuredWidth()
- + mPaddingLeft, headerTop + mHeader.getMeasuredHeight());
- }
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- // Only draw the list here.
- // The header should be drawn right after the lists children are drawn.
- // This is done so that the header is above the list items
- // but below the list decorators (scroll bars etc).
- drawChild(canvas, mList, 0);
- }
-
- // Reset values tied the header. also remove header form layout
- // This is called in response to the data set or the adapter changing
- private void clearHeader() {
- if (mHeader != null) {
- removeView(mHeader);
- mHeader = null;
- mHeaderId = null;
- mHeaderPosition = null;
- mHeaderOffset = null;
-
- // reset the top clipping length
- mList.setTopClippingLength(0);
- updateHeaderVisibilities();
- }
- }
-
- private void updateOrClearHeader(int firstVisiblePosition) {
- final int adapterCount = mAdapter == null ? 0 : mAdapter.getCount();
- if (adapterCount == 0 || !mAreHeadersSticky) {
- return;
- }
-
- final int headerViewCount = mList.getHeaderViewsCount();
- final int realFirstVisibleItem = firstVisiblePosition - headerViewCount;
-
- // It is not a mistake to call getFirstVisiblePosition() here.
- // Most of the time getFixedFirstVisibleItem() should be called
- // but that does not work great together with getChildAt()
- final boolean doesListHaveChildren = mList.getChildCount() != 0;
- final boolean isFirstViewBelowTop = doesListHaveChildren && mList.getFirstVisiblePosition() == 0
- && mList.getChildAt(0).getTop() > 0;
- final boolean isFirstVisibleItemOutsideAdapterRange = realFirstVisibleItem > adapterCount - 1
- || realFirstVisibleItem < 0;
- if (!doesListHaveChildren || isFirstVisibleItemOutsideAdapterRange
- || isFirstViewBelowTop) {
- clearHeader();
- return;
- }
-
- updateHeader(realFirstVisibleItem);
- }
-
- private void updateHeader(int firstVisiblePosition) {
-
- // check if there is a new header should be sticky
- if (mHeaderPosition == null || mHeaderPosition != firstVisiblePosition) {
- mHeaderPosition = firstVisiblePosition;
- final long headerId = mAdapter.getHeaderId(firstVisiblePosition);
- if (mHeaderId == null || mHeaderId != headerId) {
- mHeaderId = headerId;
- final View header = mAdapter.getHeaderView(mHeaderPosition,
- mHeader, this);
- if (mHeader != header) {
- if (header == null) {
- throw new NullPointerException("header may not be null");
- }
- swapHeader(header);
- }
-
- ensureHeaderHasCorrectLayoutParams(mHeader);
- measureHeader(mHeader);
-
- // Reset mHeaderOffset to null ensuring
- // that it will be set on the header and
- // not skipped for performance reasons.
- mHeaderOffset = null;
- }
- }
-
- int headerOffset = 0;
-
- // Calculate new header offset
- // Skip looking at the first view. it never matters because it always
- // results in a headerOffset = 0
- int headerBottom = mHeader.getMeasuredHeight()
- + (mClippingToPadding ? mPaddingTop : 0);
- for (int i = 0; i < mList.getChildCount(); i++) {
- final View child = mList.getChildAt(i);
- final boolean doesChildHaveHeader = child instanceof WrapperView
- && ((WrapperView) child).hasHeader();
- final boolean isChildFooter = mList.containsFooterView(child);
- if (child.getTop() >= (mClippingToPadding ? mPaddingTop : 0)
- && (doesChildHaveHeader || isChildFooter)) {
- headerOffset = Math.min(child.getTop() - headerBottom, 0);
- break;
- }
- }
-
- setHeaderOffet(headerOffset);
-
- if (!mIsDrawingListUnderStickyHeader) {
- mList.setTopClippingLength(mHeader.getMeasuredHeight()
- + mHeaderOffset);
- }
-
- updateHeaderVisibilities();
- }
-
- private void swapHeader(View newHeader) {
- if (mHeader != null) {
- removeView(mHeader);
- }
- mHeader = newHeader;
- addView(mHeader);
- mHeader.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (mOnHeaderClickListener != null) {
- mOnHeaderClickListener.onHeaderClick(
- StickyListHeadersListView.this, mHeader,
- mHeaderPosition, mHeaderId, true);
- }
- }
-
- });
- }
-
- // hides the headers in the list under the sticky header.
- // Makes sure the other ones are showing
- private void updateHeaderVisibilities() {
- int top;
- if (mHeader != null) {
- top = mHeader.getMeasuredHeight()
- + (mHeaderOffset != null ? mHeaderOffset : 0);
- } else {
- top = mClippingToPadding ? mPaddingTop : 0;
- }
- int childCount = mList.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = mList.getChildAt(i);
- if (child instanceof WrapperView) {
- WrapperView wrapperViewChild = (WrapperView) child;
- if (wrapperViewChild.hasHeader()) {
- View childHeader = wrapperViewChild.mHeader;
- if (wrapperViewChild.getTop() < top) {
- if (childHeader.getVisibility() != View.INVISIBLE) {
- childHeader.setVisibility(View.INVISIBLE);
- }
- } else {
- if (childHeader.getVisibility() != View.VISIBLE) {
- childHeader.setVisibility(View.VISIBLE);
- }
- }
- }
- }
- }
- }
-
- // Wrapper around setting the header offset in different ways depending on
- // the API version
- @SuppressLint("NewApi")
- private void setHeaderOffet(int offset) {
- if (mHeaderOffset == null || mHeaderOffset != offset) {
- mHeaderOffset = offset;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- mHeader.setTranslationY(mHeaderOffset);
- } else {
- MarginLayoutParams params = (MarginLayoutParams) mHeader
- .getLayoutParams();
- params.topMargin = mHeaderOffset;
- mHeader.setLayoutParams(params);
- }
- }
- }
-
- private class AdapterWrapperDataSetObserver extends DataSetObserver {
-
- @Override
- public void onChanged() {
- clearHeader();
- }
-
- @Override
- public void onInvalidated() {
- clearHeader();
- }
-
- }
-
- private class WrapperListScrollListener implements OnScrollListener {
-
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem,
- int visibleItemCount, int totalItemCount) {
- if (mOnScrollListenerDelegate != null) {
- mOnScrollListenerDelegate.onScroll(view, firstVisibleItem,
- visibleItemCount, totalItemCount);
- }
- updateOrClearHeader(mList.getFixedFirstVisibleItem());
- }
-
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- if (mOnScrollListenerDelegate != null) {
- mOnScrollListenerDelegate.onScrollStateChanged(view,
- scrollState);
- }
- }
-
- }
-
- private class WrapperViewListLifeCycleListener implements WrapperViewList.LifeCycleListener {
-
- @Override
- public void onDispatchDrawOccurred(Canvas canvas) {
- // onScroll is not called often at all before froyo
- // therefor we need to update the header here as well.
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
- updateOrClearHeader(mList.getFixedFirstVisibleItem());
- }
- if (mHeader != null) {
- if (mClippingToPadding) {
- canvas.save();
- canvas.clipRect(0, mPaddingTop, getRight(), getBottom());
- drawChild(canvas, mHeader, 0);
- canvas.restore();
- } else {
- drawChild(canvas, mHeader, 0);
- }
- }
- }
-
- }
-
- private class AdapterWrapperHeaderClickHandler implements
- AdapterWrapper.OnHeaderClickListener {
-
- @Override
- public void onHeaderClick(View header, int itemPosition, long headerId) {
- mOnHeaderClickListener.onHeaderClick(
- StickyListHeadersListView.this, header, itemPosition,
- headerId, false);
- }
-
- }
-
- private boolean isStartOfSection(int position) {
- return position == 0
- || mAdapter.getHeaderId(position) != mAdapter
- .getHeaderId(position - 1);
- }
-
- private int getHeaderOverlap(int position) {
- boolean isStartOfSection = isStartOfSection(position);
- if (!isStartOfSection) {
- View header = mAdapter.getHeaderView(position, null, mList);
- if (header == null) {
- throw new NullPointerException("header may not be null");
- }
- ensureHeaderHasCorrectLayoutParams(header);
- measureHeader(header);
- return header.getMeasuredHeight();
- }
- return 0;
- }
-
- /* ---------- StickyListHeaders specific API ---------- */
-
- public void setAreHeadersSticky(boolean areHeadersSticky) {
- mAreHeadersSticky = areHeadersSticky;
- if (!areHeadersSticky) {
- clearHeader();
- } else {
- updateOrClearHeader(mList.getFixedFirstVisibleItem());
- }
- // invalidating the list will trigger dispatchDraw()
- mList.invalidate();
- }
-
- public boolean areHeadersSticky() {
- return mAreHeadersSticky;
- }
-
- /**
- * Use areHeadersSticky() method instead
- */
- @Deprecated
- public boolean getAreHeadersSticky() {
- return areHeadersSticky();
- }
-
- public void setDrawingListUnderStickyHeader(
- boolean drawingListUnderStickyHeader) {
- mIsDrawingListUnderStickyHeader = drawingListUnderStickyHeader;
- // reset the top clipping length
- mList.setTopClippingLength(0);
- }
-
- public boolean isDrawingListUnderStickyHeader() {
- return mIsDrawingListUnderStickyHeader;
- }
-
- public void setOnHeaderClickListener(
- OnHeaderClickListener onHeaderClickListener) {
- mOnHeaderClickListener = onHeaderClickListener;
- if (mAdapter != null) {
- if (mOnHeaderClickListener != null) {
- mAdapter.setOnHeaderClickListener(new AdapterWrapperHeaderClickHandler());
- } else {
- mAdapter.setOnHeaderClickListener(null);
- }
- }
- }
-
- public View getListChildAt(int index) {
- return mList.getChildAt(index);
- }
-
- public int getListChildCount() {
- return mList.getChildCount();
- }
-
- /**
- * Use the method with extreme caution!! Changing any values on the
- * underlying ListView might break everything.
- *
- * @return the ListView backing this view.
- */
- public ListView getWrappedList() {
- return mList;
- }
-
- /* ---------- ListView delegate methods ---------- */
-
- public void setAdapter(StickyListHeadersAdapter adapter) {
- if (adapter == null) {
- mList.setAdapter(null);
- clearHeader();
- return;
- }
- if (mAdapter != null) {
- mAdapter.unregisterDataSetObserver(mDataSetObserver);
- }
-
- if (adapter instanceof SectionIndexer) {
- mAdapter = new SectionIndexerAdapterWrapper(getContext(), adapter);
- } else {
- mAdapter = new AdapterWrapper(getContext(), adapter);
- }
- mDataSetObserver = new AdapterWrapperDataSetObserver();
- mAdapter.registerDataSetObserver(mDataSetObserver);
-
- if (mOnHeaderClickListener != null) {
- mAdapter.setOnHeaderClickListener(new AdapterWrapperHeaderClickHandler());
- } else {
- mAdapter.setOnHeaderClickListener(null);
- }
-
- mAdapter.setDivider(mDivider, mDividerHeight);
-
- mList.setAdapter(mAdapter);
- clearHeader();
- }
-
- public StickyListHeadersAdapter getAdapter() {
- return mAdapter == null ? null : mAdapter.mDelegate;
- }
-
- public void setDivider(Drawable divider) {
- mDivider = divider;
- if (mAdapter != null) {
- mAdapter.setDivider(mDivider, mDividerHeight);
- }
- }
-
- public void setDividerHeight(int dividerHeight) {
- mDividerHeight = dividerHeight;
- if (mAdapter != null) {
- mAdapter.setDivider(mDivider, mDividerHeight);
- }
- }
-
- public Drawable getDivider() {
- return mDivider;
- }
-
- public int getDividerHeight() {
- return mDividerHeight;
- }
-
- public void setOnScrollListener(OnScrollListener onScrollListener) {
- mOnScrollListenerDelegate = onScrollListener;
- }
-
- public void setOnItemClickListener(OnItemClickListener listener) {
- mList.setOnItemClickListener(listener);
- }
-
- public void setOnItemLongClickListener(OnItemLongClickListener listener) {
- mList.setOnItemLongClickListener(listener);
- }
-
- public void addHeaderView(View v, Object data, boolean isSelectable) {
- mList.addHeaderView(v, data, isSelectable);
- }
-
- public void addHeaderView(View v) {
- mList.addHeaderView(v);
- }
-
- public void removeHeaderView(View v) {
- mList.removeHeaderView(v);
- }
-
- public int getHeaderViewsCount() {
- return mList.getHeaderViewsCount();
- }
-
- public void addFooterView(View v) {
- mList.addFooterView(v);
- }
-
- public void removeFooterView(View v) {
- mList.removeFooterView(v);
- }
-
- public int getFooterViewsCount() {
- return mList.getFooterViewsCount();
- }
-
- public void setEmptyView(View v) {
- mList.setEmptyView(v);
- }
-
- public View getEmptyView() {
- return mList.getEmptyView();
- }
-
- @Override
- public void setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled) {
- mList.setVerticalScrollBarEnabled(verticalScrollBarEnabled);
- }
-
- @Override
- public void setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled) {
- mList.setHorizontalScrollBarEnabled(horizontalScrollBarEnabled);
- }
-
- @TargetApi(Build.VERSION_CODES.FROYO)
- public void smoothScrollBy(int distance, int duration) {
- requireSdkVersion(Build.VERSION_CODES.FROYO);
- mList.smoothScrollBy(distance, duration);
- }
-
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- public void smoothScrollByOffset(int offset) {
- requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
- mList.smoothScrollByOffset(offset);
- }
-
- @SuppressLint("NewApi")
- @TargetApi(Build.VERSION_CODES.FROYO)
- public void smoothScrollToPosition(int position) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
- mList.smoothScrollToPosition(position);
- } else {
- int offset = mAdapter == null ? 0 : getHeaderOverlap(position);
- offset -= mClippingToPadding ? 0 : mPaddingTop;
- mList.smoothScrollToPositionFromTop(position, offset);
- }
- }
-
- @TargetApi(Build.VERSION_CODES.FROYO)
- public void smoothScrollToPosition(int position, int boundPosition) {
- requireSdkVersion(Build.VERSION_CODES.FROYO);
- mList.smoothScrollToPosition(position, boundPosition);
- }
-
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- public void smoothScrollToPositionFromTop(int position, int offset) {
- requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
- offset += mAdapter == null ? 0 : getHeaderOverlap(position);
- offset -= mClippingToPadding ? 0 : mPaddingTop;
- mList.smoothScrollToPositionFromTop(position, offset);
- }
-
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- public void smoothScrollToPositionFromTop(int position, int offset,
- int duration) {
- requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
- offset += mAdapter == null ? 0 : getHeaderOverlap(position);
- offset -= mClippingToPadding ? 0 : mPaddingTop;
- mList.smoothScrollToPositionFromTop(position, offset, duration);
- }
-
- public void setSelection(int position) {
- setSelectionFromTop(position, 0);
- }
-
- public void setSelectionAfterHeaderView() {
- mList.setSelectionAfterHeaderView();
- }
-
- public void setSelectionFromTop(int position, int y) {
- y += mAdapter == null ? 0 : getHeaderOverlap(position);
- y -= mClippingToPadding ? 0 : mPaddingTop;
- mList.setSelectionFromTop(position, y);
- }
-
- public void setSelector(Drawable sel) {
- mList.setSelector(sel);
- }
-
- public void setSelector(int resID) {
- mList.setSelector(resID);
- }
-
- public int getFirstVisiblePosition() {
- return mList.getFirstVisiblePosition();
- }
-
- public int getLastVisiblePosition() {
- return mList.getLastVisiblePosition();
- }
-
- public void setChoiceMode(int choiceMode) {
- mList.setChoiceMode(choiceMode);
- }
-
- public void setItemChecked(int position, boolean value) {
- mList.setItemChecked(position, value);
- }
-
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- public int getCheckedItemCount() {
- requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
- return mList.getCheckedItemCount();
- }
-
- @TargetApi(Build.VERSION_CODES.FROYO)
- public long[] getCheckedItemIds() {
- requireSdkVersion(Build.VERSION_CODES.FROYO);
- return mList.getCheckedItemIds();
- }
-
- public int getCheckedItemPosition() {
- return mList.getCheckedItemPosition();
- }
-
- public SparseBooleanArray getCheckedItemPositions() {
- return mList.getCheckedItemPositions();
- }
-
- public int getCount() {
- return mList.getCount();
- }
-
- public Object getItemAtPosition(int position) {
- return mList.getItemAtPosition(position);
- }
-
- public long getItemIdAtPosition(int position) {
- return mList.getItemIdAtPosition(position);
- }
-
- public void setOnCreateContextMenuListener(OnCreateContextMenuListener l) {
- mList.setOnCreateContextMenuListener(l);
- }
-
- public boolean showContextMenu() {
- return mList.showContextMenu();
- }
-
- public void invalidateViews() {
- mList.invalidateViews();
- }
-
- @Override
- public void setClipToPadding(boolean clipToPadding) {
- if (mList != null) {
- mList.setClipToPadding(clipToPadding);
- }
- mClippingToPadding = clipToPadding;
- }
-
- @Override
- public void setPadding(int left, int top, int right, int bottom) {
- mPaddingLeft = left;
- mPaddingTop = top;
- mPaddingRight = right;
- mPaddingBottom = bottom;
-
- if (mList != null) {
- mList.setPadding(left, top, right, bottom);
- }
- super.setPadding(0, 0, 0, 0);
- requestLayout();
- }
-
- @Override
- public int getPaddingLeft() {
- return mPaddingLeft;
- }
-
- @Override
- public int getPaddingTop() {
- return mPaddingTop;
- }
-
- @Override
- public int getPaddingRight() {
- return mPaddingRight;
- }
-
- @Override
- public int getPaddingBottom() {
- return mPaddingBottom;
- }
-
- public void setFastScrollEnabled(boolean fastScrollEnabled) {
- mList.setFastScrollEnabled(fastScrollEnabled);
- }
-
- /**
- * @see android.widget.AbsListView#setFastScrollAlwaysVisible(boolean)
- * @throws ApiLevelTooLowException on pre-Honeycomb device.
- */
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- public void setFastScrollAlwaysVisible(boolean alwaysVisible) {
- requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- mList.setFastScrollAlwaysVisible(alwaysVisible);
- }
- }
-
- /**
- * @see android.widget.AbsListView#isFastScrollAlwaysVisible()
- * @return true if the fast scroller will always show. False on pre-Honeycomb devices.
- */
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- public boolean isFastScrollAlwaysVisible(){
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
- return false;
- }
- return mList.isFastScrollAlwaysVisible();
- }
-
- private void requireSdkVersion(int versionCode) {
- if (Build.VERSION.SDK_INT < versionCode) {
- throw new ApiLevelTooLowException(versionCode);
- }
- }
-
- public int getPositionForView(View view) {
- return mList.getPositionForView(view);
- }
-
-
-
-
-
- private int mTotalCount;
- private int mItemOffsetY[];
- private boolean scrollIsComputed = false;
- private int mHeight;
-
-
- public int getListHeight() {
- return mHeight;
- }
-
- public void computeScrollY() {
- mHeight = 0;
- mTotalCount = getAdapter().getCount();
-
- int sectionHeight = 0;
- int itemHeight = 0;
- int desiredWidth = MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST);
-
- if (mItemOffsetY == null) {
- mItemOffsetY = new int[mTotalCount];
- }
- for (int i = 0; i < mTotalCount; ++i) {
-
- if (i == 0) {
- View view = getAdapter().getView(i, null, this);
- view.measure(desiredWidth, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
- sectionHeight = view.getMeasuredHeight();
- mItemOffsetY[i] = mHeight;
- mHeight += sectionHeight;
- } else if (i == 1) {
- View view = getAdapter().getView(i, null, this);
- view.measure(desiredWidth, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
- itemHeight = view.getMeasuredHeight();
- mItemOffsetY[i] = mHeight;
- mHeight += itemHeight;
- } else {
- int type = getAdapter().getItemViewType(i);
- switch (type) {
- case ContactsAdapter.TYPE_CONTACT:
- mHeight += itemHeight;
- case ContactsAdapter.TYPE_HEADER:
- mHeight += sectionHeight;
- }
- mItemOffsetY[i] = mHeight;
- mHeight += sectionHeight;
- }
-
- System.out.println(mHeight);
- }
- scrollIsComputed = true;
- }
-
- public boolean scrollYIsComputed() {
- return scrollIsComputed;
- }
-
- public int getComputedScrollY() {
- int pos, nScrollY, nItemY;
- View view = null;
- pos = getFirstVisiblePosition();
- view = getChildAt(0);
- nItemY = view.getTop();
- nScrollY = mItemOffsetY[pos] - nItemY;
- return nScrollY;
- }
-
-}
diff --git a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/WrapperView.java b/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/WrapperView.java
deleted file mode 100644
index 9ee36ad..0000000
--- a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/WrapperView.java
+++ /dev/null
@@ -1,150 +0,0 @@
-package cx.ring.views.stickylistheaders;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-
-/**
- *
- * the view that wrapps a divider header and a normal list item. The listview sees this as 1 item
- *
- * @author Emil Sjölander
- */
-public class WrapperView extends ViewGroup {
-
- View mItem;
- Drawable mDivider;
- int mDividerHeight;
- View mHeader;
- int mItemTop;
-
- WrapperView(Context c) {
- super(c);
- }
-
- public boolean hasHeader() {
- return mHeader != null;
- }
-
- public View getItem() {
- return mItem;
- }
-
- public View getHeader() {
- return mHeader;
- }
-
- void update(View item, View header, Drawable divider, int dividerHeight) {
-
- //every wrapperview must have a list item
- if (item == null) {
- throw new NullPointerException("List view item must not be null.");
- }
-
- //only remove the current item if it is not the same as the new item. this can happen if wrapping a recycled view
- if (this.mItem != item) {
- removeView(this.mItem);
- this.mItem = item;
- final ViewParent parent = item.getParent();
- if(parent != null && parent != this) {
- if(parent instanceof ViewGroup) {
- ((ViewGroup) parent).removeView(item);
- }
- }
- addView(item);
- }
-
- //same logik as above but for the header
- if (this.mHeader != header) {
- if (this.mHeader != null) {
- removeView(this.mHeader);
- }
- this.mHeader = header;
- if (header != null) {
- addView(header);
- }
- }
-
- if (this.mDivider != divider) {
- this.mDivider = divider;
- this.mDividerHeight = dividerHeight;
- invalidate();
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
- int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(measuredWidth,
- MeasureSpec.EXACTLY);
- int measuredHeight = 0;
-
- //measure header or divider. when there is a header visible it acts as the divider
- if (mHeader != null) {
- ViewGroup.LayoutParams params = mHeader.getLayoutParams();
- if (params != null && params.height > 0) {
- mHeader.measure(childWidthMeasureSpec,
- MeasureSpec.makeMeasureSpec(params.height, MeasureSpec.EXACTLY));
- } else {
- mHeader.measure(childWidthMeasureSpec,
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
- }
- measuredHeight += mHeader.getMeasuredHeight();
- } else if (mDivider != null) {
- measuredHeight += mDividerHeight;
- }
-
- //measure item
- ViewGroup.LayoutParams params = mItem.getLayoutParams();
- if (params != null && params.height > 0) {
- mItem.measure(childWidthMeasureSpec,
- MeasureSpec.makeMeasureSpec(params.height, MeasureSpec.EXACTLY));
- } else {
- mItem.measure(childWidthMeasureSpec,
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
- }
- measuredHeight += mItem.getMeasuredHeight();
-
- setMeasuredDimension(measuredWidth, measuredHeight);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
-
- l = 0;
- t = 0;
- r = getWidth();
- b = getHeight();
-
- if (mHeader != null) {
- int headerHeight = mHeader.getMeasuredHeight();
- mHeader.layout(l, t, r, headerHeight);
- mItemTop = headerHeight;
- mItem.layout(l, headerHeight, r, b);
- } else if (mDivider != null) {
- mDivider.setBounds(l, t, r, mDividerHeight);
- mItemTop = mDividerHeight;
- mItem.layout(l, mDividerHeight, r, b);
- } else {
- mItemTop = t;
- mItem.layout(l, t, r, b);
- }
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
- if (mHeader == null && mDivider != null) {
- // Drawable.setBounds() does not seem to work pre-honeycomb. So have
- // to do this instead
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
- canvas.clipRect(0, 0, getWidth(), mDividerHeight);
- }
- mDivider.draw(canvas);
- }
- }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/WrapperViewList.java b/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/WrapperViewList.java
deleted file mode 100644
index c0c95d7..0000000
--- a/ring-android/app/src/main/java/cx/ring/views/stickylistheaders/WrapperViewList.java
+++ /dev/null
@@ -1,176 +0,0 @@
-package cx.ring.views.stickylistheaders;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.AbsListView;
-import android.widget.ListView;
-
-class WrapperViewList extends ListView {
-
- interface LifeCycleListener {
- void onDispatchDrawOccurred(Canvas canvas);
- }
-
- private LifeCycleListener mLifeCycleListener;
- private List<View> mFooterViews;
- private int mTopClippingLength;
- private Rect mSelectorRect = new Rect();// for if reflection fails
- private Field mSelectorPositionField;
- private boolean mClippingToPadding = true;
-
- public WrapperViewList(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- // Use reflection to be able to change the size/position of the list
- // selector so it does not come under/over the header
- try {
- Field selectorRectField = AbsListView.class.getDeclaredField("mSelectorRect");
- selectorRectField.setAccessible(true);
- mSelectorRect = (Rect) selectorRectField.get(this);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- mSelectorPositionField = AbsListView.class.getDeclaredField("mSelectorPosition");
- mSelectorPositionField.setAccessible(true);
- }
- } catch (NoSuchFieldException e) {
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
-
- @Override
- public boolean performItemClick(View view, int position, long id) {
- if (view instanceof WrapperView) {
- view = ((WrapperView) view).mItem;
- }
- return super.performItemClick(view, position, id);
- }
-
- private void positionSelectorRect() {
- if (!mSelectorRect.isEmpty()) {
- int selectorPosition = getSelectorPosition();
- if (selectorPosition >= 0) {
- int firstVisibleItem = getFixedFirstVisibleItem();
- View v = getChildAt(selectorPosition - firstVisibleItem);
- if (v instanceof WrapperView) {
- WrapperView wrapper = ((WrapperView) v);
- mSelectorRect.top = wrapper.getTop() + wrapper.mItemTop;
- }
- }
- }
- }
-
- private int getSelectorPosition() {
- if (mSelectorPositionField == null) { // not all supported andorid
- // version have this variable
- for (int i = 0; i < getChildCount(); i++) {
- if (getChildAt(i).getBottom() == mSelectorRect.bottom) {
- return i + getFixedFirstVisibleItem();
- }
- }
- } else {
- try {
- return mSelectorPositionField.getInt(this);
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
- return -1;
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- positionSelectorRect();
- if (mTopClippingLength != 0) {
- canvas.save();
- Rect clipping = canvas.getClipBounds();
- clipping.top = mTopClippingLength;
- canvas.clipRect(clipping);
- super.dispatchDraw(canvas);
- canvas.restore();
- } else {
- super.dispatchDraw(canvas);
- }
- mLifeCycleListener.onDispatchDrawOccurred(canvas);
- }
-
- void setLifeCycleListener(LifeCycleListener lifeCycleListener) {
- mLifeCycleListener = lifeCycleListener;
- }
-
- @Override
- public void addFooterView(View v) {
- super.addFooterView(v);
- if (mFooterViews == null) {
- mFooterViews = new ArrayList<View>();
- }
- mFooterViews.add(v);
- }
-
- @Override
- public boolean removeFooterView(View v) {
- if (super.removeFooterView(v)) {
- mFooterViews.remove(v);
- return true;
- }
- return false;
- }
-
- boolean containsFooterView(View v) {
- if (mFooterViews == null) {
- return false;
- }
- return mFooterViews.contains(v);
- }
-
- void setTopClippingLength(int topClipping) {
- mTopClippingLength = topClipping;
- }
-
- int getFixedFirstVisibleItem() {
- int firstVisibleItem = getFirstVisiblePosition();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- return firstVisibleItem;
- }
-
- // first getFirstVisiblePosition() reports items
- // outside the view sometimes on old versions of android
- for (int i = 0; i < getChildCount(); i++) {
- if (getChildAt(i).getBottom() >= 0) {
- firstVisibleItem += i;
- break;
- }
- }
-
- // work around to fix bug with firstVisibleItem being to high
- // because list view does not take clipToPadding=false into account
- // on old versions of android
- if (!mClippingToPadding && getPaddingTop() > 0 && firstVisibleItem > 0) {
- if (getChildAt(0).getTop() > 0) {
- firstVisibleItem -= 1;
- }
- }
-
- return firstVisibleItem;
- }
-
- @Override
- public void setClipToPadding(boolean clipToPadding) {
- mClippingToPadding = clipToPadding;
- super.setClipToPadding(clipToPadding);
- }
-
-}
diff --git a/ring-android/app/src/main/jni/Android.mk b/ring-android/app/src/main/jni/Android.mk
index 37b98fd..7f63ff5 100644
--- a/ring-android/app/src/main/jni/Android.mk
+++ b/ring-android/app/src/main/jni/Android.mk
@@ -43,7 +43,6 @@
ARCH=$(ANDROID_ABI)
CPP_STATIC= $(ANDROID_NDK)/sources/cxx-stl/gnu-libstdc++$(CXXSTL)/libs/$(ARCH)/libgnustl_static.a \
- $(RING_CONTRIB)/lib/libexpat.a \
$(RING_CONTRIB)/lib/libgnutls.a \
$(RING_CONTRIB)/lib/libnettle.a \
$(RING_CONTRIB)/lib/libhogweed.a \
@@ -82,12 +81,14 @@
LOCAL_MODULE := libringjni
+LOCAL_CFLAGS += -fpic
+
LOCAL_CPPFLAGS += -DCCPP_PREFIX \
-DPROGSHAREDIR=\"${MY_DATADIR}/ring\" \
-DHAVE_CONFIG_H \
-DHAVE_SPEEX_CODEC \
-DHAVE_GSM_CODEC \
- -w -frtti \
+ -w -frtti -fpic \
-std=c++11 -fexceptions -fpermissive \
-DAPP_NAME=\"Ring\" \
-DSWIG_JAVA_ATTACH_CURRENT_THREAD_AS_DAEMON \
@@ -127,7 +128,7 @@
-lresample-arm-unknown-linux-androideabi
endif
-LOCAL_LDLIBS += -lexpat -lhogweed \
+LOCAL_LDLIBS += -lhogweed \
-lspeexdsp -lvorbisfile -lyaml-cpp \
-lFLAC -liax -lnettle \
-logg \
diff --git a/ring-android/app/src/main/jni/Application.mk b/ring-android/app/src/main/jni/Application.mk
index 687227d..fe89ddc 100644
--- a/ring-android/app/src/main/jni/Application.mk
+++ b/ring-android/app/src/main/jni/Application.mk
@@ -30,7 +30,7 @@
-APP_PLATFORM := android-15
+APP_PLATFORM := android-16
APP_OPTIM := debug
APP_ABI := ${ANDROID_ABI}
NDK_TOOLCHAIN_VERSION := 4.9
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_action_end_call.png b/ring-android/app/src/main/res/drawable-hdpi/ic_action_end_call.png
deleted file mode 100644
index c807fe9..0000000
--- a/ring-android/app/src/main/res/drawable-hdpi/ic_action_end_call.png
+++ /dev/null
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_call_end_black_24dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_call_end_black_24dp.png
new file mode 100644
index 0000000..3f193b1
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_call_end_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_call_end_black_36dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_call_end_black_36dp.png
new file mode 100644
index 0000000..cac21f2
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_call_end_black_36dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_call_end_white_24dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_call_end_white_24dp.png
new file mode 100644
index 0000000..625b827
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_call_end_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_call_white_24dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_call_white_24dp.png
new file mode 100644
index 0000000..4dc5065
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_call_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_chat_white_24dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_chat_white_24dp.png
new file mode 100644
index 0000000..d2cfdf1
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_chat_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_lock_white_24dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_lock_white_24dp.png
new file mode 100644
index 0000000..cd4f04a
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_lock_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_send_black_24dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_send_black_24dp.png
new file mode 100644
index 0000000..7be098f
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_send_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_share_white_24dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_share_white_24dp.png
new file mode 100644
index 0000000..b09a692
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_videocam_white_24dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_videocam_white_24dp.png
new file mode 100644
index 0000000..d83e0d5
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_videocam_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-ldrtl-hdpi/ic_send_black_24dp.png b/ring-android/app/src/main/res/drawable-ldrtl-hdpi/ic_send_black_24dp.png
new file mode 100644
index 0000000..e65515d
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-ldrtl-hdpi/ic_send_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-ldrtl-mdpi/ic_send_black_24dp.png b/ring-android/app/src/main/res/drawable-ldrtl-mdpi/ic_send_black_24dp.png
new file mode 100644
index 0000000..d65d822
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-ldrtl-mdpi/ic_send_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-ldrtl-xhdpi/ic_send_black_24dp.png b/ring-android/app/src/main/res/drawable-ldrtl-xhdpi/ic_send_black_24dp.png
new file mode 100644
index 0000000..3a25fbb
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-ldrtl-xhdpi/ic_send_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-ldrtl-xxhdpi/ic_send_black_24dp.png b/ring-android/app/src/main/res/drawable-ldrtl-xxhdpi/ic_send_black_24dp.png
new file mode 100644
index 0000000..89afc01
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-ldrtl-xxhdpi/ic_send_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-ldrtl-xxxhdpi/ic_send_black_24dp.png b/ring-android/app/src/main/res/drawable-ldrtl-xxxhdpi/ic_send_black_24dp.png
new file mode 100644
index 0000000..def070f
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-ldrtl-xxxhdpi/ic_send_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_action_end_call.png b/ring-android/app/src/main/res/drawable-mdpi/ic_action_end_call.png
deleted file mode 100644
index ee026de..0000000
--- a/ring-android/app/src/main/res/drawable-mdpi/ic_action_end_call.png
+++ /dev/null
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_call_end_black_24dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_call_end_black_24dp.png
new file mode 100644
index 0000000..3253503
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_call_end_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_call_end_black_36dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_call_end_black_36dp.png
new file mode 100644
index 0000000..3f193b1
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_call_end_black_36dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_call_end_white_24dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_call_end_white_24dp.png
new file mode 100644
index 0000000..378272f
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_call_end_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_call_white_24dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_call_white_24dp.png
new file mode 100644
index 0000000..77f9de5
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_call_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_chat_white_24dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_chat_white_24dp.png
new file mode 100644
index 0000000..dac4cb9
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_chat_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_lock_white_24dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_lock_white_24dp.png
new file mode 100644
index 0000000..1127f87
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_lock_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_send_black_24dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_send_black_24dp.png
new file mode 100644
index 0000000..83156aa
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_send_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_share_white_24dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_share_white_24dp.png
new file mode 100644
index 0000000..e944fd7
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_videocam_white_24dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_videocam_white_24dp.png
new file mode 100644
index 0000000..d146209
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_videocam_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_action_end_call.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_action_end_call.png
deleted file mode 100644
index aef6bb0..0000000
--- a/ring-android/app/src/main/res/drawable-xhdpi/ic_action_end_call.png
+++ /dev/null
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_call_end_black_24dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_call_end_black_24dp.png
new file mode 100644
index 0000000..d10890e
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_call_end_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_call_end_black_36dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_call_end_black_36dp.png
new file mode 100644
index 0000000..44794a9
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_call_end_black_36dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_call_end_white_24dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_call_end_white_24dp.png
new file mode 100644
index 0000000..a4fe688
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_call_end_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_call_white_24dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_call_white_24dp.png
new file mode 100644
index 0000000..ef45e93
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_call_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_chat_white_24dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_chat_white_24dp.png
new file mode 100644
index 0000000..ace0bef
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_chat_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png
new file mode 100644
index 0000000..ad8d91a
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_send_black_24dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_send_black_24dp.png
new file mode 100644
index 0000000..fd36be8
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_send_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png
new file mode 100644
index 0000000..22a8783
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_videocam_white_24dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_videocam_white_24dp.png
new file mode 100644
index 0000000..1b2583d
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_videocam_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_action_end_call.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_action_end_call.png
deleted file mode 100644
index c34a804..0000000
--- a/ring-android/app/src/main/res/drawable-xxhdpi/ic_action_end_call.png
+++ /dev/null
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_call_end_black_24dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_call_end_black_24dp.png
new file mode 100644
index 0000000..44794a9
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_call_end_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_call_end_black_36dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_call_end_black_36dp.png
new file mode 100644
index 0000000..ab57d90
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_call_end_black_36dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_call_end_white_24dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_call_end_white_24dp.png
new file mode 100644
index 0000000..e1831d7
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_call_end_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_call_white_24dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_call_white_24dp.png
new file mode 100644
index 0000000..90ead2e
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_call_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_chat_white_24dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_chat_white_24dp.png
new file mode 100644
index 0000000..4245090
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_chat_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png
new file mode 100644
index 0000000..0e52c7c
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_send_black_24dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_send_black_24dp.png
new file mode 100644
index 0000000..40b74eb
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_send_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png
new file mode 100644
index 0000000..a35b3cd
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_videocam_white_24dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_videocam_white_24dp.png
new file mode 100644
index 0000000..44c28e2
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_videocam_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_call_end_black_24dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_call_end_black_24dp.png
new file mode 100644
index 0000000..3a0ef39
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_call_end_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_call_end_black_36dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_call_end_black_36dp.png
new file mode 100644
index 0000000..58888d8
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_call_end_black_36dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_call_end_white_24dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_call_end_white_24dp.png
new file mode 100644
index 0000000..8801d0d
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_call_end_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_call_white_24dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_call_white_24dp.png
new file mode 100644
index 0000000..b0e0205
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_call_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_chat_white_24dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_chat_white_24dp.png
new file mode 100644
index 0000000..d708e15
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_chat_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_lock_white_24dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_lock_white_24dp.png
new file mode 100644
index 0000000..a55147b
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_lock_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_send_black_24dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_send_black_24dp.png
new file mode 100644
index 0000000..761929f
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_send_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png
new file mode 100644
index 0000000..e351c7b
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_videocam_white_24dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_videocam_white_24dp.png
new file mode 100644
index 0000000..ed20c07
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_videocam_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable/call_button.xml b/ring-android/app/src/main/res/drawable/call_button.xml
index 7e07ee4..0a5c971 100644
--- a/ring-android/app/src/main/res/drawable/call_button.xml
+++ b/ring-android/app/src/main/res/drawable/call_button.xml
@@ -1,11 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="true" android:state_pressed="true"><shape android:shape="oval">
+ <item android:state_enabled="true" android:state_pressed="true">
+ <shape android:shape="oval">
<solid android:color="@color/sfl_light_blue" />
- </shape></item>
- <item><shape android:shape="oval">
- <solid android:color="@color/sfl_dark_blue" />
- </shape></item>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="@color/color_primary_dark" />
+ </shape>
+ </item>
</selector>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/drawable/drawer_disc_handle.xml b/ring-android/app/src/main/res/drawable/drawer_disc_handle.xml
index 6d00638..bf5d4b9 100644
--- a/ring-android/app/src/main/res/drawable/drawer_disc_handle.xml
+++ b/ring-android/app/src/main/res/drawable/drawer_disc_handle.xml
@@ -1,14 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="true" android:state_pressed="true"><shape android:shape="oval">
- <solid android:color="@color/sfl_dark_blue" />
- </shape></item>
- <item android:state_enabled="false"><shape android:shape="oval">
- <solid android:color="@color/sfl_dark_blue" />
- </shape></item>
- <item><shape android:shape="oval">
- <solid android:color="@color/sfl_dark_blue" />
- </shape></item>
+ <item android:state_enabled="true" android:state_pressed="true">
+ <shape android:shape="oval">
+ <solid android:color="@color/color_primary_dark" />
+ </shape>
+ </item>
+ <item android:state_enabled="false">
+ <shape android:shape="oval">
+ <solid android:color="@color/color_primary_dark" />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="@color/color_primary_dark" />
+ </shape>
+ </item>
</selector>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/drawable/item_contact_selector.xml b/ring-android/app/src/main/res/drawable/item_contact_selector.xml
index 222289e..432c3fa 100644
--- a/ring-android/app/src/main/res/drawable/item_contact_selector.xml
+++ b/ring-android/app/src/main/res/drawable/item_contact_selector.xml
@@ -1,20 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true"><shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <shape>
<solid android:color="@color/sfl_pantone631_blue" />
-
- <padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" />
- </shape></item>
- <item android:state_focused="true"><shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <padding android:bottom="@dimen/padding_large" android:left="@dimen/padding_large" android:right="@dimen/padding_large" android:top="@dimen/padding_large" />
+ </shape>
+ </item>
+ <item android:state_focused="true">
+ <shape>
<solid android:color="@color/sfl_pantone631_blue" />
-
- <padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" />
- </shape></item>
- <item><shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <padding android:bottom="@dimen/padding_large" android:left="@dimen/padding_large" android:right="@dimen/padding_large" android:top="@dimen/padding_large" />
+ </shape>
+ </item>
+ <item>
+ <shape>
<solid android:color="@color/sfl_blue_0" />
-
- <padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" />
- </shape></item>
+ <padding android:bottom="@dimen/padding_large" android:left="@dimen/padding_large" android:right="@dimen/padding_large" android:top="@dimen/padding_large" />
+ </shape>
+ </item>
</selector>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/drawable/item_generic_selector.xml b/ring-android/app/src/main/res/drawable/item_generic_selector.xml
index da0ba95..231cdf4 100644
--- a/ring-android/app/src/main/res/drawable/item_generic_selector.xml
+++ b/ring-android/app/src/main/res/drawable/item_generic_selector.xml
@@ -4,17 +4,17 @@
<item android:state_pressed="true"><shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/darker_gray" />
- <padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" />
+ <padding android:bottom="@dimen/padding_large" android:left="@dimen/padding_large" android:right="@dimen/padding_large" android:top="@dimen/padding_large" />
</shape></item>
<item android:state_focused="true"><shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/light" />
- <padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" />
+ <padding android:bottom="@dimen/padding_large" android:left="@dimen/padding_large" android:right="@dimen/padding_large" android:top="@dimen/padding_large" />
</shape></item>
<item><shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/light" />
- <padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" />
+ <padding android:bottom="@dimen/padding_large" android:left="@dimen/padding_large" android:right="@dimen/padding_large" android:top="@dimen/padding_large" />
</shape></item>
</selector>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/drawable/item_history_selector.xml b/ring-android/app/src/main/res/drawable/item_history_selector.xml
index 77f7649..ee1120e 100644
--- a/ring-android/app/src/main/res/drawable/item_history_selector.xml
+++ b/ring-android/app/src/main/res/drawable/item_history_selector.xml
@@ -1,20 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true"><shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/transparent_grey" />
-
- <padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" />
- </shape></item>
- <item android:state_focused="true"><shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <padding android:bottom="@dimen/padding_large" android:left="@dimen/padding_large" android:right="@dimen/padding_large" android:top="@dimen/padding_large" />
+ </shape>
+ </item>
+ <item android:state_focused="true">
+ <shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/transparent_grey" />
-
- <padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" />
- </shape></item>
- <item><shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@color/transparent_light" />
-
- <padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" />
- </shape></item>
+ <padding android:bottom="@dimen/padding_large" android:left="@dimen/padding_large" android:right="@dimen/padding_large" android:top="@dimen/padding_large" />
+ </shape>
+ </item>
+ <item>
+ <shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@android:color/transparent" />
+ <padding android:bottom="@dimen/padding_large" android:left="@dimen/padding_large" android:right="@dimen/padding_large" android:top="@dimen/padding_large" />
+ </shape>
+ </item>
</selector>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/drawable/toggle_speaker_selector.xml b/ring-android/app/src/main/res/drawable/toggle_speaker_selector.xml
index 7fed3f3..8160c5d 100644
--- a/ring-android/app/src/main/res/drawable/toggle_speaker_selector.xml
+++ b/ring-android/app/src/main/res/drawable/toggle_speaker_selector.xml
@@ -16,7 +16,7 @@
</layer-list></item>
<item android:state_checked="true"><layer-list>
<item><shape android:shape="rectangle">
- <solid android:color="@color/sfl_action_blue" />
+ <solid android:color="@color/color_primary_light" />
<stroke android:width="1dp" android:color="@color/sfl_blue_lines" />
diff --git a/ring-android/app/src/main/res/layout/activity_account_settings.xml b/ring-android/app/src/main/res/layout/activity_account_settings.xml
index 13d726a..f9473ad 100644
--- a/ring-android/app/src/main/res/layout/activity_account_settings.xml
+++ b/ring-android/app/src/main/res/layout/activity_account_settings.xml
@@ -1,20 +1,22 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical" >
+ android:orientation="vertical"
+ tools:context=".client.AccountEditionActivity">
<com.astuetz.PagerSlidingTabStrip
android:id="@+id/pager_sliding_strip"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:layout_alignParentTop="true"
- android:background="@color/sfl_blue_0"
android:textColor="@color/white"
- app:pstsIndicatorColor="@color/sfl_light_blue"
- app:pstsUnderlineColor="@color/sfl_light_blue"
+ android:background="@color/color_primary_light_shadow"
+ app:pstsIndicatorColor="@color/color_primary_light"
+ app:pstsShouldExpand="true"
+ app:pstsUnderlineColor="@color/color_primary_light"
/>
- <!---->
<android.support.v4.view.ViewPager
android:id="@+id/pager"
diff --git a/ring-android/app/src/main/res/layout/activity_call_layout.xml b/ring-android/app/src/main/res/layout/activity_call_layout.xml
index 8f1878a..6649ef3 100644
--- a/ring-android/app/src/main/res/layout/activity_call_layout.xml
+++ b/ring-android/app/src/main/res/layout/activity_call_layout.xml
@@ -30,11 +30,14 @@
as that of the covered work.
-->
-<cx.ring.views.CallPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/slidingpanelayout"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_height="match_parent"
+ tools:context=".client.CallActivity">
+ <!--
<FrameLayout
android:id="@+id/message_list_frame"
android:layout_width="300dp"
@@ -43,6 +46,14 @@
<FrameLayout
android:id="@+id/ongoingcall_pane"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"
+ android:layout_gravity="right|center_vertical" />
+-->
+ <fragment
+ android:id="@+id/ongoingcall_pane"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:name="cx.ring.fragments.CallFragment"
+ tools:layout="@layout/frag_call" />
-</cx.ring.views.CallPaneLayout>
\ No newline at end of file
+</FrameLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/activity_home.xml b/ring-android/app/src/main/res/layout/activity_home.xml
index 5bbadcc..8efc6e3 100644
--- a/ring-android/app/src/main/res/layout/activity_home.xml
+++ b/ring-android/app/src/main/res/layout/activity_home.xml
@@ -32,20 +32,22 @@
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="true">
-
- <cx.ring.views.SlidingUpPanelLayout
- android:id="@+id/contact_panel"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:fitsSystemWindows="true"
+ tools:context=".client.HomeActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <ViewStub
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/viewStub" />
+
<android.support.v7.widget.Toolbar
android:id="@+id/main_toolbar"
android:layout_width="match_parent"
@@ -53,18 +55,36 @@
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
+ android:background="@color/actionbar"
- android:background="@color/sfl_dark_blue"
android:elevation="4dp"
android:gravity="bottom"
android:longClickable="true"
- android:minHeight="?android:attr/actionBarSize"
+ android:minHeight="?attr/actionBarSize"
android:popupTheme="@style/Theme.AppCompat.Light.NoActionBar"
- android:theme="@style/MyActionBar"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:contentInsetStart="72dp"
app:elevation="4dp"
app:popupTheme="@style/Theme.AppCompat.Light.NoActionBar"
- app:titleMarginBottom="16dp" />
+ app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+ app:titleMarginBottom="@dimen/action_bar_title_margin_bottom" />
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/action_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/main_toolbar"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginBottom="@dimen/action_button_bpadding"
+ android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
+ android:adjustViewBounds="false"
+ android:baselineAlignBottom="false"
+ android:elevation="4dp"
+ android:visibility="gone"
+ app:elevation="4dp"
+ app:fabSize="mini" />
<FrameLayout
android:id="@+id/main_frame"
@@ -75,32 +95,8 @@
android:layout_below="@id/main_toolbar"
android:orientation="vertical" />
- <android.support.design.widget.FloatingActionButton
- android:id="@+id/action_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBottom="@id/main_toolbar"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:layout_marginBottom="-20dp"
- android:layout_marginLeft="16dp"
- android:layout_marginStart="20dp"
- android:elevation="4dp"
- android:visibility="gone"
- app:elevation="4dp"
- app:fabSize="mini" />
</RelativeLayout>
- <FrameLayout
- android:id="@+id/contacts_frame"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clickable="true"
- android:focusable="true"
- android:focusableInTouchMode="true" />
- </cx.ring.views.SlidingUpPanelLayout>
-
<android.support.design.widget.NavigationView
android:id="@+id/left_drawer"
android:layout_width="wrap_content"
diff --git a/ring-android/app/src/main/res/layout/activity_new_conversation.xml b/ring-android/app/src/main/res/layout/activity_new_conversation.xml
new file mode 100644
index 0000000..89be1ee
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/activity_new_conversation.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="cx.ring.client.NewConversationActivity">
+
+ <fragment
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:name="cx.ring.fragments.ContactListFragment"
+ android:id="@+id/fragment"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ tools:layout="@layout/frag_contact_list" />
+</RelativeLayout>
diff --git a/ring-android/app/src/main/res/layout/frag_accounts_list.xml b/ring-android/app/src/main/res/layout/frag_accounts_list.xml
index 22d71dc..ead1b96 100644
--- a/ring-android/app/src/main/res/layout/frag_accounts_list.xml
+++ b/ring-android/app/src/main/res/layout/frag_accounts_list.xml
@@ -3,9 +3,7 @@
xmlns:dslv="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="@color/white"
- >
+ android:orientation="vertical">
<TextView
android:layout_width="match_parent"
@@ -33,7 +31,7 @@
dslv:drag_scroll_start="0.33"
dslv:drag_start_mode="onDown"
dslv:float_alpha="0.6"
- dslv:float_background_color="@color/sfl_action_blue"
+ dslv:float_background_color="@color/action_blue"
dslv:remove_enabled="false"
dslv:slide_shuffle_speed="0.3" />
diff --git a/ring-android/app/src/main/res/layout/frag_audio_mgmt.xml b/ring-android/app/src/main/res/layout/frag_audio_mgmt.xml
index 672492b..b89a291 100644
--- a/ring-android/app/src/main/res/layout/frag_audio_mgmt.xml
+++ b/ring-android/app/src/main/res/layout/frag_audio_mgmt.xml
@@ -60,7 +60,7 @@
dslv:drag_scroll_start="0.33"
dslv:drag_start_mode="onDown"
dslv:float_alpha="0.6"
- dslv:float_background_color="@color/sfl_action_blue"
+ dslv:float_background_color="@color/action_blue"
dslv:remove_enabled="false"
dslv:slide_shuffle_speed="0.3" />
</LinearLayout>
diff --git a/ring-android/app/src/main/res/layout/frag_call.xml b/ring-android/app/src/main/res/layout/frag_call.xml
index ce4d2b0..fae787b 100644
--- a/ring-android/app/src/main/res/layout/frag_call.xml
+++ b/ring-android/app/src/main/res/layout/frag_call.xml
@@ -1,84 +1,195 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/bg_72"
- android:divider="@drawable/divider">
-
- <cx.ring.model.BubblesView
- android:id="@+id/main_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_above="@+id/speaker_toggle"/>
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ tools:context=".client.CallActivity">
<RelativeLayout
- android:id="@+id/call_status_bar"
- android:layout_width="match_parent"
- android:layout_height="?android:attr/actionBarSize"
- android:layout_alignParentTop="true">
+ android:id="@+id/main_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true">
- <ImageView
- android:id="@+id/image_call"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_gravity="left"
- android:layout_marginLeft="15dp"
- android:layout_marginRight="10dp"
- android:src="@drawable/ic_action_call"/>
+ <!--
+ <Button
+ android:id="@+id/call_accept_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_toLeftOf="@+id/call_refuse_btn"
+ android:layout_toStartOf="@+id/call_refuse_btn"
+ android:backgroundTint="#447542"
+ android:text="Accept" />
- <TextView
- android:id="@+id/call_status_txt"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_toRightOf="@+id/image_call"
- android:textSize="12sp"
- android:textColor="@color/white"/>
-
- <ViewSwitcher
- android:id="@+id/security_switcher"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:visibility="gone"
- android:layout_toLeftOf="@+id/dialpad_btn">
-
- <Button
- android:id="@+id/confirm_sas"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:textSize="12sp"
- android:textColor="@color/white"/>
+ <Button
+ android:id="@+id/call_refuse_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignTop="@+id/call_accept_btn"
+ android:layout_marginEnd="58dp"
+ android:layout_marginRight="58dp"
+ android:backgroundTint="#ff6e6e"
+ android:text="Refuse" />
+-->
+ <RelativeLayout
+ android:id="@+id/contact_bubble_layout"
+ android:layout_width="fill_parent"
+ android:layout_height="200dp"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:layout_marginBottom="16dp">
<ImageView
- android:id="@+id/lock_image"
- android:layout_gravity="end|center_vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:id="@+id/contact_bubble"
+ android:layout_width="200dp"
+ android:layout_height="fill_parent"
+ android:layout_above="@+id/contact_bubble_txt"
+ android:layout_centerHorizontal="true" />
+
+ <TextView
+ android:id="@+id/contact_bubble_txt"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:gravity="center_horizontal"
+ android:text="Adrien Béraud"
+ android:textAlignment="gravity"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textIsSelectable="true"
+ android:textColor="@color/text_color_primary" />
+
+ </RelativeLayout>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="false"
+ android:layout_centerHorizontal="true"
+ android:layout_below="@+id/contact_bubble_layout">
+
+ <android.support.design.widget.FloatingActionButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/call_refuse_btn"
+ android:src="@drawable/ic_call_end_white_24dp"
+ app:backgroundTint="@color/error_red"
+ app:rippleColor="@android:color/white"
+ app:elevation="6dp"
+ app:pressedTranslationZ="12dp"
+ android:layout_below="@+id/contact_bubble_layout"
+ android:layout_centerHorizontal="true"
+ android:layout_margin="8dp" />
+
+ <android.support.design.widget.FloatingActionButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/call_accept_btn"
+ android:src="@drawable/ic_call_white_24dp"
+ app:backgroundTint="#4caf50"
+ app:rippleColor="@android:color/white"
+ app:elevation="6dp"
+ app:pressedTranslationZ="12dp"
+ android:layout_below="@+id/contact_bubble_layout"
+ android:layout_toLeftOf="@+id/call_refuse_btn"
+ android:layout_toStartOf="@+id/call_refuse_btn"
+ android:layout_margin="8dp" />
+ </LinearLayout>
+
+ <android.support.design.widget.FloatingActionButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/call_hangup_btn"
+ android:src="@drawable/ic_call_end_white_24dp"
+ app:backgroundTint="@color/error_red"
+ app:rippleColor="@android:color/white"
+ app:elevation="6dp"
+ app:pressedTranslationZ="12dp"
+ android:layout_below="@+id/contact_bubble_layout"
+ android:layout_centerHorizontal="true"
+ android:layout_margin="8dp"
+ android:visibility="gone" />
+
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:id="@+id/call_status_bar"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:layout_alignParentTop="true"
+ android:visibility="visible">
+
+ <ImageView
+ android:id="@+id/image_call"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_gravity="left"
+ android:layout_marginLeft="15dp"
+ android:layout_marginRight="10dp"
+ android:src="@drawable/ic_action_call" />
+
+ <TextView
+ android:id="@+id/call_status_txt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toRightOf="@+id/image_call"
+ android:textColor="@color/text_color_primary"
+ android:textSize="12sp" />
+
+ <ViewSwitcher
+ android:id="@+id/security_switcher"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:visibility="gone">
+
+ <Button
+ android:id="@+id/confirm_sas"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:textColor="@color/white"
+ android:textSize="12sp" />
+
+ <ImageView
+ android:id="@+id/lock_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|center_vertical" />
</ViewSwitcher>
- <ImageButton
- android:id="@+id/dialpad_btn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_centerVertical="true"
- android:layout_marginRight="10dp"
- android:background="@null"
- android:src="@drawable/ic_action_dial_pad_light"/>
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/security_indicator"
+ android:src="@drawable/ic_lock_white_24dp"
+ android:tint="#4caf50"
+ android:layout_centerVertical="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:layout_margin="16dp"
+ android:visibility="gone" />
</RelativeLayout>
<ToggleButton
- android:id="@+id/speaker_toggle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textOn=""
- android:textOff=""
- android:background="@drawable/toggle_speaker_selector"
- android:layout_alignParentBottom="true"/>
+ android:id="@+id/speaker_toggle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:background="@drawable/toggle_speaker_selector"
+ android:textOff=""
+ android:textOn=""
+ android:visibility="gone" />
</RelativeLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/frag_call_list.xml b/ring-android/app/src/main/res/layout/frag_call_list.xml
index 3be66f9..91d8686 100644
--- a/ring-android/app/src/main/res/layout/frag_call_list.xml
+++ b/ring-android/app/src/main/res/layout/frag_call_list.xml
@@ -31,30 +31,34 @@
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="@dimen/padding_small"
+ android:background="@android:color/white">
- <RelativeLayout
- android:id="@+id/confs_layouts"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_margin="10dp"
- android:background="@drawable/item_generic_selector">
+ <ListView
+ android:id="@+id/confs_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:divider="@null"
+ tools:listitem="@layout/item_calllist">
+ </ListView>
- <TextView
- android:id="@+id/confs_counter"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:layout_alignParentTop="true"
- android:textSize="30sp"/>
-
- <ListView
- android:id="@+id/confs_list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@+id/confs_counter">
- </ListView>
- </RelativeLayout>
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/newconv_fab"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_gravity="bottom|end"
+ android:layout_margin="@dimen/fab_compat_margin"
+ android:src="@drawable/ic_add_white_24dp"
+ app:backgroundTint="@color/error_red"
+ app:rippleColor="@android:color/white"
+ app:elevation="6dp"
+ app:pressedTranslationZ="12dp" />
</RelativeLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/frag_contact_list.xml b/ring-android/app/src/main/res/layout/frag_contact_list.xml
index 740e72f..f056e77 100644
--- a/ring-android/app/src/main/res/layout/frag_contact_list.xml
+++ b/ring-android/app/src/main/res/layout/frag_contact_list.xml
@@ -32,65 +32,21 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:swipe="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drag_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical" >
+ android:orientation="vertical"
+ tools:context=".client.DetailHistoryActivity">
- <RelativeLayout
- android:id="@+id/slider_button"
- android:layout_width="match_parent"
- android:layout_height="68dp" >
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="@dimen/contact_drawer_handle_height"
- android:layout_alignParentBottom="true"
- android:background="@color/sfl_dark_blue" >
-
- <TextView
- android:id="@+id/handle_title"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignParentBottom="true"
- android:layout_centerInParent="true"
- android:gravity="center"
- android:text="Contacts"
- android:textColor="@color/white"
- android:textStyle="bold" />
-
- <ImageButton
- android:id="@+id/contact_search_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentRight="true"
- android:background="@color/sfl_dark_blue"
- android:gravity="center"
- android:src="@drawable/ic_btn_search" >
- </ImageButton>
- </RelativeLayout>
-
- <!-- Declared after for implicit z order -->
-
- <cx.ring.views.HalfCircleImageView
- android:id="@+id/hello"
- android:layout_width="112dp"
- android:layout_height="68dp"
- android:layout_alignParentBottom="true"
- android:layout_centerHorizontal="true"
- android:src="@drawable/ic_action_group" />
- </RelativeLayout>
-
- <cx.ring.views.stickylistheaders.StickyListHeadersListView
+ <se.emilsjolander.stickylistheaders.StickyListHeadersListView
android:id="@+id/contacts_stickylv"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/sfl_dark_blue"
android:drawSelectorOnTop="true"
android:fastScrollEnabled="true"
- android:scrollbarStyle="outsideOverlay" />
+ android:scrollbarStyle="outsideOverlay"
+ android:divider="@null" />
<TextView
android:id="@android:id/empty"
diff --git a/ring-android/app/src/main/res/layout/frag_contact_list_header.xml b/ring-android/app/src/main/res/layout/frag_contact_list_header.xml
index 873e92c..d2ee4d8 100644
--- a/ring-android/app/src/main/res/layout/frag_contact_list_header.xml
+++ b/ring-android/app/src/main/res/layout/frag_contact_list_header.xml
@@ -1,42 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/sfl_dark_blue"
- android:orientation="vertical">
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
- <SearchView
- android:id="@+id/contact_search"
- android:layout_width="match_parent"
- android:queryHint="@string/searchbar_hint"
- android:layout_height="?android:attr/actionBarSize"
- android:background="@color/lighter_gray"/>
+ <include
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ layout="@layout/item_contact"
+ android:layout_gravity="center_horizontal"
+ android:id="@+id/newcontact_element" />
<TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:drawableLeft="@drawable/ic_action_important"
- android:gravity="center_vertical"
- android:text="@string/starred_contacts_title"
- android:textColor="@color/white"
- android:textStyle="bold"/>
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:background="@android:color/white"
+ android:gravity="center_vertical"
+ android:text="@string/starred_contacts_title"
+ android:textColor="@color/text_color_secondary"
+ android:id="@+id/fav_head_label"
+ android:textStyle="bold"
+ android:textSize="16sp"
+ android:paddingLeft="16dp" />
+
+ <ImageView
+ android:layout_width="fill_parent"
+ android:layout_height="1dp"
+ android:id="@+id/imageView3"
+ android:layout_gravity="bottom"
+ android:background="#e0e0e0" />
<LinearLayout
- android:id="@+id/llMain"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/sfl_blue_0"
- android:padding="10dp">
+ android:id="@+id/llMain"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="8dp"
+ android:background="@android:color/white">
<GridView
- android:id="@+id/favorites_grid"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:columnWidth="80dp"
- android:numColumns="auto_fit"
- android:stretchMode="spacingWidth"
- android:verticalSpacing="@dimen/contact_vertical_spacing"/>
+ android:id="@+id/favorites_grid"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:columnWidth="80dp"
+ android:numColumns="auto_fit"
+ android:stretchMode="spacingWidth"
+ android:verticalSpacing="@dimen/contact_vertical_spacing"
+ tools:listitem="@layout/item_contact_starred" />
</LinearLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/frag_conversation.xml b/ring-android/app/src/main/res/layout/frag_conversation.xml
new file mode 100644
index 0000000..b2f8ee6
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/frag_conversation.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".client.ConversationActivity">
+
+ <ListView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/hist_list"
+ android:layout_weight="1"
+ android:transcriptMode="alwaysScroll"
+ android:stackFromBottom="true"
+ android:divider="@null"
+ android:background="#ebeff0"
+ android:listSelector="@android:color/transparent"
+ tools:listitem="@layout/item_textmsg" />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:background="#e3c1c1"
+ android:id="@+id/ongoingcall_pane">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="Ongoing call"
+ android:id="@+id/textView2"
+ android:layout_centerVertical="true"
+ android:layout_centerHorizontal="true"
+ android:layout_margin="10dp" />
+ </RelativeLayout>
+
+ <ImageView
+ android:layout_width="fill_parent"
+ android:layout_height="1dp"
+ android:id="@+id/divider"
+ android:layout_gravity="center_horizontal"
+ android:background="#bdbdbd" />
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/white"
+ android:elevation="6dp">
+
+ <EditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/msg_input_txt"
+ android:layout_weight="1" />
+
+ <ImageButton
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:id="@+id/msg_send"
+ android:src="@drawable/ic_send_black_24dp"
+ android:tint="@android:color/darker_gray"
+ android:background="@android:color/transparent"
+ android:padding="8dp" />
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/frag_home.xml b/ring-android/app/src/main/res/layout/frag_home.xml
index c3ddd10..fc63c43 100644
--- a/ring-android/app/src/main/res/layout/frag_home.xml
+++ b/ring-android/app/src/main/res/layout/frag_home.xml
@@ -3,23 +3,22 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
-
+<!--
<ImageView
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:layout_alignParentTop="true"
android:background="@color/sfl_dark_blue" />
-
+-->
<com.astuetz.PagerSlidingTabStrip
android:id="@+id/pts_main"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:layout_alignParentTop="true"
- android:background="@color/sfl_blue_0"
- app:pstsIndicatorColor="@color/sfl_light_blue"
+ android:background="@color/color_primary_light_shadow"
+ app:pstsIndicatorColor="@color/color_primary_light"
app:pstsShouldExpand="true"
- app:pstsUnderlineColor="@color/sfl_light_blue"
- />
+ app:pstsUnderlineColor="@color/color_primary_light" />
<android.support.v4.view.ViewPager
android:id="@+id/pager"
diff --git a/ring-android/app/src/main/res/layout/frag_imessaging.xml b/ring-android/app/src/main/res/layout/frag_imessaging.xml
index e975a75..cc569d0 100644
--- a/ring-android/app/src/main/res/layout/frag_imessaging.xml
+++ b/ring-android/app/src/main/res/layout/frag_imessaging.xml
@@ -10,7 +10,7 @@
android:layout_above="@+id/form"
android:stackFromBottom="true"
android:transcriptMode="alwaysScroll"
- android:background="@color/sfl_dark_blue"
+ android:background="@color/color_primary_dark"
android:layout_alignParentTop="true" >
</ListView>
diff --git a/ring-android/app/src/main/res/layout/frag_menu_header.xml b/ring-android/app/src/main/res/layout/frag_menu_header.xml
index 43686ad..6a6f118 100644
--- a/ring-android/app/src/main/res/layout/frag_menu_header.xml
+++ b/ring-android/app/src/main/res/layout/frag_menu_header.xml
@@ -2,7 +2,7 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@color/sfl_blue_0"
+ android:background="@color/color_primary_dark"
android:paddingBottom="8dp"
android:paddingRight="5dp"
android:paddingTop="40dp"
diff --git a/ring-android/app/src/main/res/layout/header.xml b/ring-android/app/src/main/res/layout/header.xml
index d9c5b47..36fc52c 100644
--- a/ring-android/app/src/main/res/layout/header.xml
+++ b/ring-android/app/src/main/res/layout/header.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@null"
+ android:layout_height="48dp"
+ android:background="@android:color/white"
android:clickable="false"
android:focusable="false"
android:orientation="vertical" >
@@ -13,10 +13,21 @@
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
- android:textColor="@color/white"
- android:background="@color/sfl_dark_blue"
+ android:textColor="@color/text_color_secondary"
android:paddingLeft="5dp"
- android:textSize="14sp"
- android:textStyle="bold" />
+ android:textSize="16sp"
+ android:textStyle="bold"
+ android:layout_marginLeft="16dp"
+ android:text="A"
+ android:layout_gravity="center_vertical" />
-</LinearLayout>
\ No newline at end of file
+ <ImageView
+ android:layout_width="fill_parent"
+ android:layout_height="1dp"
+ android:id="@+id/imageView2"
+ android:layout_gravity="bottom"
+ android:background="#e0e0e0"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/item_account.xml b/ring-android/app/src/main/res/layout/item_account.xml
index 9629be8..d6a9020 100644
--- a/ring-android/app/src/main/res/layout/item_account.xml
+++ b/ring-android/app/src/main/res/layout/item_account.xml
@@ -20,7 +20,11 @@
android:layout_alignParentLeft="true"
android:layout_below="@+id/account_alias"
android:textAppearance="@style/ListSecondary"
- android:text="hostname" />
+ android:text="hostnamehostnamehostnamehostnamehostnamehostnamehostnamehostname"
+ android:ellipsize="middle"
+ android:singleLine="true"
+ android:layout_toLeftOf="@+id/error_indicator"
+ android:layout_toStartOf="@+id/error_indicator" />
<ImageView
android:id="@+id/error_indicator"
@@ -31,6 +35,8 @@
android:clickable="false"
android:focusable="false"
android:src="@drawable/ic_error_white_24dp"
- android:focusableInTouchMode="false" />
+ android:focusableInTouchMode="false"
+ android:tint="@android:color/holo_red_light"
+ android:layout_marginLeft="16dp" />
</RelativeLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/item_account_pref.xml b/ring-android/app/src/main/res/layout/item_account_pref.xml
index 92f0ae1..ee774ce 100644
--- a/ring-android/app/src/main/res/layout/item_account_pref.xml
+++ b/ring-android/app/src/main/res/layout/item_account_pref.xml
@@ -27,13 +27,16 @@
<TextView
android:id="@+id/account_host"
- android:layout_width="wrap_content"
+ android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/account_alias"
android:layout_marginLeft="72dp"
android:textAppearance="@style/ListSecondary"
- android:text="hostname"
- android:layout_alignParentLeft="true" />
+ android:text="hostnamehostnamehostnamehostnamehostname"
+ android:layout_alignParentLeft="true"
+ android:ellipsize="middle"
+ android:singleLine="true"
+ android:layout_marginRight="56dp" />
<CheckBox
android:id="@+id/account_checked"
@@ -64,6 +67,7 @@
android:layout_marginRight="16dp"
android:layout_centerVertical="true"
android:layout_toStartOf="@+id/account_checked"
- android:layout_toLeftOf="@+id/loading_indicator" />
+ android:layout_toLeftOf="@+id/loading_indicator"
+ android:tint="@color/error_red" />
</RelativeLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/item_account_selected.xml b/ring-android/app/src/main/res/layout/item_account_selected.xml
index 3c2a198..6abbe19 100644
--- a/ring-android/app/src/main/res/layout/item_account_selected.xml
+++ b/ring-android/app/src/main/res/layout/item_account_selected.xml
@@ -24,8 +24,12 @@
android:layout_alignParentLeft="true"
android:layout_below="@+id/account_alias"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="@color/white"
- android:text="hostname" />
+ android:textColor="@color/secondary_text_default_material_dark"
+ android:text="hostnamehostnamehostnamehostnamehostnamehostnamehostname"
+ android:singleLine="true"
+ android:ellipsize="middle"
+ android:layout_toLeftOf="@+id/error_indicator"
+ android:layout_toStartOf="@+id/error_indicator" />
<ImageView
android:id="@+id/error_indicator"
@@ -35,6 +39,7 @@
android:layout_centerVertical="true"
android:clickable="false"
android:focusable="false"
- android:src="@drawable/ic_error_white_24dp" />
+ android:src="@drawable/ic_error_white_24dp"
+ android:layout_marginLeft="16dp" />
</RelativeLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/item_calllist.xml b/ring-android/app/src/main/res/layout/item_calllist.xml
index aa426e4..7cfe3e5 100644
--- a/ring-android/app/src/main/res/layout/item_calllist.xml
+++ b/ring-android/app/src/main/res/layout/item_calllist.xml
@@ -1,42 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+
+Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+-->
+
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/call_entry"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_centerVertical="true"
- android:background="@drawable/item_generic_selector" >
+ android:layout_width="match_parent"
+ android:layout_height="72dp"
+ android:background="@drawable/item_history_selector"
+ android:descendantFocusability="blocksDescendants">
+
+ <ImageView
+ android:id="@+id/photo"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:background="@null"
+ android:scaleType="centerCrop"
+ android:layout_centerVertical="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginRight="16dp" />
<TextView
- android:id="@+id/call_title"
+ android:id="@+id/msg_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
+ android:layout_alignParentLeft="false"
android:layout_alignParentTop="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
- android:text=""
- android:textSize="20sp" />
+ android:textSize="16sp"
+ android:layout_toRightOf="@+id/photo"
+ android:textColor="@color/text_color_primary"
+ android:layout_marginTop="2dp" />
<TextView
android:id="@+id/call_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_below="@+id/call_title"
- android:text=""
- android:textSize="12sp" />
-
- <TextView
- android:id="@+id/call_time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/call_title"
- android:layout_toRightOf="@+id/call_status"
- android:paddingLeft="10dp"
- android:text=""
- android:textSize="12sp" />
+ android:layout_alignParentLeft="false"
+ android:layout_below="@+id/msg_txt"
+ android:textSize="14sp"
+ android:layout_toRightOf="@+id/photo"
+ android:textColor="@color/text_color_secondary" />
</RelativeLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/item_codec.xml b/ring-android/app/src/main/res/layout/item_codec.xml
index a604aa1..cd0bd51 100644
--- a/ring-android/app/src/main/res/layout/item_codec.xml
+++ b/ring-android/app/src/main/res/layout/item_codec.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/codec_container"
android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight" >
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ tools:context=".client.AccountEditionActivity">
<ImageView
android:id="@+id/drag_handle"
@@ -11,7 +13,7 @@
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:layout_marginLeft="10dp"
- android:src="@drawable/handle"/>
+ android:src="@drawable/ic_reorder_black_24dp"/>
<TextView
android:id="@+id/codec_name"
diff --git a/ring-android/app/src/main/res/layout/item_contact.xml b/ring-android/app/src/main/res/layout/item_contact.xml
index 8b961b0..4d2647e 100644
--- a/ring-android/app/src/main/res/layout/item_contact.xml
+++ b/ring-android/app/src/main/res/layout/item_contact.xml
@@ -33,110 +33,63 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_height="wrap_content" >
<RelativeLayout
+ android:id="@+id/contactview"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@color/sfl_action_blue"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:background="@android:color/white"
android:descendantFocusability="blocksDescendants" >
- <LinearLayout
- android:id="@+id/contact_underview"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignParentBottom="true"
- android:layout_alignParentTop="true"
+ <ImageView
+ android:id="@+id/photo"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:contentDescription="@string/contact_picture_description"
+ android:scaleType="centerCrop" />
+
+ <TextView
+ android:id="@+id/display_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:textColor="@color/text_color_primary"
+ android:textIsSelectable="false"
android:layout_centerVertical="true"
- android:orientation="horizontal"
- android:weightSum="6" >
+ android:layout_toRightOf="@+id/photo"
+ android:layout_toEndOf="@+id/photo"
+ android:textSize="16sp"
+ android:layout_marginLeft="16dp" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:divider="@drawable/divider"
+ android:showDividers="middle"
+ android:visibility="gone">
<ImageButton
- android:id="@+id/quick_starred"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
+ android:id="@+id/quick_call"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dp"
android:background="@null"
- android:contentDescription="@string/contact_quick_starred_description"
- android:src="@drawable/ic_action_not_important" />
+ android:contentDescription="@string/contact_quick_call_description"
+ android:src="@drawable/ic_action_call" />
<ImageButton
- android:id="@+id/quick_edit"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
+ android:id="@+id/quick_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dp"
android:background="@null"
- android:contentDescription="@string/contact_quick_edit_description"
- android:src="@drawable/ic_action_edit" />
-
- <ImageButton
- android:id="@+id/quick_discard"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:background="@null"
- android:contentDescription="@string/contact_quick_discard_description"
- android:src="@drawable/ic_action_discard" />
-
- <Space
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="3" />
+ android:contentDescription="@string/contact_quick_msg_description"
+ android:src="@drawable/ic_action_chat" />
</LinearLayout>
-
- <RelativeLayout
- android:id="@+id/contactview"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/item_contact_selector" >
-
- <ImageView
- android:id="@+id/photo"
- android:layout_width="70dp"
- android:layout_height="70dp"
- android:contentDescription="@string/contact_picture_description"
- android:scaleType="centerCrop" />
-
- <TextView
- android:id="@+id/display_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBottom="@+id/photo"
- android:layout_alignTop="@+id/photo"
- android:layout_marginLeft="15dp"
- android:layout_toRightOf="@+id/photo"
- android:gravity="center_vertical"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="@color/white"
- android:textSize="14sp" />
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_centerVertical="true"
- android:divider="@drawable/divider"
- android:showDividers="middle" >
-
- <ImageButton
- android:id="@+id/quick_call"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="5dp"
- android:background="@null"
- android:contentDescription="@string/contact_quick_call_description"
- android:src="@drawable/ic_action_call" />
-
- <ImageButton
- android:id="@+id/quick_message"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="5dp"
- android:background="@null"
- android:contentDescription="@string/contact_quick_msg_description"
- android:src="@drawable/ic_action_chat" />
- </LinearLayout>
- </RelativeLayout>
</RelativeLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/item_contact_starred.xml b/ring-android/app/src/main/res/layout/item_contact_starred.xml
index d3426a7..6b9e396 100644
--- a/ring-android/app/src/main/res/layout/item_contact_starred.xml
+++ b/ring-android/app/src/main/res/layout/item_contact_starred.xml
@@ -1,32 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <RelativeLayout
- android:id="@+id/contactview"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/contactview"
+ android:orientation="vertical">
<ImageView
android:id="@+id/photo"
- android:layout_width="70dp"
- android:layout_height="70dp"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
android:layout_centerHorizontal="true"
android:contentDescription="@string/contact_picture_description"
- android:scaleType="centerCrop" />
+ android:scaleType="centerCrop"
+ android:layout_gravity="center_horizontal" />
<TextView
android:id="@+id/display_name"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_below="@+id/photo"
android:layout_centerHorizontal="true"
android:gravity="center"
android:singleLine="true"
- android:textColor="@color/white"
+ android:textColor="@color/text_color_primary"
android:textSize="12sp" />
- </RelativeLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/item_history.xml b/ring-android/app/src/main/res/layout/item_history.xml
index d56455a..1297873 100644
--- a/ring-android/app/src/main/res/layout/item_history.xml
+++ b/ring-android/app/src/main/res/layout/item_history.xml
@@ -17,25 +17,15 @@
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-Additional permission under GNU GPL version 3 section 7:
-
-If you modify this program, or any covered work, by linking or
-combining it with the OpenSSL project's OpenSSL library (or a
-modified version of that library), containing parts covered by the
-terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
-grants you additional permission to convey the resulting work.
-Corresponding Source for a non-source form of such a combination
-shall include the source code for the parts of OpenSSL used as well
-as that of the covered work.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/contactview"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="100dp"
android:background="@drawable/item_history_selector"
- android:descendantFocusability="blocksDescendants" >
+ android:descendantFocusability="blocksDescendants">
<ImageButton
android:id="@+id/photo"
@@ -54,7 +44,8 @@
android:layout_toRightOf="@+id/photo"
android:paddingLeft="@dimen/padding_small"
android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceMedium" />
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="Adrien" />
<TextView
android:id="@+id/date_start"
diff --git a/ring-android/app/src/main/res/layout/item_textmsg.xml b/ring-android/app/src/main/res/layout/item_textmsg.xml
new file mode 100644
index 0000000..ddef7b4
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/item_textmsg.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+
+Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <RelativeLayout
+ android:id="@+id/txt_entry_right"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"
+ android:padding="@dimen/padding_large"
+ android:focusable="false"
+ android:layout_gravity="right"
+ android:visibility="gone">
+
+ <TextView
+ android:id="@+id/msg_txt_right"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:color/white"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:padding="12dp"
+ android:scrollHorizontally="true"
+ android:singleLine="false"
+ android:text="Ceci est un long message sur plusieurs lignes. Il apparaitera en multilignes"
+ android:textColor="@color/text_color_primary"
+ android:textSize="16sp"
+ android:focusable="true"
+ android:textIsSelectable="true"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:layout_marginLeft="48dp" />
+
+ <TextView
+ android:id="@+id/msg_details_txt_right"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Adrien - 12 mars"
+ android:textColor="@color/text_color_secondary"
+ android:textSize="14sp"
+ android:layout_below="@+id/msg_txt_right"
+ android:layout_alignRight="@+id/msg_txt_right"
+ android:layout_alignEnd="@+id/msg_txt_right" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:id="@+id/txt_entry"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"
+ android:padding="@dimen/padding_large"
+ android:focusable="false">
+
+ <ImageView
+ android:id="@+id/photo"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginRight="16dp"
+ android:background="@null"
+ android:scaleType="centerCrop" />
+
+ <TextView
+ android:id="@+id/msg_txt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_toEndOf="@+id/photo"
+ android:layout_toRightOf="@+id/photo"
+ android:background="@android:color/white"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:padding="12dp"
+ android:scrollHorizontally="true"
+ android:singleLine="false"
+ android:text="Ceci est un long message sur plusieurs lignes. Il apparaitera en multilignes"
+ android:textColor="@color/text_color_primary"
+ android:textSize="16sp"
+ android:focusable="true"
+ android:textIsSelectable="true"
+ android:layout_marginRight="48dp" />
+
+ <TextView
+ android:id="@+id/msg_details_txt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="false"
+ android:layout_below="@+id/msg_txt"
+ android:layout_toRightOf="@+id/photo"
+ android:text="Adrien - 12 mars"
+ android:textColor="@color/text_color_secondary"
+ android:textSize="14sp" />
+
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:id="@+id/call_entry"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusable="false"
+ android:descendantFocusability="blocksDescendants"
+ android:background="#ced8da"
+ android:padding="12dp"
+ android:layout_marginBottom="16dp"
+ android:layout_gravity="right|bottom"
+ android:visibility="gone">
+
+ <TextView
+ android:id="@+id/call_hist_txt"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:scrollHorizontally="true"
+ android:singleLine="false"
+ android:text="Appel manqué"
+ android:textColor="@color/text_color_primary"
+ android:textSize="14sp"
+ />
+
+ <TextView
+ android:id="@+id/call_details_txt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="false"
+ android:text="Adrien - 12 mars"
+ android:textColor="@color/text_color_secondary"
+ android:textSize="12sp"
+ android:layout_below="@+id/call_hist_txt" />
+
+ </RelativeLayout>
+</FrameLayout>
diff --git a/ring-android/app/src/main/res/menu/ac_call.xml b/ring-android/app/src/main/res/menu/ac_call.xml
index 7e74699..eff7bf7 100644
--- a/ring-android/app/src/main/res/menu/ac_call.xml
+++ b/ring-android/app/src/main/res/menu/ac_call.xml
@@ -4,7 +4,7 @@
<item
android:id="@+id/menuitem_chat"
android:showAsAction="always"
- android:icon="@drawable/ic_action_chat"
+ android:icon="@drawable/ic_chat_white_24dp"
android:title="@string/ab_action_chat"/>
</menu>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/menu/conversation_actions.xml b/ring-android/app/src/main/res/menu/conversation_actions.xml
new file mode 100644
index 0000000..601faf4
--- /dev/null
+++ b/ring-android/app/src/main/res/menu/conversation_actions.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item
+ android:id="@+id/conv_action_videocall"
+ android:icon="@drawable/ic_videocam_white_24dp"
+ android:title="Video call"
+ android:showAsAction="always"
+ />
+ <item
+ android:id="@+id/conv_action_audiocall"
+ android:icon="@drawable/ic_call_white_24dp"
+ android:title="Audio call"
+ android:showAsAction="always"/>
+</menu>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/menu/newconv_option_menu.xml b/ring-android/app/src/main/res/menu/newconv_option_menu.xml
new file mode 100644
index 0000000..9ef8122
--- /dev/null
+++ b/ring-android/app/src/main/res/menu/newconv_option_menu.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/contact_search"
+ android:title="Nom d'un contact ou numéro"
+ android:icon="@drawable/ic_btn_search"
+ android:showAsAction="collapseActionView|ifRoom"
+ android:actionViewClass="android.widget.SearchView" />
+</menu>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/values-land/dimens.xml b/ring-android/app/src/main/res/values-land/dimens.xml
new file mode 100644
index 0000000..b2c3f52
--- /dev/null
+++ b/ring-android/app/src/main/res/values-land/dimens.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2004-2014 Savoir-Faire Linux Inc.
+
+Author: Adrien Beraud <adrien.beraud@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Additional permission under GNU GPL version 3 section 7:
+
+If you modify this program, or any covered work, by linking or
+combining it with the OpenSSL project's OpenSSL library (or a
+modified version of that library), containing parts covered by the
+terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+grants you additional permission to convey the resulting work.
+Corresponding Source for a non-source form of such a combination
+shall include the source code for the parts of OpenSSL used as well
+as that of the covered work.
+-->
+<resources>
+
+ <dimen name="action_bar_title_margin_bottom">10dp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/values-sw600dp/dimens.xml b/ring-android/app/src/main/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000..bcb3ccb
--- /dev/null
+++ b/ring-android/app/src/main/res/values-sw600dp/dimens.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2004-2014 Savoir-Faire Linux Inc.
+
+Author: Adrien Beraud <adrien.beraud@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Additional permission under GNU GPL version 3 section 7:
+
+If you modify this program, or any covered work, by linking or
+combining it with the OpenSSL project's OpenSSL library (or a
+modified version of that library), containing parts covered by the
+terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+grants you additional permission to convey the resulting work.
+Corresponding Source for a non-source form of such a combination
+shall include the source code for the parts of OpenSSL used as well
+as that of the covered work.
+-->
+<resources>
+
+ <dimen name="action_bar_title_margin_bottom">20dp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/values-v21/dimens.xml b/ring-android/app/src/main/res/values-v21/dimens.xml
new file mode 100644
index 0000000..f3c4cba
--- /dev/null
+++ b/ring-android/app/src/main/res/values-v21/dimens.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2004-2014 Savoir-Faire Linux Inc.
+
+Author: Adrien Beraud <adrien.beraud@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Additional permission under GNU GPL version 3 section 7:
+
+If you modify this program, or any covered work, by linking or
+combining it with the OpenSSL project's OpenSSL library (or a
+modified version of that library), containing parts covered by the
+terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+grants you additional permission to convey the resulting work.
+Corresponding Source for a non-source form of such a combination
+shall include the source code for the parts of OpenSSL used as well
+as that of the covered work.
+-->
+<resources>
+
+ <dimen name="action_button_bpadding">-20dp</dimen>
+ <dimen name="fab_compat_margin">16dp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/values-v21/styles.xml b/ring-android/app/src/main/res/values-v21/styles.xml
index cbf5228..89b7d60 100644
--- a/ring-android/app/src/main/res/values-v21/styles.xml
+++ b/ring-android/app/src/main/res/values-v21/styles.xml
@@ -1,22 +1,23 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="AppThemeWithOverlay" parent="AppThemeBase">
- <item name="android:actionBarStyle">@style/MyActionBar</item>
- <item name="android:windowActionBarOverlay">true</item>
- <item name="windowActionBarOverlay">true</item>
- <item name="android:windowBackground">@drawable/bg_72</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
- <item name="android:statusBarColor">@android:color/transparent</item>
+ <item name="android:statusBarColor">@color/color_primary_light</item>
<item name="android:windowTranslucentStatus">true</item>
</style>
<style name="AppThemeWithoutOverlay" parent="@android:style/Theme.Material.Light.DarkActionBar">
<item name="android:actionBarStyle">@style/NativeActionBar</item>
+ <item name="android:colorAccent">@color/color_primary_light</item>
+ <item name="android:colorPrimary">@color/color_primary_light</item>
+ <item name="android:colorPrimaryDark">@color/color_primary_dark</item>
+
</style>
<style name="NativeActionBar" parent="@android:style/Widget.ActionBar">
- <item name="android:background">@color/sfl_dark_blue</item>
+ <item name="android:background">@color/actionbar</item>
<item name="android:titleTextStyle">@style/NativeActionBar.Text</item>
+ <item name="android:elevation">4dp</item>
</style>
<style name="NativeActionBar.Text" parent="@android:style/TextAppearance.Material.Widget.ActionBar.Title">
diff --git a/ring-android/app/src/main/res/values/colors.xml b/ring-android/app/src/main/res/values/colors.xml
index b21f6b0..bb377d6 100644
--- a/ring-android/app/src/main/res/values/colors.xml
+++ b/ring-android/app/src/main/res/values/colors.xml
@@ -1,14 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
-
-
- <!-- SFL colors -->
+
+ <color name="color_primary_light">#3AC0D2</color>
+ <color name="color_primary_light_shadow">#40b5c6</color>
+
+ <color name="color_primary_dark">#004C60</color>
+
+
+ <color name="action_blue">#004C60</color>
+
+ <color name="actionbar">@color/color_primary_light</color>
+
+
+ <!-- SFL colors-->
<color name="sfl_pantone631_blue">#56B0C9</color>
- <color name="sfl_dark_blue">#09353c</color>
+ <!-- <color name="sfl_dark_blue">#09353c</color>
<color name="sfl_another_blue">#051d21</color>
<color name="sfl_action_blue">#AA2eadda</color>
-
+ -->
<color name="sfl_blue_0">#002930</color>
@@ -51,8 +61,12 @@
<color name="holo_orange_dark">#ffff8800</color>
<!-- A really bright Holo shade of blue -->
<color name="holo_blue_bright">#ff00ddff</color>
-
-
+
<color name="transparent_grey">#AACCCCCC</color>
-
+
+ <color name="text_color_primary">@color/abc_primary_text_material_light</color>
+ <color name="text_color_secondary">@color/abc_secondary_text_material_light</color>
+ <color name="text_color_primary_dark">@color/abc_primary_text_material_dark</color>
+ <color name="text_color_secondary_dark">@color/abc_secondary_text_material_dark</color>
+
</resources>
diff --git a/ring-android/app/src/main/res/values/dimens.xml b/ring-android/app/src/main/res/values/dimens.xml
index 2d3ac13..31e5c92 100644
--- a/ring-android/app/src/main/res/values/dimens.xml
+++ b/ring-android/app/src/main/res/values/dimens.xml
@@ -47,8 +47,14 @@
<dimen name="contact_drawer_handle_height">36dp</dimen>
<dimen name="contact_drawer_handle_height_with_shadow">40dp</dimen>
- <dimen name="contact_vertical_spacing">10dp</dimen>
+ <dimen name="contact_vertical_spacing">16dp</dimen>
<dimen name="header_history_detail">200dp</dimen>
+
+ <dimen name="action_button_bpadding">-35dp</dimen>
+ <dimen name="fab_compat_margin">0dp</dimen>
+
+ <dimen name="action_bar_title_margin_bottom">12dp</dimen>
+
</resources>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/values/strings.xml b/ring-android/app/src/main/res/values/strings.xml
index b2584e7..5978919 100644
--- a/ring-android/app/src/main/res/values/strings.xml
+++ b/ring-android/app/src/main/res/values/strings.xml
@@ -63,8 +63,8 @@
<!-- Dialing Fragment -->
<string name="dial_action_call">Call</string>
- <string name="dial_error_no_number_dialed">Dial a number</string>
- <string name="dial_hint">Type phone number</string>
+ <string name="dial_error_no_number_dialed">Dial a mNumber</string>
+ <string name="dial_hint">Type phone mNumber</string>
<!-- History Fragment -->
<string name="hist_replay_button">Replay</string>
@@ -89,7 +89,7 @@
<!-- ContactList Fragment -->
<string name="no_contact_found">No contact found</string>
<string name="starred_contacts_title">Favorites</string>
- <string name="searchbar_hint">Enter name or phone number…</string>
+ <string name="searchbar_hint">Enter name or phone mNumber…</string>
<!-- FileExplorerDFragement -->
<string name="file_explorer_title">Select a file</string>
@@ -97,7 +97,7 @@
<!-- TransferDFragment -->
<string name="transfer_to_another_call">Transfer to another current call:</string>
<string name="transfer_no_other_call">No other calls pending</string>
- <string name="transfer_type_number">Type number to transfer to:</string>
+ <string name="transfer_type_number">Type mNumber to transfer to:</string>
<!-- Notifications -->
<string name="notif_missed_call_title">Missed call</string>
diff --git a/ring-android/app/src/main/res/values/styles.xml b/ring-android/app/src/main/res/values/styles.xml
index 89379e3..0e1b43b 100644
--- a/ring-android/app/src/main/res/values/styles.xml
+++ b/ring-android/app/src/main/res/values/styles.xml
@@ -1,16 +1,15 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="AppThemeBase" parent="Theme.AppCompat.Light.NoActionBar">
-
+ <style name="AppThemeBase" parent="@style/Theme.AppCompat.Light.NoActionBar">
+ <item name="actionBarStyle">@style/MyActionBar</item>
+ <item name="colorAccent">@color/color_primary_dark</item>
+ <item name="colorPrimary">@color/color_primary_light</item>
+ <item name="colorPrimaryDark">@color/color_primary_dark</item>
+ <item name="android:windowActionBarOverlay">true</item>
+ <item name="windowActionBarOverlay">true</item>
</style>
<style name="AppThemeWithOverlay" parent="AppThemeBase">
-
- <item name="android:actionBarStyle">@style/MyActionBar</item>
-
- <item name="android:windowActionBarOverlay">true</item>
- <item name="windowActionBarOverlay">true</item>
- <item name="android:windowBackground">@drawable/bg_72</item>
<item name="android:activatedBackgroundIndicator">@drawable/navigation_selector</item>
</style>
@@ -24,22 +23,26 @@
<item name="android:actionBarStyle">@style/NativeActionBar</item>
</style>
- <style name="NativeActionBar" parent="@android:style/Widget.ActionBar">
- <item name="android:background">@color/sfl_dark_blue</item>
- <item name="titleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse</item>
-
+ <style name="NativeActionBar" parent="@android:style/Widget.DeviceDefault.Light.ActionBar.Solid.Inverse">
+ <item name="android:background">@color/color_primary_light</item>
+ <item name="elevation">4dp</item>
</style>
- <style name="NativeActionBar.Text" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title">
- </style>
-
- <style name="MyActionBar" parent="@style/Widget.AppCompat.Light.ActionBar.Solid">
+ <style name="MyActionBar" parent="@style/Widget.AppCompat.ActionBar.Solid">
<item name="android:textColorPrimary">@android:color/primary_text_dark</item>
+ <item name="android:textColorHighlight">@android:color/primary_text_dark</item>
<item name="android:textColorSecondary">@android:color/secondary_text_dark</item>
+ <item name="colorControlNormal">@color/white</item>
<item name="selectableItemBackground">?android:selectableItemBackground</item>
<item name="selectableItemBackgroundBorderless">?android:selectableItemBackground</item>
- <item name="actionMenuTextColor">@color/abc_primary_text_material_light</item>
+ <item name="titleTextStyle">@style/MyTitleTextStyle</item>
+ <item name="elevation">4dp</item>
</style>
+
+ <style name="MyTitleTextStyle" parent="@style/TextAppearance.AppCompat.Widget.ActionBar.Title">
+ <item name="android:textColor">@color/error_red</item>
+ </style>
+
<style name="MenuHeader" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:textColorPrimary">@android:color/primary_text_dark</item>
<item name="android:textColorSecondary">@android:color/secondary_text_dark</item>