Merge branch 'master' into dev_phonenumber

Conflicts:
	src/org/linphone/LinphoneActivity.java
This commit is contained in:
Margaux Clerc 2016-08-09 13:25:16 +02:00
commit 995b1cd323
50 changed files with 1210 additions and 2399 deletions

15
README
View file

@ -7,8 +7,8 @@ COMPILATION INSTRUCTIONS
To build liblinphone for Android, you must:
-------------------------------------------
0) download the Android sdk with platform-tools and tools updated to latest revision (at least API 16 is needed), then add both 'tools' and 'platform-tools' folders in your path.
1) download the Android ndk (version r11) from google and add it to your path (no symlink !!!).
0) download the Android sdk (API 23 at least) with platform-tools and tools updated to latest revision, then add both 'tools' and 'platform-tools' folders in your path.
1) download the Android ndk (version r11c or 12b) from google and add it to your path (no symlink !!!).
2) install yasm, nasm, ant, python, cmake and vim-common
On 64 bits linux systems you'll need the ia32-libs package
With the latest Debian (multiarch), you need this:
@ -27,11 +27,12 @@ To build liblinphone for Android, you must:
$ make mediastreamer2-sdk
8) (Optional) To generate a signed apk to publish on the Google Play, run
$ make release
Make sure you filled the ant.properties values for version.name, key.store and key.alias in order to correctly sign the generated apk.
You also may want to create a file name ant_password.properties with the following:
key.store.password=[your_password]
key.alias.password=[your_password]
If you don't, the passwords will be asked at the signing phase.
Make sure you filled the ant.properties values for version.name, key.store and key.alias in order to correctly sign the generated apk.
You also may want to create a file name ant_password.properties with the following:
key.store.password=[your_password]
key.alias.password=[your_password]
If you don't, the passwords will be asked at the signing phase.
9) (Optional) Once you compiled the libraries succesfully with 'make', you can reduce the compilation time using 'make quick': it will only generate a new APK from java files.
To run the tutorials:
--------------------

View file

@ -48,7 +48,7 @@
</condition>
<exec executable="bash" unless:set="has.crashed">
<arg value="-c"/>
<arg value="adb shell run-as org.linphone.tester cat /data/data/org.linphone.tester/files/junit-report.xml > liblinphone-junit-report.xml"/>
<arg value="adb shell cat /data/data/org.linphone.tester/files/junit-report.xml > liblinphone-junit-report.xml"/>
</exec>
<zip destfile="${archive.name}.zip">

View file

@ -128,8 +128,9 @@
<ListView
android:id="@+id/chat_message_list"
android:divider="@android:color/transparent"
android:choiceMode="multipleChoice"
android:stackFromBottom="true"
android:transcriptMode="alwaysScroll"
android:transcriptMode="normal"
android:dividerHeight="10dp"
android:cacheColorHint="@color/transparent"
android:layout_above="@id/remote_composing"

114
res/layout/chat_bubble.xml Normal file
View file

@ -0,0 +1,114 @@
<?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="wrap_content">
<RelativeLayout
android:id="@+id/bubble"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<CheckBox
android:id="@+id/delete_message"
android:button="@drawable/checkbox"
android:contentDescription="@string/content_description_delete"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_width="30dp"
android:layout_height="30dp"
android:adjustViewBounds="true"
android:layout_alignParentRight="true"
android:visibility="gone"/>
<LinearLayout
android:id="@+id/background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/delete_message"
android:orientation="horizontal">
<ImageView
android:id="@+id/contact_picture"
android:src="@drawable/avatar"
android:paddingLeft="10dp"
android:paddingTop="10dp"
android:layout_width="40dp"
android:layout_height="40dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/contact_header"
android:singleLine="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/message"
style="@style/font11"
android:autoLink="web"
android:linksClickable="true"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/image"
android:visibility="gone"
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="centerInside"
android:layout_centerInParent="true" />
<RelativeLayout
android:id="@+id/file_transfer_layout"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/progress_bar"
style="@android:style/Widget.ProgressBar.Horizontal"
android:paddingRight="5dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_width="150dp"
android:layout_height="5dp"/>
<Button
android:id="@+id/file_transfer_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/progress_bar"/>
</RelativeLayout>
</LinearLayout>
<ImageView
android:id="@+id/status"
android:contentDescription="@string/content_description_message_status"
android:visibility="invisible"
android:padding="5dp"
android:layout_gravity="top|right"
android:layout_width="20dp"
android:layout_height="20dp"
android:adjustViewBounds="true" />
<ProgressBar
android:id="@+id/inprogress"
android:visibility="gone"
android:paddingRight="5dp"
android:layout_gravity="top|right"
android:layout_width="20dp"
android:layout_height="20dp"/>
</LinearLayout>
</RelativeLayout>
</RelativeLayout>

View file

@ -1,114 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bubble"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="left"
android:orientation="horizontal">
<CheckBox
android:id="@+id/delete_message"
android:button="@drawable/checkbox"
android:contentDescription="@string/content_description_delete"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_width="30dp"
android:layout_height="30dp"
android:adjustViewBounds="true"
android:layout_alignParentRight="true"
android:visibility="gone" />
<LinearLayout
android:id="@+id/message_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/delete_message">
<LinearLayout
android:background="@drawable/resizable_chat_bubble_incoming"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:orientation="horizontal">
<RelativeLayout
android:id="@+id/avatar_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/contact_picture"
android:src="@drawable/avatar"
android:contentDescription="@string/content_description_contact_picture"
android:layout_width="40dp"
android:layout_height="40dp"/>
<ImageView
android:id="@+id/mask"
android:src="@drawable/avatar_chat_mask"
android:layout_width="40dp"
android:layout_height="40dp"/>
</RelativeLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="10dp">
<TextView
android:id="@+id/contact_header"
style="@style/font9"
android:singleLine="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/message"
style="@style/font11"
android:linksClickable="true"
android:autoLink="web"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:id="@+id/image"
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="centerInside"
android:visibility="gone"
android:maxWidth="250dp"
android:maxHeight="250dp"/>
<RelativeLayout
android:id="@+id/file_transfer_layout"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/progress_bar"
style="@android:style/Widget.ProgressBar.Horizontal"
android:paddingRight="5dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_width="150dp"
android:layout_height="5dp"/>
<Button
android:id="@+id/accept_download"
android:text="@string/accept"
android:background="@drawable/resizable_assistant_button"
style="@style/font8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/progress_bar"/>
</RelativeLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>

View file

@ -1,112 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bubble"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:id="@+id/delete_message"
android:button="@drawable/checkbox"
android:contentDescription="@string/content_description_delete"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_width="30dp"
android:layout_height="30dp"
android:adjustViewBounds="true"
android:layout_alignParentRight="true"
android:visibility="gone"/>
<LinearLayout
android:background="@drawable/resizable_chat_bubble_outgoing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/delete_message"
android:orientation="horizontal">
<ImageView
android:id="@+id/contact_picture"
android:src="@drawable/avatar"
android:paddingLeft="10dp"
android:paddingTop="10dp"
android:layout_width="40dp"
android:layout_height="40dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/contact_header"
style="@style/font3"
android:singleLine="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/message"
style="@style/font11"
android:autoLink="web"
android:linksClickable="true"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/image"
android:visibility="gone"
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="centerInside"
android:layout_centerInParent="true" />
<RelativeLayout
android:id="@+id/file_transfer_layout"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/progress_bar"
style="@android:style/Widget.ProgressBar.Horizontal"
android:paddingRight="5dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_width="150dp"
android:layout_height="5dp"/>
<Button
android:id="@+id/cancel_upload"
android:text="@string/cancel"
android:background="@drawable/resizable_confirm_delete_button"
style="@style/font15"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/progress_bar"/>
</RelativeLayout>
</LinearLayout>
<ImageView
android:id="@+id/status"
android:contentDescription="@string/content_description_message_status"
android:visibility="invisible"
android:padding="5dp"
android:layout_gravity="top|right"
android:layout_width="20dp"
android:layout_height="20dp"
android:adjustViewBounds="true" />
<ProgressBar
android:id="@+id/inprogress"
android:visibility="gone"
android:paddingRight="5dp"
android:layout_gravity="top|right"
android:layout_width="20dp"
android:layout_height="20dp"/>
</LinearLayout>
</RelativeLayout>

View file

@ -136,6 +136,7 @@
android:inputType="textPersonName|textCapWords"/>
<TextView
android:id="@+id/contactOrganizationTitle"
android:text="@string/contact_organization"
style="@style/font13"
android:textAllCaps="true"

View file

@ -71,6 +71,8 @@
<bool name="allow_only_one_phone_number">false</bool>
<bool name="allow_only_one_sip_address">false</bool>
<bool name="display_contact_organization">true</bool>
<bool name="setup_cancel_move_to_back">false</bool>
<bool name="enable_call_notification">true</bool>

View file

@ -107,6 +107,7 @@
<string name="pref_background_mode_key">pref_background_mode_key</string>
<string name="pref_codec_bitrate_limit_key">pref_codec_bitrate_limit_key</string>
<string name="pref_adaptive_rate_control_key">pref_adaptive_rate_control_key</string>
<string name="pref_echo_tester_key">pref_echo_tester_key</string>
<string name="push_reg_id_key">push_reg_id_key</string>
<string name="push_sender_id_key">push_sender_id_key</string>
@ -204,4 +205,5 @@
<string name="pref_use_lime_encryption_key">pref_use_lime_encryption_key</string>
<string name="pref_device_ringtone_key">pref_device_ringtone_key</string>
<string name="pref_auto_answer_key">pref_auto_answer_key</string>
<string name="pref_android_app_settings_key">pref_android_app_settings_key</string>
</resources>

View file

@ -273,6 +273,7 @@
<string name="pref_echo_cancellation">Echo cancellation</string>
<string name="pref_echo_cancellation_summary">Removes the echo heard by other end</string>
<string name="pref_echo_canceller_calibration">Echo canceler calibration</string>
<string name="pref_echo_tester">Test echo</string>
<string name="ec_calibrating">Calibrating…</string>
<string name="ec_calibrated">Calibrated in %s ms</string>
<string name="no_echo">No echo</string>
@ -340,6 +341,7 @@
<string name="pref_autostart">Start at boot time</string>
<string name="pref_incoming_call_timeout_title">Incoming call hangup (in seconds)</string>
<string name="pref_remote_provisioning_title">Remote provisioning</string>
<string name="pref_android_app_settings_title">Android app settings</string>
<string name="pref_primary_account_title">Primary account</string>
<string name="pref_display_name_title">Display name</string>
<string name="pref_user_name_title">Username</string>

View file

@ -3,7 +3,7 @@
<ContactsSource
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Change package ! -->
<!-- Change package, leave vnd.android.cursor.item/ before and .profile at the end. -->
<ContactsDataKind
android:mimeType="vnd.android.cursor.item/org.linphone.profile"
android:icon="@drawable/linphone_logo"

View file

@ -69,6 +69,11 @@
android:title="@string/pref_echo_canceller_calibration"
android:key="@string/pref_echo_canceller_calibration_key"
android:persistent="false"/>
<Preference
android:title="@string/pref_echo_tester"
android:key="@string/pref_echo_tester_key"
android:persistent="false"/>
<CheckBoxPreference
android:title="@string/pref_adaptive_rate_control"
@ -345,6 +350,11 @@
android:key="@string/pref_remote_provisioning_key"
android:inputType="textUri"
android:persistent="false"/>
<Preference
android:title="@string/pref_android_app_settings_title"
android:key="@string/pref_android_app_settings_key"
android:persistent="false"/>
</PreferenceCategory>

View file

@ -69,11 +69,11 @@ public class AboutFragment extends Fragment implements OnClickListener {
sendLogButton = view.findViewById(R.id.send_log);
sendLogButton.setOnClickListener(this);
sendLogButton.setVisibility(org.linphone.LinphonePreferences.instance().isDebugEnabled() ? View.VISIBLE : View.GONE);
sendLogButton.setVisibility(LinphonePreferences.instance().isDebugEnabled() ? View.VISIBLE : View.GONE);
resetLogButton = view.findViewById(R.id.reset_log);
resetLogButton.setOnClickListener(this);
resetLogButton.setVisibility(org.linphone.LinphonePreferences.instance().isDebugEnabled() ? View.VISIBLE : View.GONE);
resetLogButton.setVisibility(LinphonePreferences.instance().isDebugEnabled() ? View.VISIBLE : View.GONE);
mListener = new LinphoneCoreListenerBase() {
@Override
@ -145,7 +145,7 @@ public class AboutFragment extends Fragment implements OnClickListener {
lc.addListener(mListener);
}
if (org.linphone.LinphoneActivity.isInstanciated()) {
if (LinphoneActivity.isInstanciated()) {
LinphoneActivity.instance().selectMenu(FragmentsAvailable.ABOUT);
}
@ -154,8 +154,8 @@ public class AboutFragment extends Fragment implements OnClickListener {
@Override
public void onClick(View v) {
if (org.linphone.LinphoneActivity.isInstanciated()) {
LinphoneCore lc = org.linphone.LinphoneManager.getLcIfManagerNotDestroyedOrNull();
if (LinphoneActivity.isInstanciated()) {
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
if (v == sendLogButton) {
if (lc != null) {
lc.uploadLogCollection();
@ -174,6 +174,4 @@ public class AboutFragment extends Fragment implements OnClickListener {
public void onDestroy() {
super.onDestroy();
}
}

View file

@ -53,8 +53,7 @@ public class AccountPreferencesFragment extends PreferencesListFragment {
mPrefs = LinphonePreferences.instance();
}
public void onCreate(Bundle savedInstanceState)
{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
PreferenceScreen screen = getPreferenceScreen();
@ -69,7 +68,7 @@ public class AccountPreferencesFragment extends PreferencesListFragment {
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
}
public static boolean isEditTextEmpty(String s){
public static boolean isEditTextEmpty(String s) {
return s.equals(""); // really empty.
}

View file

@ -19,7 +19,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import java.util.List;
import org.linphone.compatibility.Compatibility;
import org.linphone.mediastream.Log;
import android.annotation.TargetApi;
@ -68,31 +67,31 @@ public class BluetoothManager extends BroadcastReceiver {
public BluetoothManager() {
isBluetoothConnected = false;
if (!ensureInit()) {
Log.w("BluetoothManager tried to init but LinphoneService not ready yet...");
Log.w("[Bluetooth] Manager tried to init but LinphoneService not ready yet...");
}
instance = this;
}
public void initBluetooth() {
if (!ensureInit()) {
Log.w("BluetoothManager tried to init bluetooth but LinphoneService not ready yet...");
Log.w("[Bluetooth] Manager tried to init bluetooth but LinphoneService not ready yet...");
return;
}
IntentFilter filter = new IntentFilter();
filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." + BluetoothAssignedNumbers.PLANTRONICS);
filter.addAction(Compatibility.getAudioManagerEventForBluetoothConnectionStateChangedEvent());
filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
mContext.registerReceiver(this, filter);
Log.d("Bluetooth receiver started");
Log.d("[Bluetooth] Receiver started");
startBluetooth();
}
private void startBluetooth() {
if (isBluetoothConnected) {
Log.e("Bluetooth already started");
Log.e("[Bluetooth] Already started, skipping...");
return;
}
@ -100,14 +99,14 @@ public class BluetoothManager extends BroadcastReceiver {
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
if (mProfileListener != null) {
Log.w("Bluetooth headset profile was already opened, let's close it");
Log.w("[Bluetooth] Headset profile was already opened, let's close it");
mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
}
mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
Log.d("Bluetooth headset connected");
Log.d("[Bluetooth] Headset connected");
mBluetoothHeadset = (BluetoothHeadset) proxy;
isBluetoothConnected = true;
}
@ -116,17 +115,17 @@ public class BluetoothManager extends BroadcastReceiver {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = null;
isBluetoothConnected = false;
Log.d("Bluetooth headset disconnected");
Log.d("[Bluetooth] Headset disconnected");
LinphoneManager.getInstance().routeAudioToReceiver();
}
}
};
boolean success = mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEADSET);
if (!success) {
Log.e("Bluetooth getProfileProxy failed !");
Log.e("[Bluetooth] getProfileProxy failed !");
}
} else {
Log.w("Bluetooth interface disabled on device");
Log.w("[Bluetooth] Interface disabled on device");
}
}
@ -153,7 +152,7 @@ public class BluetoothManager extends BroadcastReceiver {
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled() && mAudioManager != null && mAudioManager.isBluetoothScoAvailableOffCall()) {
if (isBluetoothHeadsetAvailable()) {
if (mAudioManager != null && !mAudioManager.isBluetoothScoOn()) {
Log.d("Bluetooth sco off, let's start it");
Log.d("[Bluetooth] SCO off, let's start it");
mAudioManager.setBluetoothScoOn(true);
mAudioManager.startBluetoothSco();
}
@ -180,12 +179,12 @@ public class BluetoothManager extends BroadcastReceiver {
}
if (ok) {
if (retries > 0) {
Log.d("Bluetooth route ok after " + retries + " retries");
Log.d("[Bluetooth] Audio route ok after " + retries + " retries");
} else {
Log.d("Bluetooth route ok");
Log.d("[Bluetooth] Audio route ok");
}
} else {
Log.d("Bluetooth still not ok...");
Log.d("[Bluetooth] Audio route still not ok...");
}
return ok;
@ -212,7 +211,7 @@ public class BluetoothManager extends BroadcastReceiver {
break;
}
}
Log.d(isHeadsetConnected ? "Headset found, bluetooth audio route available" : "No headset found, bluetooth audio route unavailable");
Log.d(isHeadsetConnected ? "[Bluetooth] Headset found, bluetooth audio route available" : "[Bluetooth] No headset found, bluetooth audio route unavailable");
}
return isHeadsetConnected;
}
@ -237,12 +236,12 @@ public class BluetoothManager extends BroadcastReceiver {
mAudioManager.stopBluetoothSco();
mAudioManager.setBluetoothScoOn(false);
}
Log.w("Bluetooth sco disconnected!");
Log.w("[Bluetooth] SCO disconnected!");
}
}
public void stopBluetooth() {
Log.w("Stopping bluetooth...");
Log.w("[Bluetooth] Stopping...");
isBluetoothConnected = false;
disableBluetoothSCO();
@ -253,7 +252,7 @@ public class BluetoothManager extends BroadcastReceiver {
}
mBluetoothDevice = null;
Log.w("Bluetooth stopped!");
Log.w("[Bluetooth] Stopped!");
if (LinphoneManager.isInstanciated()) {
LinphoneManager.getInstance().routeAudioToReceiver();
@ -266,7 +265,7 @@ public class BluetoothManager extends BroadcastReceiver {
try {
mContext.unregisterReceiver(this);
Log.d("Bluetooth receiver stopped");
Log.d("[Bluetooth] Receiver stopped");
} catch (Exception e) {}
} catch (Exception e) {
Log.e(e);
@ -278,30 +277,30 @@ public class BluetoothManager extends BroadcastReceiver {
return;
String action = intent.getAction();
if (Compatibility.getAudioManagerEventForBluetoothConnectionStateChangedEvent().equals(action)) {
if (AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED.equals(action)) {
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, 0);
if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
Log.d("Bluetooth sco state => connected");
Log.d("[Bluetooth] SCO state: connected");
// LinphoneManager.getInstance().audioStateChanged(AudioState.BLUETOOTH);
isScoConnected = true;
} else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {
Log.d("Bluetooth sco state => disconnected");
Log.d("[Bluetooth] SCO state: disconnected");
// LinphoneManager.getInstance().audioStateChanged(AudioState.SPEAKER);
isScoConnected = false;
} else {
Log.d("Bluetooth sco state => " + state);
Log.d("[Bluetooth] SCO state: " + state);
}
}
else if (BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, BluetoothAdapter.STATE_DISCONNECTED);
if (state == 0) {
Log.d("Bluetooth state => disconnected");
Log.d("[Bluetooth] State: disconnected");
stopBluetooth();
} else if (state == 2) {
Log.d("Bluetooth state => connected");
Log.d("[Bluetooth] State: connected");
startBluetooth();
} else {
Log.d("Bluetooth state => " + state);
Log.d("[Bluetooth] State: " + state);
}
}
else if (intent.getAction().equals(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT)) {
@ -314,7 +313,7 @@ public class BluetoothManager extends BroadcastReceiver {
if (eventName.equals("BUTTON") && args.length >= 3) {
Integer buttonID = (Integer) args[1];
Integer mode = (Integer) args[2];
Log.d("Bluetooth event: " + command + " : " + eventName + ", id = " + buttonID + " (" + mode + ")");
Log.d("[Bluetooth] Event: " + command + " : " + eventName + ", id = " + buttonID + " (" + mode + ")");
}
}
}

View file

@ -119,6 +119,7 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve
private boolean isConferenceRunning = false;
private LinphoneCoreListenerBase mListener;
private DrawerLayout sideMenu;
private boolean mProximitySensingEnabled;
public static CallActivity instance() {
return instance;
@ -800,8 +801,22 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve
}
}
private void enableProximitySensing(boolean enable){
if (enable){
if (!mProximitySensingEnabled){
mSensorManager.registerListener(this, mProximity, SensorManager.SENSOR_DELAY_NORMAL);
mProximitySensingEnabled = true;
}
}else{
if (mProximitySensingEnabled){
mSensorManager.unregisterListener(this);
mProximitySensingEnabled = false;
}
}
}
private void showAudioView() {
mSensorManager.registerListener(this, mProximity, SensorManager.SENSOR_DELAY_NORMAL);
enableProximitySensing(true);
replaceFragmentVideoByAudio();
displayAudioCall();
showStatusBar();
@ -816,7 +831,7 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve
}
refreshInCallActions();
mSensorManager.unregisterListener(this);
enableProximitySensing(false);
replaceFragmentAudioByVideo();
hideStatusBar();
}
@ -1173,7 +1188,7 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve
handleViewIntent();
if (!isVideoEnabled(LinphoneManager.getLc().getCurrentCall())) {
mSensorManager.registerListener(this, mProximity, SensorManager.SENSOR_DELAY_NORMAL);
enableProximitySensing(true);
removeCallbacks();
}
}
@ -1223,10 +1238,7 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve
mControlsHandler.removeCallbacks(mControls);
}
mControls = null;
if (!isVideoEnabled(LinphoneManager.getLc().getCurrentCall())) {
mSensorManager.unregisterListener(this);
}
enableProximitySensing(false);
}
@Override
@ -1239,7 +1251,7 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve
mControls = null;
mControlsHandler = null;
mSensorManager.unregisterListener(this);
enableProximitySensing(false);
unbindDrawables(findViewById(R.id.topLayout));
instance = null;
@ -1510,7 +1522,7 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve
private void displayConference(boolean isInConf){
if(isInConf) {
mControlsLayout.setVisibility(View.VISIBLE);
mSensorManager.registerListener(this, mProximity, SensorManager.SENSOR_DELAY_NORMAL);
enableProximitySensing(true);
mActiveCallHeader.setVisibility(View.GONE);
mNoCurrentCall.setVisibility(View.GONE);
conferenceList.removeAllViews();

View file

@ -21,8 +21,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
@ -37,22 +42,29 @@ import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneCoreFactory;
import org.linphone.core.LinphoneCoreListenerBase;
import org.linphone.mediastream.Log;
import org.linphone.ui.BubbleChat;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.app.Fragment;
import android.app.ProgressDialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.ExifInterface;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@ -61,9 +73,10 @@ import android.os.Parcelable;
import android.provider.MediaStore;
import android.support.v4.content.CursorLoader;
import android.text.Editable;
import android.text.Spanned;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.view.ContextMenu;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
@ -72,7 +85,6 @@ import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
@ -81,6 +93,7 @@ import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
@ -111,6 +124,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
private SearchContactsListAdapter searchAdapter;
private ListView messagesList, resultContactsSearch;
private LayoutInflater inflater;
private Bitmap defaultBitmap;
private boolean isEditMode = false;
private LinphoneContact contact;
@ -143,9 +157,10 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
//Initialize UI
defaultBitmap = BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.chat_picture_over);
contactName = (TextView) view.findViewById(R.id.contact_name);
messagesList = (ListView) view.findViewById(R.id.chat_message_list);
messagesList.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
searchContactField = (EditText) view.findViewById(R.id.search_contact_field);
resultContactsSearch = (ListView) view.findViewById(R.id.result_contacts);
@ -214,7 +229,6 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
LinphoneService.instance().removeMessageNotification();
cr.markAsRead();
adapter.addMessage(cr.getHistory(1)[0]);
messagesList.setSelection(adapter.getCount()-1);
String externalBodyUrl = message.getExternalBodyUrl();
LinphoneContent fileTransferContent = message.getFileTransferInformation();
@ -320,135 +334,11 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
}
class ChatMessageAdapter extends BaseAdapter {
ArrayList<LinphoneChatMessage> history;
Context context;
public ChatMessageAdapter(Context c) {
context = c;
history = new ArrayList<LinphoneChatMessage>();
refreshHistory();
}
public void destroy() {
if (history != null) {
history.clear();
}
}
public void refreshHistory() {
history.clear();
LinphoneChatMessage[] messages = chatRoom.getHistory();
history.addAll(Arrays.asList(messages));
notifyDataSetChanged();
}
public void addMessage(LinphoneChatMessage message) {
history.add(message);
notifyDataSetChanged();
}
@Override
public int getCount() {
return history.size();
}
@Override
public LinphoneChatMessage getItem(int position) {
return history.get(position);
}
@Override
public long getItemId(int position) {
return history.get(position).getStorageId();
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
LinphoneChatMessage message = history.get(position);
RelativeLayout rlayout;
if (convertView != null) {
rlayout = (RelativeLayout) convertView;
View bbv = rlayout.getChildAt(0);
rlayout.removeAllViews();
BubbleChat bbc = (BubbleChat) bbv.getTag();
bbc.destroy();
} else {
rlayout = new RelativeLayout(context);
}
BubbleChat bubble = new BubbleChat(context, message, contact);
View v = bubble.getView();
v.setTag(bubble);
registerForContextMenu(v);
CheckBox deleteChatBubble = (CheckBox) v.findViewById(R.id.delete_message);
if(isEditMode) {
deleteChatBubble.setVisibility(View.VISIBLE);
if(message.isOutgoing()){
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams.setMargins(100, 10, 10, 10);
v.setLayoutParams(layoutParams);
} else {
LinearLayout message_layout = (LinearLayout) v.findViewById(R.id.message_content);
message_layout.setGravity(Gravity.RIGHT);
}
deleteChatBubble.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
messagesList.setItemChecked(position, b);
if (getNbItemsChecked() == getCount()) {
deselectAll.setVisibility(View.VISIBLE);
selectAll.setVisibility(View.GONE);
enabledDeleteButton(true);
} else {
if (getNbItemsChecked() == 0) {
deselectAll.setVisibility(View.GONE);
selectAll.setVisibility(View.VISIBLE);
enabledDeleteButton(false);
} else {
deselectAll.setVisibility(View.GONE);
selectAll.setVisibility(View.VISIBLE);
enabledDeleteButton(true);
}
}
}
});
if(messagesList.isItemChecked(position)) {
deleteChatBubble.setChecked(true);
} else {
deleteChatBubble.setChecked(false);
}
rlayout.addView(v);
} else {
if(message.isOutgoing()){
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams.setMargins(100, 10, 10, 10);
v.setLayoutParams(layoutParams);
} else {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
layoutParams.setMargins(10, 10, 100, 10);
v.setLayoutParams(layoutParams);
}
rlayout.addView(v);
}
return rlayout;
}
}
public void initChatRoom(String sipUri) {
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
LinphoneAddress lAddress = null;
if(sipUri == null){
if (sipUri == null) {
initNewChatConversation();
} else {
try {
@ -477,7 +367,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
private void displayMessageList() {
if(chatRoom != null) {
if (chatRoom != null) {
if (adapter != null) {
adapter.refreshHistory();
} else {
@ -488,7 +378,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
private void displayChatHeader(LinphoneAddress address) {
if(contact != null) {
if (contact != null) {
contactName.setText(contact.getFullName());
} else if(address != null){
contactName.setText(LinphoneUtils.getAddressDisplayName(address));
@ -580,6 +470,10 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
if (adapter != null) {
adapter.destroy();
}
if (defaultBitmap != null) {
defaultBitmap.recycle();
defaultBitmap = null;
}
super.onDestroy();
}
@ -618,7 +512,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
String draft = getArguments().getString("messageDraft");
message.setText(draft);
if(!newChatConversation) {
if (!newChatConversation) {
initChatRoom(sipUri);
searchContactField.setVisibility(View.GONE);
resultContactsSearch.setVisibility(View.GONE);
@ -626,10 +520,10 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
}
}
private void selectAllList(boolean isSelectAll){
private void selectAllList(boolean isSelectAll) {
int size = messagesList.getAdapter().getCount();
for(int i=0; i<size; i++) {
messagesList.setItemChecked(i,isSelectAll);
for (int i = 0; i < size; i++) {
messagesList.setItemChecked(i, isSelectAll);
}
}
@ -642,8 +536,8 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
private void removeChats(){
int size = messagesList.getAdapter().getCount();
for(int i=0; i<size; i++) {
if(messagesList.isItemChecked(i)){
for (int i = 0; i < size; i++) {
if (messagesList.isItemChecked(i)) {
LinphoneChatMessage message = (LinphoneChatMessage) messagesList.getAdapter().getItem(i);
chatRoom.deleteMessage(message);
}
@ -654,7 +548,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.back_to_call) {
LinphoneActivity.instance().resetClassicMenuLayoutAndGoBackToCallIfStillRunning();
return;
@ -827,7 +721,9 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
txt = message.getText();
}
if (txt != null) {
Compatibility.copyTextToClipboard(getActivity(), txt);
ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = android.content.ClipData.newPlainText("Message", txt);
clipboard.setPrimaryClip(clip);
LinphoneActivity.instance().displayCustomToast(getString(R.string.text_copied_to_clipboard), Toast.LENGTH_SHORT);
}
}
@ -1038,7 +934,492 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
searchAdapter.notifyDataSetChanged();
}
class ChatMessageAdapter extends BaseAdapter {
private class ViewHolder implements LinphoneChatMessage.LinphoneChatMessageListener {
public int id;
public RelativeLayout bubbleLayout;
public CheckBox delete;
public LinearLayout background;
public ImageView contactPicture;
public TextView contactName;
public TextView messageText;
public ImageView messageImage;
public RelativeLayout fileTransferLayout;
public ProgressBar fileTransferProgressBar;
public Button fileTransferAction;
public ImageView messageStatus;
public ProgressBar messageSendingInProgress;
public ViewHolder(View view) {
id = view.getId();
bubbleLayout = (RelativeLayout) view.findViewById(R.id.bubble);
delete = (CheckBox) view.findViewById(R.id.delete_message);
background = (LinearLayout) view.findViewById(R.id.background);
contactPicture = (ImageView) view.findViewById(R.id.contact_picture);
contactName = (TextView) view.findViewById(R.id.contact_header);
messageText = (TextView) view.findViewById(R.id.message);
messageImage = (ImageView) view.findViewById(R.id.image);
fileTransferLayout = (RelativeLayout) view.findViewById(R.id.file_transfer_layout);
fileTransferProgressBar = (ProgressBar) view.findViewById(R.id.progress_bar);
fileTransferAction = (Button) view.findViewById(R.id.file_transfer_action);
messageStatus = (ImageView) view.findViewById(R.id.status);
messageSendingInProgress = (ProgressBar) view.findViewById(R.id.inprogress);
}
@Override
public void onLinphoneChatMessageStateChanged(LinphoneChatMessage msg, State state) {
}
@Override
public void onLinphoneChatMessageFileTransferReceived(LinphoneChatMessage msg, LinphoneContent content, LinphoneBuffer buffer) {
}
@Override
public void onLinphoneChatMessageFileTransferSent(LinphoneChatMessage msg, LinphoneContent content, int offset, int size, LinphoneBuffer bufferToFill) {
}
@Override
public void onLinphoneChatMessageFileTransferProgressChanged(LinphoneChatMessage msg, LinphoneContent content, int offset, int total) {
if (msg.getStorageId() == id) fileTransferProgressBar.setProgress(offset * 100 / total);
}
}
ArrayList<LinphoneChatMessage> history;
Context context;
public ChatMessageAdapter(Context c) {
context = c;
history = new ArrayList<LinphoneChatMessage>();
refreshHistory();
}
public void destroy() {
if (history != null) {
history.clear();
}
}
public void refreshHistory() {
history.clear();
LinphoneChatMessage[] messages = chatRoom.getHistory();
history.addAll(Arrays.asList(messages));
notifyDataSetChanged();
}
public void addMessage(LinphoneChatMessage message) {
history.add(message);
notifyDataSetChanged();
messagesList.setSelection(getCount() - 1);
}
@Override
public int getCount() {
return history.size();
}
@Override
public LinphoneChatMessage getItem(int position) {
return history.get(position);
}
@Override
public long getItemId(int position) {
return history.get(position).getStorageId();
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final LinphoneChatMessage message = history.get(position);
View view = null;
final ViewHolder holder;
boolean sameMessage = false;
if (convertView != null) {
view = convertView;
holder = (ViewHolder) view.getTag();
LinphoneManager.removeListener(holder);
} else {
view = LayoutInflater.from(context).inflate(R.layout.chat_bubble, null);
holder = new ViewHolder(view);
view.setTag(holder);
}
if (holder.id == message.getStorageId()) {
sameMessage = true;
} else {
holder.id = message.getStorageId();
}
view.setId(holder.id);
registerForContextMenu(view);
LinphoneChatMessage.State status = message.getStatus();
String externalBodyUrl = message.getExternalBodyUrl();
LinphoneContent fileTransferContent = message.getFileTransferInformation();
holder.delete.setVisibility(View.GONE);
holder.messageText.setVisibility(View.GONE);
holder.messageImage.setVisibility(View.GONE);
holder.fileTransferLayout.setVisibility(View.GONE);
holder.fileTransferProgressBar.setProgress(0);
holder.fileTransferAction.setEnabled(true);
holder.messageStatus.setVisibility(View.INVISIBLE);
holder.messageSendingInProgress.setVisibility(View.GONE);
String displayName = message.getFrom().getDisplayName();
if (displayName == null) {
displayName = message.getFrom().getUserName();
}
if (!message.isOutgoing()) {
if (contact != null) {
if (contact != null && contact.getFullName() != null) {
displayName = contact.getFullName();
}
if (contact.hasPhoto()) {
Bitmap photo = contact.getPhoto();
if (photo != null) {
holder.contactPicture.setImageBitmap(photo);
} else {
LinphoneUtils.setImagePictureFromUri(getActivity(), holder.contactPicture, contact.getPhotoUri(), contact.getThumbnailUri());
}
} else {
holder.contactPicture.setImageResource(R.drawable.avatar);
}
} else {
holder.contactPicture.setImageResource(R.drawable.avatar);
}
} else {
holder.contactPicture.setImageResource(R.drawable.avatar);
}
holder.contactName.setText(timestampToHumanDate(context, message.getTime()) + " - " + displayName);
if (status == LinphoneChatMessage.State.NotDelivered) {
holder.messageStatus.setVisibility(View.VISIBLE);
holder.messageStatus.setImageResource(R.drawable.chat_message_not_delivered);
} else if (status == LinphoneChatMessage.State.InProgress) {
holder.messageSendingInProgress.setVisibility(View.VISIBLE);
}
if (externalBodyUrl != null || fileTransferContent != null) {
String appData = message.getAppData();
if (message.isOutgoing() && appData != null) {
holder.messageImage.setVisibility(View.VISIBLE);
if (!sameMessage) loadBitmap(appData, holder.messageImage);
if (LinphoneManager.getInstance().getMessageUploadPending() != null && LinphoneManager.getInstance().getMessageUploadPending().getStorageId() == message.getStorageId()) {
holder.messageSendingInProgress.setVisibility(View.GONE);
holder.fileTransferLayout.setVisibility(View.VISIBLE);
LinphoneManager.addListener(holder);
}
} else {
if (appData != null && !LinphoneManager.getInstance().isMessagePending(message) && appData.contains(context.getString(R.string.temp_photo_name_with_date).split("%s")[0])) {
appData = null;
}
if (appData == null) {
LinphoneManager.addListener(holder);
holder.fileTransferLayout.setVisibility(View.VISIBLE);
} else {
if (LinphoneManager.getInstance().isMessagePending(message)) {
LinphoneManager.addListener(holder);
holder.fileTransferAction.setEnabled(false);
holder.fileTransferLayout.setVisibility(View.VISIBLE);
} else {
LinphoneManager.removeListener(holder);
holder.fileTransferLayout.setVisibility(View.GONE);
holder.messageImage.setVisibility(View.VISIBLE);
if (!sameMessage) loadBitmap(appData, holder.messageImage);
}
}
}
} else {
Spanned text = null;
String msg = message.getText();
if (msg != null) {
text = getTextWithHttpLinks(msg);
holder.messageText.setText(text);
holder.messageText.setMovementMethod(LinkMovementMethod.getInstance());
holder.messageText.setVisibility(View.VISIBLE);
}
}
if (message.isOutgoing()) {
holder.fileTransferAction.setText(getString(R.string.cancel));
holder.fileTransferAction.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (LinphoneManager.getInstance().getMessageUploadPending() != null) {
holder.fileTransferProgressBar.setVisibility(View.GONE);
holder.fileTransferProgressBar.setProgress(0);
message.cancelFileTransfer();
LinphoneManager.getInstance().setUploadPendingFileMessage(null);
}
}
});
} else {
holder.fileTransferAction.setText(getString(R.string.accept));
holder.fileTransferAction.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (context.getPackageManager().checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, context.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
v.setEnabled(false);
String extension = message.getFileTransferInformation().getSubtype();
String filename = context.getString(R.string.temp_photo_name_with_date).replace("%s", String.valueOf(System.currentTimeMillis())) + "." + extension;
File file = new File(Environment.getExternalStorageDirectory(), filename);
message.setAppData(filename);
LinphoneManager.getInstance().addDownloadMessagePending(message);
message.setListener(LinphoneManager.getInstance());
message.setFileTransferFilepath(file.getPath());
message.downloadFile();
} else {
Log.w("WRITE_EXTERNAL_STORAGE permission not granted, won't be able to store the downloaded file");
LinphoneActivity.instance().checkAndRequestExternalStoragePermission();
}
}
});
}
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
if (message.isOutgoing()) {
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams.setMargins(100, 10, 10, 10);
holder.background.setBackgroundResource(R.drawable.resizable_chat_bubble_outgoing);
holder.contactName.setTextAppearance(R.style.font3);
holder.fileTransferAction.setTextAppearance(R.style.font15);
holder.fileTransferAction.setBackgroundResource(R.drawable.resizable_confirm_delete_button);
} else {
if (isEditMode) {
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams.setMargins(100, 10, 10, 10);
} else {
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
layoutParams.setMargins(10, 10, 100, 10);
}
holder.background.setBackgroundResource(R.drawable.resizable_chat_bubble_incoming);
holder.contactName.setTextAppearance(R.style.font9);
holder.fileTransferAction.setTextAppearance(R.style.font8);
holder.fileTransferAction.setBackgroundResource(R.drawable.resizable_assistant_button);
}
holder.bubbleLayout.setLayoutParams(layoutParams);
if (isEditMode) {
holder.delete.setVisibility(View.VISIBLE);
holder.delete.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
messagesList.setItemChecked(position, b);
if (getNbItemsChecked() == getCount()) {
deselectAll.setVisibility(View.VISIBLE);
selectAll.setVisibility(View.GONE);
enabledDeleteButton(true);
} else {
if (getNbItemsChecked() == 0) {
deselectAll.setVisibility(View.GONE);
selectAll.setVisibility(View.VISIBLE);
enabledDeleteButton(false);
} else {
deselectAll.setVisibility(View.GONE);
selectAll.setVisibility(View.VISIBLE);
enabledDeleteButton(true);
}
}
}
});
if (messagesList.isItemChecked(position)) {
holder.delete.setChecked(true);
} else {
holder.delete.setChecked(false);
}
}
return view;
}
private String timestampToHumanDate(Context context, long timestamp) {
try {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(timestamp);
SimpleDateFormat dateFormat;
if (isToday(cal)) {
dateFormat = new SimpleDateFormat(context.getResources().getString(R.string.today_date_format));
} else {
dateFormat = new SimpleDateFormat(context.getResources().getString(R.string.messages_date_format));
}
return dateFormat.format(cal.getTime());
} catch (NumberFormatException nfe) {
return String.valueOf(timestamp);
}
}
private boolean isToday(Calendar cal) {
return isSameDay(cal, Calendar.getInstance());
}
private boolean isSameDay(Calendar cal1, Calendar cal2) {
if (cal1 == null || cal2 == null) {
return false;
}
return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR));
}
private Spanned getTextWithHttpLinks(String text) {
if (text.contains("<")) {
text = text.replace("<", "&lt;");
}
if (text.contains(">")) {
text = text.replace(">", "&gt;");
}
if (text.contains("http://")) {
int indexHttp = text.indexOf("http://");
int indexFinHttp = text.indexOf(" ", indexHttp) == -1 ? text.length() : text.indexOf(" ", indexHttp);
String link = text.substring(indexHttp, indexFinHttp);
String linkWithoutScheme = link.replace("http://", "");
text = text.replaceFirst(link, "<a href=\"" + link + "\">" + linkWithoutScheme + "</a>");
}
if (text.contains("https://")) {
int indexHttp = text.indexOf("https://");
int indexFinHttp = text.indexOf(" ", indexHttp) == -1 ? text.length() : text.indexOf(" ", indexHttp);
String link = text.substring(indexHttp, indexFinHttp);
String linkWithoutScheme = link.replace("https://", "");
text = text.replaceFirst(link, "<a href=\"" + link + "\">" + linkWithoutScheme + "</a>");
}
return Compatibility.fromHtml(text);
}
public void loadBitmap(String path, ImageView imageView) {
if (cancelPotentialWork(path, imageView)) {
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
final AsyncBitmap asyncBitmap = new AsyncBitmap(context.getResources(), defaultBitmap, task);
imageView.setImageDrawable(asyncBitmap);
task.execute(path);
}
}
private class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
public String path;
public BitmapWorkerTask(ImageView imageView) {
path = null;
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
@Override
protected Bitmap doInBackground(String... params) {
path = params[0];
Bitmap bm = null;
if (path.startsWith("content")) {
try {
bm = MediaStore.Images.Media.getBitmap(context.getContentResolver(), Uri.parse(path));
} catch (FileNotFoundException e) {
Log.e(e);
} catch (IOException e) {
Log.e(e);
}
} else {
bm = BitmapFactory.decodeFile(path);
path = "file://" + path;
}
if (bm != null) {
bm = ThumbnailUtils.extractThumbnail(bm, SIZE_MAX, SIZE_MAX);
}
return bm;
}
// Once complete, see if ImageView is still around and set bitmap.
@Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask && imageView != null) {
imageView.setImageBitmap(bitmap);
imageView.setTag(path);
imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse((String)v.getTag()), "image/*");
context.startActivity(intent);
}
});
}
}
}
}
class AsyncBitmap extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncBitmap(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
}
private boolean cancelPotentialWork(String path, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
final String bitmapData = bitmapWorkerTask.path;
// If bitmapData is not yet set or it differs from the new data
if (bitmapData == null || bitmapData != path) {
// Cancel previous task
bitmapWorkerTask.cancel(true);
} else {
// The same work is already in progress
return false;
}
}
// No task associated with the ImageView, or an existing task was cancelled
return true;
}
private BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncBitmap) {
final AsyncBitmap asyncDrawable = (AsyncBitmap) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
}
class SearchContactsListAdapter extends BaseAdapter {
private class ViewHolder {
public TextView name;
public TextView address;
public ViewHolder(View view) {
name = (TextView) view.findViewById(R.id.contact_name);
address = (TextView) view.findViewById(R.id.contact_address);
}
}
private List<ContactAddress> contacts;
private LayoutInflater mInflater;
@ -1089,24 +1470,26 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
ContactAddress contact;
ViewHolder holder = null;
do {
contact = getItem(position);
} while (contact == null);
if (convertView != null) {
view = convertView;
holder = (ViewHolder) view.getTag();
} else {
view = mInflater.inflate(R.layout.search_contact_cell, parent, false);
holder = new ViewHolder(view);
view.setTag(holder);
}
final String a = contact.address;
LinphoneContact c = contact.contact;
TextView name = (TextView) view.findViewById(R.id.contact_name);
name.setText(c.getFullName());
TextView address = (TextView) view.findViewById(R.id.contact_address);
address.setText(a);
holder.name.setText(c.getFullName());
holder.address.setText(a);
view.setOnClickListener(new OnClickListener() {
@Override

View file

@ -30,6 +30,7 @@ import org.linphone.mediastream.Log;
import android.app.Dialog;
import android.app.Fragment;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.ContextMenu;
@ -117,15 +118,16 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
}
}
private void removeChatsConversation(){
private void removeChatsConversation() {
int size = chatList.getAdapter().getCount();
for(int i=0; i<size; i++) {
if(chatList.isItemChecked(i)){
View item = chatList.getAdapter().getView(i, null, null);
if(item != null) {
LinphoneChatRoom chatroom = LinphoneManager.getLc().getOrCreateChatRoom(item.getTag().toString());
if (chatroom != null)
for (int i = 0; i < size; i++) {
if (chatList.isItemChecked(i)) {
String sipUri = chatList.getAdapter().getItem(i).toString();
if (sipUri != null) {
LinphoneChatRoom chatroom = LinphoneManager.getLc().getOrCreateChatRoom(sipUri);
if (chatroom != null) {
chatroom.deleteHistory();
}
}
}
}
@ -241,7 +243,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
if (info == null || info.targetView == null) {
return false;
}
String sipUri = (String) info.targetView.getTag();
String sipUri = chatList.getAdapter().getItem(info.position).toString();
LinphoneActivity.instance().removeFromChatList(sipUri);
mConversations = LinphoneActivity.instance().getChatList();
@ -331,7 +333,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
@Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
String sipUri = (String) view.getTag();
String sipUri = chatList.getAdapter().getItem(position).toString();
if (LinphoneActivity.isInstanciated() && !isEditMode) {
LinphoneActivity.instance().displayChat(sipUri);
@ -339,7 +341,24 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
}
class ChatListAdapter extends BaseAdapter {
private class ViewHolder {
public TextView lastMessageView;
public TextView date;
public TextView displayName;
public TextView unreadMessages;
public CheckBox select;
public ImageView contactPicture;
public ViewHolder(View view) {
lastMessageView = (TextView) view.findViewById(R.id.lastMessage);
date = (TextView) view.findViewById(R.id.date);
displayName = (TextView) view.findViewById(R.id.sipUri);
unreadMessages = (TextView) view.findViewById(R.id.unreadMessages);
select = (CheckBox) view.findViewById(R.id.delete_chatroom);
contactPicture = (ImageView) view.findViewById(R.id.contact_picture);
}
}
ChatListAdapter() {}
public int getCount() {
@ -347,7 +366,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
}
public Object getItem(int position) {
return position;
return mConversations.get(position);
}
public long getItemId(int position) {
@ -356,21 +375,23 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
public View getView(final int position, View convertView, ViewGroup parent) {
View view = null;
ViewHolder holder = null;
String sipUri = mConversations.get(position);
if (convertView != null) {
view = convertView;
holder = (ViewHolder) view.getTag();
} else {
view = mInflater.inflate(R.layout.chatlist_cell, parent, false);
holder = new ViewHolder(view);
view.setTag(holder);
}
String sipUri = mConversations.get(position);
view.setTag(sipUri);
LinphoneAddress address;
try {
address = LinphoneCoreFactory.instance().createLinphoneAddress(sipUri);
} catch (LinphoneCoreException e) {
Log.e("Chat view cannot parse address",e);
Log.e("Chat view cannot parse address", e);
return view;
}
@ -378,57 +399,55 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
String message = "";
Long time;
TextView lastMessageView = (TextView) view.findViewById(R.id.lastMessage);
TextView date = (TextView) view.findViewById(R.id.date);
TextView displayName = (TextView) view.findViewById(R.id.sipUri);
TextView unreadMessages = (TextView) view.findViewById(R.id.unreadMessages);
CheckBox select = (CheckBox) view.findViewById(R.id.delete_chatroom);
ImageView contactPicture = (ImageView) view.findViewById(R.id.contact_picture);
LinphoneChatRoom chatRoom = LinphoneManager.getLc().getChatRoom(address);
int unreadMessagesCount = chatRoom.getUnreadMessagesCount();
LinphoneChatMessage[] history = chatRoom.getHistory(1);
LinphoneChatMessage msg = history[0];
if(msg.getFileTransferInformation() != null || msg.getExternalBodyUrl() != null || msg.getAppData() != null ){
lastMessageView.setBackgroundResource(R.drawable.chat_file_message);
holder.lastMessageView.setBackgroundResource(R.drawable.chat_file_message);
time = msg.getTime();
date.setText(LinphoneUtils.timestampToHumanDate(getActivity(),time,getString(R.string.messages_list_date_format)));
lastMessageView.setText("");
holder.date.setText(LinphoneUtils.timestampToHumanDate(getActivity(),time,getString(R.string.messages_list_date_format)));
holder.lastMessageView.setText("");
} else if (msg.getText() != null && msg.getText().length() > 0 ){
message = msg.getText();
lastMessageView.setBackgroundResource(0);
holder.lastMessageView.setBackgroundResource(0);
time = msg.getTime();
date.setText(LinphoneUtils.timestampToHumanDate(getActivity(),time,getString(R.string.messages_list_date_format)));
lastMessageView.setText(message);
holder.date.setText(LinphoneUtils.timestampToHumanDate(getActivity(),time,getString(R.string.messages_list_date_format)));
holder.lastMessageView.setText(message);
}
displayName.setSelected(true); // For animation
displayName.setText(contact == null ? LinphoneUtils.getAddressDisplayName(address) : contact.getFullName());
holder.displayName.setSelected(true); // For animation
holder.displayName.setText(contact == null ? LinphoneUtils.getAddressDisplayName(address) : contact.getFullName());
if (contact != null) {
LinphoneUtils.setImagePictureFromUri(view.getContext(), contactPicture, contact.getPhotoUri(), contact.getThumbnailUri());
Bitmap photo = contact.getPhoto();
if (photo != null) {
holder.contactPicture.setImageBitmap(photo);
} else {
LinphoneUtils.setImagePictureFromUri(getActivity(), holder.contactPicture, contact.getPhotoUri(), contact.getThumbnailUri());
}
} else {
contactPicture.setImageResource(R.drawable.avatar);
holder.contactPicture.setImageResource(R.drawable.avatar);
}
if (unreadMessagesCount > 0) {
unreadMessages.setVisibility(View.VISIBLE);
unreadMessages.setText(String.valueOf(unreadMessagesCount));
if(unreadMessagesCount > 99){
unreadMessages.setTextSize(12);
holder.unreadMessages.setVisibility(View.VISIBLE);
holder.unreadMessages.setText(String.valueOf(unreadMessagesCount));
if (unreadMessagesCount > 99) {
holder.unreadMessages.setTextSize(12);
}
displayName.setTypeface(null, Typeface.BOLD);
holder.displayName.setTypeface(null, Typeface.BOLD);
} else {
unreadMessages.setVisibility(View.GONE);
displayName.setTypeface(null, Typeface.NORMAL);
holder.unreadMessages.setVisibility(View.GONE);
holder.displayName.setTypeface(null, Typeface.NORMAL);
}
if (isEditMode) {
unreadMessages.setVisibility(View.GONE);
select.setVisibility(View.VISIBLE);
select.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
holder.unreadMessages.setVisibility(View.GONE);
holder.select.setVisibility(View.VISIBLE);
holder.select.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
chatList.setItemChecked(position, b);
@ -450,13 +469,13 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte
}
});
if(chatList.isItemChecked(position)) {
select.setChecked(true);
holder.select.setChecked(true);
} else {
select.setChecked(false);
holder.select.setChecked(false);
}
} else {
if (unreadMessagesCount > 0) {
unreadMessages.setVisibility(View.VISIBLE);
holder.unreadMessages.setVisibility(View.VISIBLE);
}
}
return view;

View file

@ -1,117 +0,0 @@
package org.linphone;
import org.linphone.core.LinphoneChatMessage;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
/*
ChatMessage.java
Copyright (C) 2012 Belledonne Communications, Grenoble, France
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 2
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/**
* @author Sylvain Berfini
* @deprecated
*/
public class ChatMessage {
private String message;
private String timestamp;
private String url;
private boolean incoming;
private int status;
private int id;
private Bitmap image;
private boolean isRead;
public ChatMessage(int id, String message, byte[] rawImage, String timestamp, boolean incoming, int status, boolean read) {
super();
this.id = id;
this.message = message;
this.timestamp = timestamp;
this.incoming = incoming;
this.status = status;
this.image = rawImage != null ? BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length) : null;
this.isRead = read;
}
public ChatMessage(int id, String message, Bitmap image, String timestamp, boolean incoming, int status, boolean read) {
super();
this.id = id;
this.message = message;
this.timestamp = timestamp;
this.incoming = incoming;
this.status = status;
this.image = image;
this.isRead = read;
}
public int getId() {
return id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public boolean isIncoming() {
return incoming;
}
public void setIncoming(boolean incoming) {
this.incoming = incoming;
}
public void setStatus(int status) {
this.status = status;
}
public LinphoneChatMessage.State getStatus() {
return LinphoneChatMessage.State.fromInt(status);
}
public Bitmap getImage() {
return image;
}
public boolean isRead() {
return isRead;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String toString() {
return this.id + " : " + this.message + " (" + this.url + ") @ " + this.timestamp + ", read= " + this.isRead + ", incoming= " + this.incoming + ", status = " + this.status;
}
}

View file

@ -1,139 +0,0 @@
package org.linphone;
/*
Contact.java
Copyright (C) 2012 Belledonne Communications, Grenoble, France
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 2
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.linphone.compatibility.Compatibility;
import org.linphone.core.LinphoneAddress;
import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneFriend;
import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.net.Uri;
/**
* @author Sylvain Berfini
* @deprecated
*/
public class Contact implements Serializable {
private static final long serialVersionUID = 3790717505065723499L;
private String id;
private String name;
private transient Uri photoUri;
private transient Uri thumbnailUri;
private transient Bitmap photo;
private List<String> numbersOrAddresses;
private boolean hasFriends;
private LinphoneAddress address;
public Contact(String id, String name) {
super();
this.id = id;
this.name = name;
this.photoUri = null;
this.thumbnailUri = null;
this.hasFriends = false;
this.address = null;
}
public Contact(String id, LinphoneAddress address) {
super();
this.id = id;
this.name = LinphoneUtils.getAddressDisplayName(address);
this.photoUri = null;
this.thumbnailUri = null;
this.address = address;
}
public Contact(String id, String name, Uri photo, Uri thumbnail) {
super();
this.id = id;
this.name = name;
this.photoUri = photo;
this.thumbnailUri = thumbnail;
this.photo = null;
this.hasFriends = false;
this.address = null;
}
public Contact(String id, String name, Uri photo, Uri thumbnail, Bitmap picture) {
super();
this.id = id;
this.name = name;
this.photoUri = photo;
this.thumbnailUri = thumbnail;
this.photo = picture;
this.hasFriends = false;
this.address = null;
}
public boolean hasFriends() {
return hasFriends;
}
public String getID() {
return id;
}
public String getName() {
return name;
}
public LinphoneAddress getLinphoneAddress() {
return address;
}
public Uri getPhotoUri() {
return photoUri;
}
public Uri getThumbnailUri() {
return thumbnailUri;
}
public Bitmap getPhoto() {
return photo;
}
public List<String> getNumbersOrAddresses() {
if (numbersOrAddresses == null)
numbersOrAddresses = new ArrayList<String>();
return numbersOrAddresses;
}
public void refresh(ContentResolver cr) {
this.numbersOrAddresses = Compatibility.extractContactNumbersAndAddresses(id, cr);
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
if(lc != null && lc.getFriendList() != null) {
for (LinphoneFriend friend :lc.getFriendList()){
if (id.equals(friend.getRefKey())) {
hasFriends = true;
this.numbersOrAddresses.add(friend.getAddress().asStringUriOnly());
}
}
}
this.name = Compatibility.refreshContactName(cr, id);
}
}

View file

@ -78,8 +78,9 @@ public class ContactDetailsFragment extends Fragment implements OnClickListener
deleteContact.setOnClickListener(this);
organization = (TextView) view.findViewById(R.id.contactOrganization);
boolean isOrgVisible = getResources().getBoolean(R.bool.display_contact_organization);
String org = contact.getOrganization();
if (org != null && !org.isEmpty()) {
if (org != null && !org.isEmpty() && isOrgVisible) {
organization.setText(org);
} else {
organization.setVisibility(View.GONE);

View file

@ -204,9 +204,16 @@ public class ContactEditorFragment extends Fragment {
}
});
organization = (EditText) view.findViewById(R.id.contactOrganization);
if (!isNewContact) {
organization.setText(contact.getOrganization());
boolean isOrgVisible = getResources().getBoolean(R.bool.display_contact_organization);
if (!isOrgVisible) {
organization.setVisibility(View.GONE);
view.findViewById(R.id.contactOrganizationTitle).setVisibility(View.GONE);
} else {
if (!isNewContact) {
organization.setText(contact.getOrganization());
}
}
if (!isNewContact) {

View file

@ -30,6 +30,7 @@ import org.linphone.core.PresenceActivityType;
import android.app.Dialog;
import android.app.Fragment;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
@ -416,6 +417,28 @@ public class ContactsListFragment extends Fragment implements OnClickListener, O
}
class ContactsListAdapter extends BaseAdapter implements SectionIndexer {
private class ViewHolder {
public CheckBox delete;
public ImageView linphoneFriend;
public TextView name;
public LinearLayout separator;
public TextView separatorText;
public ImageView contactPicture;
public TextView organization;
public ImageView friendStatus;
public ViewHolder(View view) {
delete = (CheckBox) view.findViewById(R.id.delete);
linphoneFriend = (ImageView) view.findViewById(R.id.friendLinphone);
name = (TextView) view.findViewById(R.id.name);
separator = (LinearLayout) view.findViewById(R.id.separator);
separatorText = (TextView) view.findViewById(R.id.separator_text);
contactPicture = (ImageView) view.findViewById(R.id.contact_picture);
organization = (TextView) view.findViewById(R.id.contactOrganization);
friendStatus = (ImageView) view.findViewById(R.id.friendStatus);
}
}
private List<LinphoneContact> contacts;
String[] sections;
ArrayList<String> sectionsList;
@ -461,57 +484,57 @@ public class ContactsListFragment extends Fragment implements OnClickListener, O
LinphoneContact contact = (LinphoneContact) getItem(position);
if (contact == null) return null;
ViewHolder holder = null;
if (convertView != null) {
view = convertView;
holder = (ViewHolder) view.getTag();
} else {
view = mInflater.inflate(R.layout.contact_cell, parent, false);
holder = new ViewHolder(view);
view.setTag(holder);
}
CheckBox delete = (CheckBox) view.findViewById(R.id.delete);
ImageView linphoneFriend = (ImageView) view.findViewById(R.id.friendLinphone);
TextView name = (TextView) view.findViewById(R.id.name);
name.setText(contact.getFullName());
LinearLayout separator = (LinearLayout) view.findViewById(R.id.separator);
TextView separatorText = (TextView) view.findViewById(R.id.separator_text);
holder.name.setText(contact.getFullName());
if (getPositionForSection(getSectionForPosition(position)) != position) {
separator.setVisibility(View.GONE);
holder.separator.setVisibility(View.GONE);
} else {
separator.setVisibility(View.VISIBLE);
holder.separator.setVisibility(View.VISIBLE);
String fullName = contact.getFullName();
if (fullName != null && !fullName.isEmpty()) {
separatorText.setText(String.valueOf(fullName.charAt(0)));
holder.separatorText.setText(String.valueOf(fullName.charAt(0)));
}
}
if (contact.isInLinphoneFriendList()) {
linphoneFriend.setVisibility(View.VISIBLE);
holder.linphoneFriend.setVisibility(View.VISIBLE);
} else {
linphoneFriend.setVisibility(View.GONE);
holder.linphoneFriend.setVisibility(View.GONE);
}
ImageView icon = (ImageView) view.findViewById(R.id.contact_picture);
if (contact.hasPhoto()) {
LinphoneUtils.setImagePictureFromUri(getActivity(), icon, contact.getPhotoUri(), contact.getThumbnailUri());
} else if (contact.getPhotoUri() != null) {
icon.setImageURI(contact.getPhotoUri());
Bitmap photo = contact.getPhoto();
if (photo != null) {
holder.contactPicture.setImageBitmap(photo);
} else {
LinphoneUtils.setImagePictureFromUri(getActivity(), holder.contactPicture, contact.getPhotoUri(), contact.getThumbnailUri());
}
} else {
icon.setImageResource(R.drawable.avatar);
holder.contactPicture.setImageResource(R.drawable.avatar);
}
TextView organization = (TextView) view.findViewById(R.id.contactOrganization);
boolean isOrgVisible = getResources().getBoolean(R.bool.display_contact_organization);
String org = contact.getOrganization();
if (org != null && !org.isEmpty()) {
organization.setText(org);
organization.setVisibility(View.VISIBLE);
if (org != null && !org.isEmpty() && isOrgVisible) {
holder.organization.setText(org);
holder.organization.setVisibility(View.VISIBLE);
} else {
organization.setVisibility(View.GONE);
holder.organization.setVisibility(View.GONE);
}
if (isEditMode) {
delete.setVisibility(View.VISIBLE);
delete.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
holder.delete.setVisibility(View.VISIBLE);
holder.delete.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
contactsList.setItemChecked(position, b);
@ -533,29 +556,28 @@ public class ContactsListFragment extends Fragment implements OnClickListener, O
}
});
if (contactsList.isItemChecked(position)) {
delete.setChecked(true);
holder.delete.setChecked(true);
} else {
delete.setChecked(false);
holder.delete.setChecked(false);
}
} else {
delete.setVisibility(View.GONE);
holder.delete.setVisibility(View.GONE);
}
ImageView friendStatus = (ImageView) view.findViewById(R.id.friendStatus);
LinphoneFriend[] friends = LinphoneManager.getLc().getFriendList();
if (!ContactsManager.getInstance().isContactPresenceDisabled() && friends != null) {
friendStatus.setVisibility(View.VISIBLE);
holder.friendStatus.setVisibility(View.VISIBLE);
PresenceActivityType presenceActivity = friends[0].getPresenceModel().getActivity().getType();
if (presenceActivity == PresenceActivityType.Online) {
friendStatus.setImageResource(R.drawable.led_connected);
holder.friendStatus.setImageResource(R.drawable.led_connected);
} else if (presenceActivity == PresenceActivityType.Busy) {
friendStatus.setImageResource(R.drawable.led_error);
holder.friendStatus.setImageResource(R.drawable.led_error);
} else if (presenceActivity == PresenceActivityType.Away) {
friendStatus.setImageResource(R.drawable.led_inprogress);
holder.friendStatus.setImageResource(R.drawable.led_inprogress);
} else if (presenceActivity == PresenceActivityType.Offline) {
friendStatus.setImageResource(R.drawable.led_disconnected);
holder.friendStatus.setImageResource(R.drawable.led_disconnected);
} else {
friendStatus.setImageResource(R.drawable.call_quality_indicator_0);
holder.friendStatus.setImageResource(R.drawable.call_quality_indicator_0);
}
}

View file

@ -21,10 +21,11 @@ package org.linphone;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.linphone.compatibility.Compatibility;
import org.linphone.core.LinphoneAddress;
import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneFriend;
@ -38,11 +39,13 @@ import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.Data;
interface ContactsUpdatedListener {
@ -228,14 +231,13 @@ public class ContactsManager extends ContentObserver {
contactsFetchTask.execute();
}
private class ContactsFetchTask extends AsyncTask<Void, List<LinphoneContact>, List<LinphoneContact>> {
@SuppressWarnings("unchecked")
protected List<LinphoneContact> doInBackground(Void... params) {
List<LinphoneContact> contacts = new ArrayList<LinphoneContact>();
if (hasContactsAccess()) {
Cursor c = Compatibility.getContactsCursor(contentResolver, null);
Cursor c = getContactsCursor(contentResolver);
if (c != null) {
while (c.moveToNext()) {
String id = c.getString(c.getColumnIndex(Data.CONTACT_ID));
@ -367,4 +369,38 @@ public class ContactsManager extends ContentObserver {
public String getString(int resourceID) {
return context.getString(resourceID);
}
private Cursor getContactsCursor(ContentResolver cr) {
String req = "(" + Data.MIMETYPE + " = '" + CommonDataKinds.Phone.CONTENT_ITEM_TYPE
+ "' AND " + CommonDataKinds.Phone.NUMBER + " IS NOT NULL "
+ " OR (" + Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
+ "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " IS NOT NULL))";
String[] projection = new String[] { Data.CONTACT_ID, Data.DISPLAY_NAME };
String query = Data.DISPLAY_NAME + " IS NOT NULL AND (" + req + ")";
Cursor cursor = cr.query(Data.CONTENT_URI, projection, query, null, " lower(" + Data.DISPLAY_NAME + ") COLLATE UNICODE ASC");
if (cursor == null) {
return cursor;
}
MatrixCursor result = new MatrixCursor(cursor.getColumnNames());
Set<String> groupBy = new HashSet<String>();
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex(Data.DISPLAY_NAME));
if (!groupBy.contains(name)) {
groupBy.add(name);
Object[] newRow = new Object[cursor.getColumnCount()];
int contactID = cursor.getColumnIndex(Data.CONTACT_ID);
int displayName = cursor.getColumnIndex(Data.DISPLAY_NAME);
newRow[contactID] = cursor.getString(contactID);
newRow[displayName] = cursor.getString(displayName);
result.addRow(newRow);
}
}
cursor.close();
return result;
}
}

View file

@ -31,6 +31,7 @@ import org.linphone.core.LinphoneCallLog.CallStatus;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.app.Fragment;
import android.view.LayoutInflater;
@ -349,6 +350,22 @@ public class HistoryListFragment extends Fragment implements OnClickListener, On
}
class CallHistoryAdapter extends BaseAdapter {
private class ViewHolder {
public TextView contact;
public ImageView detail;
public CheckBox select;
public ImageView callDirection;
public ImageView contactPicture;
public ViewHolder(View view) {
contact = (TextView) view.findViewById(R.id.sip_uri);
detail = (ImageView) view.findViewById(R.id.detail);
select = (CheckBox) view.findViewById(R.id.delete);
callDirection = (ImageView) view.findViewById(R.id.icon);
contactPicture = (ImageView) view.findViewById(R.id.contact_picture);
}
}
CallHistoryAdapter(Context aContext) {
}
@ -401,23 +418,20 @@ public class HistoryListFragment extends Fragment implements OnClickListener, On
public View getView(final int position, View convertView, ViewGroup parent) {
View view = null;
ViewHolder holder;
ViewHolder holder = null;
if (convertView != null) {
view = convertView;
holder = (ViewHolder) view.getTag();
} else {
view = mInflater.inflate(R.layout.history_cell, parent,false);
holder = new ViewHolder();
holder.contact = (TextView) view.findViewById(R.id.sip_uri);
holder.detail = (ImageView) view.findViewById(R.id.detail);
holder.select = (CheckBox) view.findViewById(R.id.delete);
holder.callDirection = (ImageView) view.findViewById(R.id.icon);
holder.contactPicture = (ImageView) view.findViewById(R.id.contact_picture);
holder = new ViewHolder(view);
view.setTag(holder);
}
final LinphoneCallLog log = mLogs.get(position);
long timestamp = log.getTimestamp();
final LinphoneAddress address;
LinphoneAddress address;
holder.contact.setSelected(true); // For automated horizontal scrolling of long texts
@ -457,9 +471,18 @@ public class HistoryListFragment extends Fragment implements OnClickListener, On
LinphoneContact c = ContactsManager.getInstance().findContactFromAddress(address);
String displayName = null;
final String sipUri = address.asString();
if(c != null){
if (c != null) {
displayName = c.getFullName();
LinphoneUtils.setImagePictureFromUri(view.getContext(),holder.contactPicture,c.getPhotoUri(),c.getThumbnailUri());
if (c.hasPhoto()) {
Bitmap photo = c.getPhoto();
if (photo != null) {
holder.contactPicture.setImageBitmap(photo);
} else {
LinphoneUtils.setImagePictureFromUri(getActivity(), holder.contactPicture, c.getPhotoUri(), c.getThumbnailUri());
}
} else {
LinphoneUtils.setImagePictureFromUri(getActivity(), holder.contactPicture, c.getPhotoUri(), c.getThumbnailUri());
}
} else {
holder.contactPicture.setImageResource(R.drawable.avatar);
}
@ -469,7 +492,6 @@ public class HistoryListFragment extends Fragment implements OnClickListener, On
} else {
holder.contact.setText(displayName);
}
//view.setTag(sipUri);
if (isEditMode) {
holder.select.setVisibility(View.VISIBLE);
@ -512,16 +534,7 @@ public class HistoryListFragment extends Fragment implements OnClickListener, On
}
});
}
view.setTag(holder);
return view;
}
}
static class ViewHolder {
TextView contact;
ImageView detail;
CheckBox select;
ImageView callDirection;
ImageView contactPicture;
}
}

View file

@ -110,6 +110,7 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta
private static final int PERMISSIONS_REQUEST_CONTACTS = 208;
private static final int PERMISSIONS_RECORD_AUDIO_ECHO_CANCELLER = 209;
private static final int PERMISSIONS_READ_EXTERNAL_STORAGE_DEVICE_RINGTONE = 210;
private static final int PERMISSIONS_RECORD_AUDIO_ECHO_TESTER = 211;
private static LinphoneActivity instance;
@ -1178,7 +1179,7 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta
public void checkAndRequestReadContactsPermission() {
checkAndRequestPermission(Manifest.permission.READ_CONTACTS, PERMISSIONS_REQUEST_CONTACTS);
}
private boolean willContactsPermissionBeAsked() {
return LinphonePreferences.instance().firstTimeAskingForPermission(Manifest.permission.READ_CONTACTS) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS);
}
@ -1191,6 +1192,10 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta
checkAndRequestPermission(Manifest.permission.RECORD_AUDIO, PERMISSIONS_RECORD_AUDIO_ECHO_CANCELLER);
}
public void checkAndRequestRecordAudioPermissionsForEchoTester() {
checkAndRequestPermission(Manifest.permission.RECORD_AUDIO, PERMISSIONS_RECORD_AUDIO_ECHO_TESTER);
}
public void checkAndRequestReadExternalStoragePermissionForDeviceRingtone() {
checkAndRequestPermission(Manifest.permission.READ_EXTERNAL_STORAGE, PERMISSIONS_READ_EXTERNAL_STORAGE_DEVICE_RINGTONE);
}
@ -1269,6 +1274,10 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta
case PERMISSIONS_READ_EXTERNAL_STORAGE_DEVICE_RINGTONE:
((SettingsFragment) fragment).enableDeviceRingtone(grantResults[0] == PackageManager.PERMISSION_GRANTED);
break;
case PERMISSIONS_RECORD_AUDIO_ECHO_TESTER:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
((SettingsFragment) fragment).startEchoTester();
break;
}
}

View file

@ -18,6 +18,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.linphone;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
@ -38,8 +40,10 @@ import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.MediaStore;
import android.provider.ContactsContract.CommonDataKinds;
public class LinphoneContact implements Serializable, Comparable<LinphoneContact> {
@ -55,6 +59,7 @@ public class LinphoneContact implements Serializable, Comparable<LinphoneContact
private transient ArrayList<ContentProviderOperation> changesToCommit;
private transient ArrayList<ContentProviderOperation> changesToCommit2;
private boolean hasSipAddress;
private Bitmap photoBitmap, thumbnailBitmap;
public LinphoneContact() {
addresses = new ArrayList<LinphoneNumberOrAddress>();
@ -66,6 +71,19 @@ public class LinphoneContact implements Serializable, Comparable<LinphoneContact
hasSipAddress = false;
}
@Override
protected void finalize() throws Throwable {
if (photoBitmap != null) {
photoBitmap.recycle();
photoBitmap = null;
}
if (thumbnailBitmap != null) {
thumbnailBitmap.recycle();
thumbnailBitmap = null;
}
super.finalize();
}
@Override
public int compareTo(LinphoneContact contact) {
String fullName = getFullName();
@ -167,21 +185,62 @@ public class LinphoneContact implements Serializable, Comparable<LinphoneContact
}
public void setPhotoUri(Uri uri) {
if (uri.equals(photoUri)) return;
photoUri = uri;
if (photoBitmap != null) {
photoBitmap.recycle();
}
try {
photoBitmap = MediaStore.Images.Media.getBitmap(ContactsManager.getInstance().getContentResolver(), photoUri);
} catch (FileNotFoundException e) {
// Let's not say anything if the picture doesn't exist, it will pollute the logs
} catch (IOException e) {
Log.e(e);
}
}
public Uri getPhotoUri() {
return photoUri;
}
public Bitmap getPhotoBitmap() {
return photoBitmap;
}
public void setThumbnailUri(Uri uri) {
if (uri.equals(thumbnailUri)) return;
thumbnailUri = uri;
if (thumbnailBitmap != null) {
thumbnailBitmap.recycle();
}
try {
thumbnailBitmap = MediaStore.Images.Media.getBitmap(ContactsManager.getInstance().getContentResolver(), thumbnailUri);
} catch (FileNotFoundException e) {
// Let's not say anything if the picture doesn't exist, it will pollute the logs
} catch (IOException e) {
Log.e(e);
}
}
public Uri getThumbnailUri() {
return thumbnailUri;
}
public Bitmap getThumbnailBitmap() {
return thumbnailBitmap;
}
public Bitmap getPhoto() {
if (photoBitmap != null) {
return photoBitmap;
} else if (thumbnailBitmap != null) {
return thumbnailBitmap;
}
return null;
}
public void setPhoto(byte[] photo) {
if (photo != null) {
if (isAndroidContact()) {
@ -419,6 +478,7 @@ public class LinphoneContact implements Serializable, Comparable<LinphoneContact
friend.edit();
friend.setFamilyName(lastName);
friend.setGivenName(firstName);
friend.setName(fullName);
for (LinphoneAddress address : friend.getAddresses()) {
friend.removeAddress(address);
@ -443,25 +503,21 @@ public class LinphoneContact implements Serializable, Comparable<LinphoneContact
friend.addPhoneNumber(noa.getValue());
}
}
if (friend.getAddress() != null) {
friend.setName(fullName);
}
friend.done();
if (friend.getAddress() != null) {
if (lc.findFriendByAddress(friend.getAddress().asString()) == null) {
try {
lc.addFriend(friend);
if (!ContactsManager.getInstance().hasContactsAccess()) {
// This refresh is only needed if app has no contacts permission to refresh the list of LinphoneFriends.
// Otherwise contacts will be refreshed due to changes in native contact and the handler in ContactsManager
ContactsManager.getInstance().fetchContactsAsync();
}
} catch (LinphoneCoreException e) {
Log.e(e);
}
if (!friend.isAlreadyPresentInFriendList()) {
try {
LinphoneManager.getLcIfManagerNotDestroyedOrNull().addFriend(friend);
} catch (LinphoneCoreException e) {
Log.e(e);
}
}
if (!ContactsManager.getInstance().hasContactsAccess()) {
// This refresh is only needed if app has no contacts permission to refresh the list of LinphoneFriends.
// Otherwise contacts will be refreshed due to changes in native contact and the handler in ContactsManager
ContactsManager.getInstance().fetchContactsAsync();
}
}
public void save() {

View file

@ -30,13 +30,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import org.linphone.compatibility.Compatibility;
import org.linphone.core.CallDirection;
import org.linphone.core.LinphoneAddress;
import org.linphone.core.LinphoneBuffer;
@ -77,7 +74,6 @@ import org.linphone.tools.OpenH264DownloadHelper;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
@ -87,10 +83,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.ConnectivityManager;
@ -105,10 +97,6 @@ import android.preference.CheckBoxPreference;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.telephony.TelephonyManager;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;
/**
@ -141,6 +129,7 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
private String basePath;
private static boolean sExited;
private boolean mAudioFocused;
private boolean echoTesterIsRunning;
private int mLastNetworkType=-1;
private ConnectivityManager mConnectivityManager;
private BroadcastReceiver mKeepAliveReceiver;
@ -164,6 +153,7 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
protected LinphoneManager(final Context c) {
sExited = false;
echoTesterIsRunning = false;
mServiceContext = c;
basePath = c.getFilesDir().getAbsolutePath();
mLPConfigXsd = basePath + "/lpconfig.xsd";
@ -217,8 +207,8 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
mCodecDownloader = LinphoneCoreFactory.instance().createOpenH264DownloadHelper();
mCodecListener = new OpenH264DownloadHelperListener() {
ProgressDialog progress;
int box = 1;
int ctxt = 0;
int box = 1;
@Override
public void OnProgress(final int current, final int max) {
@ -240,8 +230,10 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
progress.dismiss();
progress = null;
LinphoneManager.getLc().reloadMsPlugins(null);
if (ohcodec.getUserDataSize() > box && ohcodec.getUserData(box) != null)
((CheckBoxPreference)ohcodec.getUserData(box)).setSummary(mCodecDownloader.getLicenseMessage());
if (ohcodec.getUserDataSize() > box && ohcodec.getUserData(box) != null) {
((CheckBoxPreference) ohcodec.getUserData(box)).setSummary(mCodecDownloader.getLicenseMessage());
((CheckBoxPreference) ohcodec.getUserData(box)).setTitle("OpenH264");
}
}
}
});
@ -688,6 +680,32 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
Log.e(e, "Cannot start linphone");
}
}
private void initPushNotificationsService() {
try {
Class<?> GCMRegistrar = Class.forName("com.google.android.gcm.GCMRegistrar");
GCMRegistrar.getMethod("checkDevice", Context.class).invoke(null, mServiceContext);
try {
GCMRegistrar.getMethod("checkManifest", Context.class).invoke(null, mServiceContext);
} catch (IllegalStateException e) {
Log.e("[Push Notification] No receiver found", e);
}
final String regId = (String)GCMRegistrar.getMethod("getRegistrationId", Context.class).invoke(null, mServiceContext);
String newPushSenderID = mServiceContext.getString(R.string.push_sender_id);
String currentPushSenderID = LinphonePreferences.instance().getPushNotificationRegistrationID();
if (regId.equals("") || currentPushSenderID == null || !currentPushSenderID.equals(newPushSenderID)) {
GCMRegistrar.getMethod("register", Context.class, String[].class).invoke(null, mServiceContext, new String[]{newPushSenderID});
Log.i("[Push Notification] Storing current sender id = " + newPushSenderID);
} else {
Log.i("[Push Notification] Already registered with id = " + regId);
LinphonePreferences.instance().setPushNotificationRegistrationID(regId);
}
} catch (java.lang.UnsupportedOperationException e) {
Log.i("[Push Notification] Not activated");
} catch (Exception e1) {
Log.i("[Push Notification] Assuming GCM jar is not provided.");
}
}
private synchronized void initLiblinphone(LinphoneCore lc) throws LinphoneCoreException {
mLc = lc;
@ -742,7 +760,7 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
mLc.migrateCallLogs();
if (mServiceContext.getResources().getBoolean(R.bool.enable_push_id)) {
Compatibility.initPushNotificationService(mServiceContext);
initPushNotificationsService();
}
/*
@ -1020,7 +1038,16 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
}
return null;
}
public void setAudioManagerInCallMode() {
if (mAudioManager.getMode() == AudioManager.MODE_IN_COMMUNICATION) {
Log.w("[AudioManager] already in MODE_IN_COMMUNICATION, skipping...");
return;
}
Log.d("[AudioManager] Mode: MODE_IN_COMMUNICATION");
mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
}
@SuppressLint("Wakelock")
public void callState(final LinphoneCore lc,final LinphoneCall call, final State state, final String message) {
Log.i("New call state [",state,"]");
@ -1056,7 +1083,7 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
if (state == State.Connected) {
if (mLc.getCallsNb() == 1) {
requestAudioFocus();
Compatibility.setAudioManagerInCallMode(mAudioManager);
setAudioManagerInCallMode();
}
if (Hacks.needSoftvolume()) {
@ -1066,7 +1093,7 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
}
if (state == State.OutgoingEarlyMedia) {
Compatibility.setAudioManagerInCallMode(mAudioManager);
setAudioManagerInCallMode();
}
if (state == State.CallReleased || state == State.Error) {
@ -1146,7 +1173,7 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
public void startEcCalibration(LinphoneCoreListener l) throws LinphoneCoreException {
routeAudioToSpeaker();
Compatibility.setAudioManagerInCallMode((AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE));
setAudioManagerInCallMode();
Log.i("Set audio mode on 'Voice Communication'");
int oldVolume = mAudioManager.getStreamVolume(STREAM_VOICE_CALL);
int maxVolume = mAudioManager.getStreamMaxVolume(STREAM_VOICE_CALL);
@ -1155,6 +1182,42 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
mAudioManager.setStreamVolume(STREAM_VOICE_CALL, oldVolume, 0);
}
public int startEchoTester() throws LinphoneCoreException {
routeAudioToSpeaker();
setAudioManagerInCallMode();
Log.i("Set audio mode on 'Voice Communication'");
int oldVolume = mAudioManager.getStreamVolume(STREAM_VOICE_CALL);
int maxVolume = mAudioManager.getStreamMaxVolume(STREAM_VOICE_CALL);
int sampleRate = 0;
mAudioManager.setStreamVolume(STREAM_VOICE_CALL, maxVolume, 0);
String sampleRateProperty = mAudioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
sampleRate = Integer.parseInt(sampleRateProperty);
int status = mLc.startEchoTester(sampleRate);
if (status > 0)
echoTesterIsRunning = true;
else {
echoTesterIsRunning = false;
routeAudioToReceiver();
mAudioManager.setStreamVolume(STREAM_VOICE_CALL, oldVolume, 0);
((AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE)).setMode(AudioManager.MODE_NORMAL);
Log.i("Set audio mode on 'Normal'");
}
return status;
}
public int stopEchoTester() throws LinphoneCoreException {
echoTesterIsRunning = false;
int status = mLc.stopEchoTester();
routeAudioToReceiver();
((AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE)).setMode(AudioManager.MODE_NORMAL);
Log.i("Set audio mode on 'Normal'");
return status;
}
public boolean getEchoTesterStatus() {
return echoTesterIsRunning;
}
private boolean isRinging;
private void requestAudioFocus(){
@ -1328,88 +1391,6 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag
mAudioManager.adjustStreamVolume(LINPHONE_VOLUME_STREAM, i < 0 ? AudioManager.ADJUST_LOWER : AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI);
}
public static Boolean isProximitySensorNearby(final SensorEvent event) {
float threshold = 4.001f; // <= 4 cm is near
final float distanceInCm = event.values[0];
final float maxDistance = event.sensor.getMaximumRange();
Log.d("Proximity sensor report [",distanceInCm,"] , for max range [",maxDistance,"]");
if (maxDistance <= threshold) {
// Case binary 0/1 and short sensors
threshold = maxDistance;
}
return distanceInCm < threshold;
}
private static boolean sLastProximitySensorValueNearby;
private static Set<Activity> sProximityDependentActivities = new HashSet<Activity>();
private static SensorEventListener sProximitySensorListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
if (event.timestamp == 0) return; //just ignoring for nexus 1
sLastProximitySensorValueNearby = isProximitySensorNearby(event);
proximityNearbyChanged();
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
};
private static void simulateProximitySensorNearby(Activity activity, boolean nearby) {
final Window window = activity.getWindow();
WindowManager.LayoutParams params = window.getAttributes();
View view = ((ViewGroup) window.getDecorView().findViewById(android.R.id.content)).getChildAt(0);
if (nearby) {
params.screenBrightness = 0.1f;
view.setVisibility(View.INVISIBLE);
Compatibility.hideNavigationBar(activity);
} else {
params.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
view.setVisibility(View.VISIBLE);
Compatibility.showNavigationBar(activity);
}
window.setAttributes(params);
}
private static void proximityNearbyChanged() {
boolean nearby = sLastProximitySensorValueNearby;
for (Activity activity : sProximityDependentActivities) {
simulateProximitySensorNearby(activity, nearby);
}
}
public static synchronized void startProximitySensorForActivity(Activity activity) {
if (sProximityDependentActivities.contains(activity)) {
Log.i("proximity sensor already active for " + activity.getLocalClassName());
return;
}
if (sProximityDependentActivities.isEmpty()) {
SensorManager sm = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE);
Sensor s = sm.getDefaultSensor(Sensor.TYPE_PROXIMITY);
if (s != null) {
sm.registerListener(sProximitySensorListener,s,SensorManager.SENSOR_DELAY_UI);
Log.i("Proximity sensor detected, registering");
}
} else if (sLastProximitySensorValueNearby){
simulateProximitySensorNearby(activity, true);
}
sProximityDependentActivities.add(activity);
}
public static synchronized void stopProximitySensorForActivity(Activity activity) {
sProximityDependentActivities.remove(activity);
simulateProximitySensorNearby(activity, false);
if (sProximityDependentActivities.isEmpty()) {
SensorManager sm = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE);
sm.unregisterListener(sProximitySensorListener);
sLastProximitySensorValueNearby = false;
}
}
public static synchronized LinphoneCore getLcIfManagerNotDestroyedOrNull() {
if (sExited || instance == null) {
// Can occur if the UI thread play a posted event but in the meantime the LinphoneManager was destroyed

View file

@ -431,7 +431,7 @@ public final class LinphoneUtils {
StringBuilder sb = new StringBuilder();
try {
p = Runtime.getRuntime().exec(new String[] { "logcat", "-d", "|", "grep", "`adb shell ps | grep org.linphone | cut -c10-15`" });
p = Runtime.getRuntime().exec(new String[] { "logcat", "-d", "|", "grep", "`adb shell ps | grep " + context.getPackageName() + " | cut -c10-15`" });
br = new BufferedReader(new InputStreamReader(p.getInputStream()), 2048);
String line;

View file

@ -1,6 +0,0 @@
package org.linphone;
public class OpenGLESDisplay {
public static native void init(int ptr, int width, int height);
public static native void render(int ptr);
}

View file

@ -48,6 +48,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.preference.CheckBoxPreference;
@ -58,6 +59,7 @@ import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
import android.provider.Settings;
/**
* @author Sylvain Berfini
@ -611,10 +613,52 @@ public class SettingsFragment extends PreferencesListFragment {
return true;
}
});
findPreference(getString(R.string.pref_echo_tester_key)).setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
synchronized (SettingsFragment.this) {
int recordAudio = getActivity().getPackageManager().checkPermission(Manifest.permission.RECORD_AUDIO, getActivity().getPackageName());
if (recordAudio == PackageManager.PERMISSION_GRANTED) {
if (LinphoneManager.getInstance().getEchoTesterStatus())
stopEchoTester();
else
startEchoTester();
} else {
LinphoneActivity.instance().checkAndRequestRecordAudioPermissionsForEchoTester();
}
}
return true;
}
});
}
public void startEchoTester() {
Preference preference = findPreference(getString(R.string.pref_echo_tester_key));
try {
if (LinphoneManager.getInstance().startEchoTester() > 0) {
preference.setSummary("Is running");
}
} catch (LinphoneCoreException e) {
e.printStackTrace();
}
}
public void stopEchoTester() {
Preference preference = findPreference(getString(R.string.pref_echo_tester_key));
try {
if (LinphoneManager.getInstance().stopEchoTester() > 0) {
preference.setSummary("Is stopped");
}
} catch (LinphoneCoreException e) {
e.printStackTrace();
}
}
public void startEchoCancellerCalibration() {
try {
if (LinphoneManager.getInstance().getEchoTesterStatus())
stopEchoTester();
LinphoneManager.getInstance().startEcCalibration(mListener);
} catch (LinphoneCoreException e) {
Log.e(e);
@ -642,7 +686,9 @@ public class SettingsFragment extends PreferencesListFragment {
codecs.removeAll();
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
final OpenH264DownloadHelper mCodecDownloader = LinphoneManager.getInstance().getOpenH264DownloadHelper();
for (final PayloadType pt : lc.getVideoCodecs()) {
final CheckBoxPreference codec = new CheckBoxPreference(getActivity());
codec.setTitle(pt.getMime());
@ -659,8 +705,10 @@ public class SettingsFragment extends PreferencesListFragment {
}
}
}
if (pt.getMime().equals("H264") && mCodecDownloader.isCodecFound())
if (pt.getMime().equals("H264") && mCodecDownloader.isCodecFound()) {
codec.setSummary(mCodecDownloader.getLicenseMessage());
codec.setTitle("OpenH264");
}
codec.setChecked(lc.isPayloadTypeEnabled(pt));
codec.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@ -670,9 +718,9 @@ public class SettingsFragment extends PreferencesListFragment {
try {
if (enable && Version.getCpuAbis().contains("armeabi-v7a") && !Version.getCpuAbis().contains("x86")
&& pt.getMime().equals("H264") && !mCodecDownloader.isCodecFound()) {
LinphoneManager.getInstance().getOpenH264DownloadHelper().setOpenH264HelperListener(LinphoneManager.getInstance().getOpenH264HelperListener());
LinphoneManager.getInstance().getOpenH264DownloadHelper().setUserData(0,LinphoneManager.getInstance().getContext());
LinphoneManager.getInstance().getOpenH264DownloadHelper().setUserData(1,codec);
mCodecDownloader.setOpenH264HelperListener(LinphoneManager.getInstance().getOpenH264HelperListener());
mCodecDownloader.setUserData(0,LinphoneManager.getInstance().getContext());
mCodecDownloader.setUserData(1,codec);
AlertDialog.Builder builder = new AlertDialog.Builder(LinphoneManager.getInstance().getContext());
builder.setCancelable(false);
@ -702,7 +750,6 @@ public class SettingsFragment extends PreferencesListFragment {
codecs.addPreference(codec);
}
((CheckBoxPreference) findPreference(getString(R.string.pref_video_enable_key))).setChecked(mPrefs.isVideoEnabled());
((CheckBoxPreference) findPreference(getString(R.string.pref_video_use_front_camera_key))).setChecked(mPrefs.useFrontCam());
((CheckBoxPreference) findPreference(getString(R.string.pref_video_initiate_call_with_video_key))).setChecked(mPrefs.shouldInitiateVideoCall());
@ -1146,6 +1193,24 @@ public class SettingsFragment extends PreferencesListFragment {
return true;
}
});
findPreference(getString(R.string.pref_android_app_settings_key)).setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
synchronized (SettingsFragment.this) {
Context context = SettingsFragment.this.getActivity();
Intent i = new Intent();
i.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
i.addCategory(Intent.CATEGORY_DEFAULT);
i.setData(Uri.parse("package:" + context.getPackageName()));
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
i.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
context.startActivity(i);
}
return true;
}
});
findPreference(getString(R.string.pref_display_name_key)).setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
@ -1182,9 +1247,11 @@ public class SettingsFragment extends PreferencesListFragment {
}
}
@Override
public void onPause() {
if (LinphoneManager.getInstance().getEchoTesterStatus())
stopEchoTester();
LinphoneActivity.instance().hideTopBar();
super.onPause();
}

View file

@ -478,7 +478,10 @@ public class StatusFragment extends Fragment {
PayloadType payloadAudio = params.getUsedAudioCodec();
PayloadType payloadVideo = params.getUsedVideoCodec();
if (payloadVideo != null && payloadAudio != null) {
codec.setText(payloadVideo.getMime() + " / " + payloadAudio.getMime() + (payloadAudio.getRate() / 1000));
String videoMime = payloadVideo.getMime();
if (payloadVideo.getMime().equals("H264") && LinphoneManager.getInstance().getOpenH264DownloadHelper().isCodecFound())
videoMime = "OpenH264";
codec.setText(videoMime + " / " + payloadAudio.getMime() + (payloadAudio.getRate() / 1000));
}
dl.setText(String.valueOf((int) videoStats.getDownloadBandwidth()) + " / " + (int) audioStats.getDownloadBandwidth() + " kbits/s");
ul.setText(String.valueOf((int) videoStats.getUploadBandwidth()) + " / " + (int) audioStats.getUploadBandwidth() + " kbits/s");

View file

@ -19,6 +19,13 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.core.LinphoneCoreException;
import org.linphone.core.OpenH264DownloadHelperListener;
import org.linphone.core.PayloadType;
import org.linphone.tools.OpenH264DownloadHelper;
import android.app.Fragment;
import android.os.Bundle;
import android.os.Handler;
@ -29,15 +36,6 @@ import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import org.linphone.LinphoneActivity;
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.core.LinphoneCoreFactory;
import org.linphone.core.OpenH264DownloadHelperListener;
import org.linphone.core.LinphoneCoreException;
import org.linphone.core.PayloadType;
import org.linphone.tools.OpenH264DownloadHelper;
/**
* @author Erwan CROZE
*/

View file

@ -18,11 +18,11 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import org.linphone.R;
import org.linphone.compatibility.Compatibility;
import android.app.Fragment;
import android.os.Bundle;
import android.text.Editable;
import android.text.Html;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
@ -53,7 +53,7 @@ public class LinphoneLoginFragment extends Fragment implements OnClickListener,
password = (EditText) view.findViewById(R.id.assistant_password);
password.addTextChangedListener(this);
forgotPassword = (TextView) view.findViewById(R.id.forgot_password);
forgotPassword.setText(Html.fromHtml("<a href=\"" + url + "\"'>"+ getString(R.string.forgot_password) + "</a>"));
forgotPassword.setText(Compatibility.fromHtml("<a href=\"" + url + "\"'>"+ getString(R.string.forgot_password) + "</a>"));
forgotPassword.setMovementMethod(LinkMovementMethod.getInstance());
displayName = (EditText) view.findViewById(R.id.assistant_display_name);
apply = (Button) view.findViewById(R.id.assistant_apply);

View file

@ -1,68 +0,0 @@
package org.linphone.compatibility;
import org.linphone.LinphonePreferences;
import org.linphone.R;
import org.linphone.mediastream.Log;
import android.annotation.TargetApi;
import android.content.Context;
import android.media.AudioManager;
/*
ApiEightPlus.java
Copyright (C) 2012 Belledonne Communications, Grenoble, France
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 2
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/**
* @author Sylvain Berfini
*/
@TargetApi(8)
public class ApiEightPlus {
public static void initPushNotificationService(Context context) {
try {
Class<?> GCMRegistrar = Class.forName("com.google.android.gcm.GCMRegistrar");
// Starting the push notification service
GCMRegistrar.getMethod("checkDevice", Context.class).invoke(null, context);
try {
GCMRegistrar.getMethod("checkManifest", Context.class).invoke(null, context);
} catch (IllegalStateException e){
Log.e("Push notification: No receiver found",e);
}
final String regId = (String)GCMRegistrar.getMethod("getRegistrationId", Context.class).invoke(null, context);
String newPushSenderID = context.getString(R.string.push_sender_id);
String currentPushSenderID = LinphonePreferences.instance().getPushNotificationRegistrationID();
if (regId.equals("") || currentPushSenderID == null || !currentPushSenderID.equals(newPushSenderID)) {
GCMRegistrar.getMethod("register", Context.class, String[].class).invoke(null, context, new String[]{newPushSenderID});
Log.d("Push Notification: storing current sender id = " + newPushSenderID);
} else {
Log.d("Push Notification: already registered with id = " + regId);
LinphonePreferences.instance().setPushNotificationRegistrationID(regId);
}
} catch (java.lang.UnsupportedOperationException e) {
Log.i("Push Notification: not activated");
} catch (Exception e1) {
//assume the jar is not provided
Log.i("Push Notification: assuming GCM jar is not provided.");
}
}
@SuppressWarnings("deprecation")
public static String getAudioManagerEventForBluetoothConnectionStateChangedEvent() {
return AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED;
}
}

View file

@ -3,19 +3,15 @@ package org.linphone.compatibility;
import java.util.ArrayList;
import org.linphone.R;
import org.linphone.mediastream.Log;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.SipAddress;
@ -119,21 +115,6 @@ public class ApiElevenPlus {
return notif;
}
public static void copyTextToClipboard(Context context, String msg) {
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = android.content.ClipData.newPlainText("Message", msg);
clipboard.setPrimaryClip(clip);
}
public static void setAudioManagerInCallMode(AudioManager manager) {
if (manager.getMode() == AudioManager.MODE_IN_COMMUNICATION) {
Log.w("---AudioManager: already in MODE_IN_COMMUNICATION, skipping...");
return;
}
Log.d("---AudioManager: set mode to MODE_IN_COMMUNICATION");
manager.setMode(AudioManager.MODE_IN_COMMUNICATION);
}
public static Intent prepareAddContactIntent(String displayName, String sipUri) {
Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);

View file

@ -1,406 +0,0 @@
package org.linphone.compatibility;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.linphone.LinphoneContact;
import org.linphone.R;
import org.linphone.core.LinphoneAddress;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.media.AudioManager;
import android.net.Uri;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.support.v4.app.NotificationCompat;
import android.text.ClipboardManager;
import android.text.TextUtils;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
/*
ApiFivePlus.java
Copyright (C) 2012 Belledonne Communications, Grenoble, France
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 2
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/**
* @author Sylvain Berfini
*/
@SuppressWarnings("deprecation")
@TargetApi(5)
public class ApiFivePlus {
public static void overridePendingTransition(Activity activity, int idAnimIn, int idAnimOut) {
activity.overridePendingTransition(idAnimIn, idAnimOut);
}
public static Intent prepareAddContactIntent(String displayName, String sipUri) {
Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
intent.putExtra(ContactsContract.Intents.Insert.NAME, displayName);
// VoIP field not available, we store the address in the IM field
intent.putExtra(ContactsContract.Intents.Insert.IM_HANDLE, sipUri);
intent.putExtra(ContactsContract.Intents.Insert.IM_PROTOCOL, "sip");
return intent;
}
public static Intent prepareEditContactIntent(int id) {
Intent intent = new Intent(Intent.ACTION_EDIT, Contacts.CONTENT_URI);
Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id);
intent.setData(contactUri);
return intent;
}
public static Intent prepareEditContactIntentWithSipAddress(int id, String sipUri) {
Intent intent = new Intent(Intent.ACTION_EDIT, Contacts.CONTENT_URI);
Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id);
intent.setData(contactUri);
// VoIP field not available, we store the address in the IM field
intent.putExtra(ContactsContract.Intents.Insert.IM_HANDLE, sipUri);
intent.putExtra(ContactsContract.Intents.Insert.IM_PROTOCOL, "sip");
return intent;
}
public static List<String> extractContactNumbersAndAddresses(String id, ContentResolver cr) {
List<String> list = new ArrayList<String>();
Uri uri = Data.CONTENT_URI;
String[] projection = {ContactsContract.CommonDataKinds.Im.DATA};
// IM addresses
String selection = new StringBuilder()
.append(Data.CONTACT_ID).append(" = ? AND ")
.append(Data.MIMETYPE).append(" = '")
.append(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
.append("' AND lower(")
.append(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL)
.append(") = 'sip'")
.toString();
Cursor c = cr.query(uri, projection, selection, new String[]{id}, null);
if (c != null) {
int nbId = c.getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA);
while (c.moveToNext()) {
list.add("sip:" + c.getString(nbId));
}
c.close();
}
// Phone Numbers
c = cr.query(Phone.CONTENT_URI, new String[]{Phone.NUMBER}, Phone.CONTACT_ID + " = " + id, null, null);
if (c != null) {
while (c.moveToNext()) {
String number = c.getString(c.getColumnIndex(Phone.NUMBER));
list.add(number);
}
c.close();
}
return list;
}
public static Cursor getContactsCursor(ContentResolver cr, List<String> ids) {
String req = Data.MIMETYPE + " = '" + CommonDataKinds.Phone.CONTENT_ITEM_TYPE
+ "' AND " + CommonDataKinds.Phone.NUMBER + " IS NOT NULL";
req += " OR (" + Contacts.Data.MIMETYPE + " = '" + CommonDataKinds.Im.CONTENT_ITEM_TYPE
+ "' AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip')";
if(ids != null){
String s = TextUtils.join(",", ids);
req += " OR (" + Data.CONTACT_ID + " IN (" + s + "))";
}
return getGeneralContactCursor(cr, req, true);
}
public static Cursor getSIPContactsCursor(ContentResolver cr, List<String> ids) {
String req = null;
req = Contacts.Data.MIMETYPE + " = '" + CommonDataKinds.Im.CONTENT_ITEM_TYPE
+ "' AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip'";
if(ids != null){
String s = TextUtils.join(",", ids);
req += " OR (" + Data.CONTACT_ID + " IN (" + s + "))";
}
return getGeneralContactCursor(cr, req, true);
}
private static Cursor getSIPContactCursor(ContentResolver cr, String id) {
String req = null;
req = Contacts.Data.MIMETYPE + " = '" + CommonDataKinds.Im.CONTENT_ITEM_TYPE
+ " AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip' AND "
+ android.provider.ContactsContract.CommonDataKinds.Im.DATA + " LIKE '" + id + "'";
return getGeneralContactCursor(cr, req, false);
}
public static Cursor getGeneralContactCursor(ContentResolver cr, String select, boolean shouldGroupBy) {
String[] projection = new String[] { Data.CONTACT_ID, Data.DISPLAY_NAME };
String query;
query = Data.DISPLAY_NAME + " IS NOT NULL AND (" + select + ")";
Cursor cursor = cr.query(Data.CONTENT_URI, projection, query, null, " lower(" + Data.DISPLAY_NAME + ") COLLATE UNICODE ASC");
if (!shouldGroupBy || cursor == null) {
return cursor;
}
MatrixCursor result = new MatrixCursor(cursor.getColumnNames());
Set<String> groupBy = new HashSet<String>();
while (cursor.moveToNext()) {
String name = cursor.getString(getCursorDisplayNameColumnIndex(cursor));
if (!groupBy.contains(name)) {
groupBy.add(name);
Object[] newRow = new Object[cursor.getColumnCount()];
int contactID = cursor.getColumnIndex(Data.CONTACT_ID);
int displayName = cursor.getColumnIndex(Data.DISPLAY_NAME);
newRow[contactID] = cursor.getString(contactID);
newRow[displayName] = cursor.getString(displayName);
result.addRow(newRow);
}
}
cursor.close();
return result;
}
public static int getCursorDisplayNameColumnIndex(Cursor cursor) {
return cursor.getColumnIndex(Data.DISPLAY_NAME);
}
public static LinphoneContact getContact(ContentResolver cr, Cursor cursor, int position) {
try {
if(cursor != null) {
cursor.moveToFirst();
boolean success = cursor.move(position);
if (!success)
return null;
String id = cursor.getString(cursor.getColumnIndex(Data.CONTACT_ID));
String name = getContactDisplayName(cursor);
Uri thumbnail = getContactPictureUri(id);
Uri photo = getContactPhotoUri(id);
InputStream input = getContactPictureInputStream(cr, id);
LinphoneContact contact = new LinphoneContact();
contact.setAndroidId(id);
contact.setFullName(name);
if (input != null) {
contact.setPhotoUri(photo);
contact.setThumbnailUri(thumbnail);
}
return contact;
} else {
return null;
}
} catch (Exception e) {
}
return null;
}
public static InputStream getContactPictureInputStream(ContentResolver cr, String id) {
Uri person = ContentUris.withAppendedId(Contacts.CONTENT_URI, Long.parseLong(id));
return Contacts.openContactPhotoInputStream(cr, person);
}
private static String getContactDisplayName(Cursor cursor) {
return cursor.getString(cursor.getColumnIndex(Data.DISPLAY_NAME));
}
private static Uri getContactPictureUri(String id) {
Uri person = ContentUris.withAppendedId(Contacts.CONTENT_URI, Long.parseLong(id));
return Uri.withAppendedPath(person, Contacts.Photo.CONTENT_DIRECTORY);
}
private static Uri getContactPhotoUri(String id) {
Uri person = ContentUris.withAppendedId(Contacts.CONTENT_URI, Long.parseLong(id));
return Uri.withAppendedPath(person, Contacts.Photo.DISPLAY_PHOTO);
}
public static Uri findUriPictureOfContactAndSetDisplayName(LinphoneAddress address, ContentResolver cr) {
String username = address.getUserName();
String domain = address.getDomain();
String sipUri = username + "@" + domain;
Cursor cursor = getSIPContactCursor(cr, sipUri);
if(cursor != null) {
LinphoneContact contact = getContact(cr, cursor, 0);
if (contact != null && contact.getNumbersOrAddresses().contains(sipUri)) {
address.setDisplayName(contact.getFullName());
cursor.close();
return contact.getPhotoUri();
}
cursor.close();
}
return null;
}
public static String refreshContactName(ContentResolver cr, String id) {
Cursor cursor = getGeneralContactCursor(cr, Data.CONTACT_ID + " = '" + id + "'", false);
if (cursor != null && cursor.moveToFirst()) {
String contactDisplayName = getContactDisplayName(cursor);
cursor.close();
return contactDisplayName;
}
return null;
}
public static Notification createMessageNotification(Context context, String title, String msg, PendingIntent intent) {
Notification notif = new Notification();
notif.icon = R.drawable.topbar_chat_notification;
notif.iconLevel = 0;
notif.when = System.currentTimeMillis();
notif.flags &= Notification.FLAG_ONGOING_EVENT;
notif.defaults |= Notification.DEFAULT_VIBRATE;
notif.defaults |= Notification.DEFAULT_SOUND;
notif.defaults |= Notification.DEFAULT_LIGHTS;
return notif;
}
public static Notification createInCallNotification(Context context, String title, String msg, int iconID, PendingIntent intent) {
NotificationCompat.Builder notifBuilder = new NotificationCompat.Builder(context)
.setSmallIcon(iconID)
.setContentTitle(title)
.setContentText(msg)
.setContentIntent(intent);
return notifBuilder.build();
}
public static void setPreferenceChecked(Preference preference, boolean checked) {
((CheckBoxPreference) preference).setChecked(checked);
}
public static boolean isPreferenceChecked(Preference preference) {
return ((CheckBoxPreference) preference).isChecked();
}
public static void copyTextToClipboard(Context context, String msg) {
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText(msg);
}
public static void addSipAddressToContact(Context context, ArrayList<ContentProviderOperation> ops, String sipAddress) {
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Im.DATA, sipAddress)
.withValue(ContactsContract.CommonDataKinds.Im.TYPE, ContactsContract.CommonDataKinds.Im.TYPE_CUSTOM)
.withValue(ContactsContract.CommonDataKinds.Im.LABEL, context.getString(R.string.addressbook_label))
.build()
);
}
public static void addSipAddressToContact(Context context, ArrayList<ContentProviderOperation> ops, String sipAddress, String rawContactID) {
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactID)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Im.DATA, sipAddress)
.withValue(ContactsContract.CommonDataKinds.Im.TYPE, ContactsContract.CommonDataKinds.Im.TYPE_CUSTOM)
.withValue(ContactsContract.CommonDataKinds.Im.LABEL, context.getString(R.string.addressbook_label))
.build()
);
}
public static void updateSipAddressForContact(ArrayList<ContentProviderOperation> ops, String oldSipAddress, String newSipAddress, String contactID) {
String select = ContactsContract.Data.CONTACT_ID + "=? AND "
+ ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE + "' AND "
+ ContactsContract.CommonDataKinds.Im.DATA + "=?";
String[] args = new String[] { String.valueOf(contactID), oldSipAddress };
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(select, args)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Im.DATA, newSipAddress)
.build()
);
}
public static void deleteSipAddressFromContact(ArrayList<ContentProviderOperation> ops, String oldSipAddress, String contactID) {
String select = ContactsContract.Data.CONTACT_ID + "=? AND "
+ ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE + "' AND "
+ ContactsContract.CommonDataKinds.Im.DATA + "=?";
String[] args = new String[] { String.valueOf(contactID), oldSipAddress };
ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
.withSelection(select, args)
.build()
);
}
public static void removeGlobalLayoutListener(ViewTreeObserver viewTreeObserver, OnGlobalLayoutListener keyboardListener) {
viewTreeObserver.removeGlobalOnLayoutListener(keyboardListener);
}
public static void setAudioManagerInCallMode(AudioManager manager) {
/* Do not use MODE_IN_CALL, because it is reserved to GSM. This is causing conflicts on audio system resulting in silenced audio.*/
//manager.setMode(AudioManager.MODE_IN_CALL);
}
public static Notification createNotification(Context context, String title, String message, int icon, int level, PendingIntent intent, boolean isOngoingEvent) {
NotificationCompat.Builder notifBuilder = new NotificationCompat.Builder(context)
.setSmallIcon(icon, level)
.setContentTitle(title)
.setContentText(message)
.setContentIntent(intent);
return notifBuilder.build();
}
public static Notification createSimpleNotification(Context context, String title, String text, PendingIntent intent) {
NotificationCompat.Builder notifBuilder = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.linphone_logo)
.setContentTitle(title)
.setContentText(text)
.setContentIntent(intent);
Notification notif = notifBuilder.build();
notif.defaults |= Notification.DEFAULT_VIBRATE;
notif.defaults |= Notification.DEFAULT_SOUND;
notif.defaults |= Notification.DEFAULT_LIGHTS;
return notif;
}
}

View file

@ -1,53 +0,0 @@
package org.linphone.compatibility;
import android.annotation.TargetApi;
import android.app.Activity;
import android.media.AudioManager;
import android.preference.Preference;
import android.preference.TwoStatePreference;
import android.view.View;
/*
ApiFourteenPlus.java
Copyright (C) 2012 Belledonne Communications, Grenoble, France
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 2
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/**
* @author Sylvain Berfini
*/
@TargetApi(14)
public class ApiFourteenPlus {
public static void setPreferenceChecked(Preference preference, boolean checked) {
((TwoStatePreference) preference).setChecked(checked);
}
public static boolean isPreferenceChecked(Preference preference) {
return ((TwoStatePreference) preference).isChecked();
}
public static void hideNavigationBar(Activity activity) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
}
public static void showNavigationBar(Activity activity) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
public static String getAudioManagerEventForBluetoothConnectionStateChangedEvent() {
return AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED;
}
}

View file

@ -1,195 +0,0 @@
package org.linphone.compatibility;
import java.util.ArrayList;
import java.util.List;
import org.linphone.LinphoneContact;
import org.linphone.R;
import org.linphone.core.LinphoneAddress;
import android.annotation.TargetApi;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.text.TextUtils;
/*
ApiNinePlus.java
Copyright (C) 2012 Belledonne Communications, Grenoble, France
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 2
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/**
* @author Sylvain Berfini
*/
@TargetApi(9)
public class ApiNinePlus {
public static void addSipAddressToContact(Context context, ArrayList<ContentProviderOperation> ops, String sipAddress) {
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.SipAddress.DATA, sipAddress)
.withValue(CommonDataKinds.SipAddress.TYPE, CommonDataKinds.SipAddress.TYPE_CUSTOM)
.withValue(CommonDataKinds.SipAddress.LABEL, context.getString(R.string.addressbook_label))
.build()
);
}
public static void addSipAddressToContact(Context context, ArrayList<ContentProviderOperation> ops, String sipAddress, String rawContactID) {
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactID)
.withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.SipAddress.DATA, sipAddress)
.withValue(CommonDataKinds.SipAddress.TYPE, CommonDataKinds.SipAddress.TYPE_CUSTOM)
.withValue(CommonDataKinds.SipAddress.LABEL, context.getString(R.string.addressbook_label))
.build()
);
}
public static void updateSipAddressForContact(ArrayList<ContentProviderOperation> ops, String oldSipAddress, String newSipAddress, String contactID) {
String select = ContactsContract.Data.CONTACT_ID + "=? AND "
+ ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "' AND "
+ ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + "=?";
String[] args = new String[] { String.valueOf(contactID), oldSipAddress };
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(select, args)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS, newSipAddress)
.build()
);
}
public static void deleteSipAddressFromContact(ArrayList<ContentProviderOperation> ops, String oldSipAddress, String contactID) {
String select = ContactsContract.Data.CONTACT_ID + "=? AND "
+ ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "' AND "
+ ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + "=? ";
String[] args = new String[] { String.valueOf(contactID), oldSipAddress };
ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
.withSelection(select, args)
.build()
);
}
public static List<String> extractContactNumbersAndAddresses(String id, ContentResolver cr) {
List<String> list = new ArrayList<String>();
Uri uri = Data.CONTENT_URI;
String[] projection;
// SIP addresses
String selection2 = new StringBuilder()
.append(Data.CONTACT_ID)
.append(" = ? AND ")
.append(Data.MIMETYPE)
.append(" = '")
.append(ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
.append("'")
.toString();
projection = new String[] {ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS};
Cursor c = cr.query(uri, projection, selection2, new String[]{id}, null);
if (c != null) {
int nbid = c.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS);
while (c.moveToNext()) {
list.add("sip:" + c.getString(nbid));
}
c.close();
}
// Phone Numbers
c = cr.query(Phone.CONTENT_URI, new String[] { Phone.NUMBER }, Phone.CONTACT_ID + " = " + id, null, null);
if (c != null) {
while (c.moveToNext()) {
String number = c.getString(c.getColumnIndex(Phone.NUMBER));
list.add(number);
}
c.close();
}
return list;
}
public static Cursor getContactsCursor(ContentResolver cr, String search, List<String> ids) {
String req;
if(ids != null && ids.size() > 0) {
req = "(" + Data.MIMETYPE + " = '" + CommonDataKinds.Phone.CONTENT_ITEM_TYPE
+ "' AND " + CommonDataKinds.Phone.NUMBER + " IS NOT NULL "
+ " OR (" + Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
+ "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " IS NOT NULL)"
+ " OR (" + Data.CONTACT_ID + " IN (" + TextUtils.join(" , ", ids) + ")))";
} else {
req = "(" + Data.MIMETYPE + " = '" + CommonDataKinds.Phone.CONTENT_ITEM_TYPE
+ "' AND " + CommonDataKinds.Phone.NUMBER + " IS NOT NULL "
+ " OR (" + Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
+ "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " IS NOT NULL))";
}
if (search != null) {
req += " AND " + Data.DISPLAY_NAME + " LIKE '%" + search + "%'";
}
return ApiFivePlus.getGeneralContactCursor(cr, req, true);
}
public static Cursor getSIPContactsCursor(ContentResolver cr, String search, List<String> ids) {
String req = "(" + Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
+ "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " IS NOT NULL) ";
if(ids != null && ids.size() > 0) {
req += " OR (" + Data.CONTACT_ID + " IN (" + TextUtils.join(" , ", ids) + "))";
}
if (search != null) {
req += " AND " + Data.DISPLAY_NAME + " LIKE '%" + search + "%'";
}
return ApiFivePlus.getGeneralContactCursor(cr, req, true);
}
private static Cursor getSIPContactCursor(ContentResolver cr, String id) {
String req = null;
req = Contacts.Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
+ "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " LIKE '" + id + "'";
return ApiFivePlus.getGeneralContactCursor(cr, req, false);
}
public static Uri findUriPictureOfContactAndSetDisplayName(LinphoneAddress address, ContentResolver cr) {
String username = address.getUserName();
String domain = address.getDomain();
String sipUri = username + "@" + domain;
Cursor cursor = getSIPContactCursor(cr, sipUri);
LinphoneContact contact = ApiFivePlus.getContact(cr, cursor, 0);
if (contact != null && contact.getNumbersOrAddresses().contains(sipUri)) {
address.setDisplayName(contact.getFullName());
cursor.close();
return contact.getPhotoUri();
}
cursor.close();
return null;
}
}

View file

@ -33,6 +33,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@TargetApi(16)
public class ApiSixteenPlus {
@SuppressWarnings("deprecation")
public static Notification createMessageNotification(Context context,
int msgCount, String msgSender, String msg, Bitmap contactIcon,
PendingIntent intent) {

View file

@ -33,6 +33,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
@TargetApi(21)
public class ApiTwentyOnePlus {
@SuppressWarnings("deprecation")
public static Notification createMessageNotification(Context context,
int msgCount, String msgSender, String msg, Bitmap contactIcon,
PendingIntent intent) {

View file

@ -17,191 +17,54 @@ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.linphone.LinphoneContact;
import org.linphone.core.LinphoneAddress;
import org.linphone.mediastream.Version;
import android.app.Activity;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.net.Uri;
import android.os.PowerManager;
import android.preference.Preference;
import android.provider.Settings;
import android.text.Html;
import android.text.Spanned;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
/**
* @author Sylvain Berfini
*/
public class Compatibility {
public static void overridePendingTransition(Activity activity, int idAnimIn, int idAnimOut) {
if (Version.sdkAboveOrEqual(Version.API05_ECLAIR_20)) {
ApiFivePlus.overridePendingTransition(activity, idAnimIn, idAnimOut);
}
}
public static Intent prepareAddContactIntent(String displayName, String sipUri) {
if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
return ApiElevenPlus.prepareAddContactIntent(displayName, sipUri);
} else {
return ApiFivePlus.prepareAddContactIntent(displayName, sipUri);
}
}
public static Intent prepareEditContactIntent(int id) {
if (Version.sdkAboveOrEqual(Version.API05_ECLAIR_20)) {
return ApiFivePlus.prepareEditContactIntent(id);
}
return null;
}
public static Intent prepareEditContactIntentWithSipAddress(int id, String sipAddress) {
if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
return ApiElevenPlus.prepareEditContactIntentWithSipAddress(id, sipAddress);
} else {
return ApiFivePlus.prepareEditContactIntent(id);
}
}
public static List<String> extractContactNumbersAndAddresses(String id, ContentResolver cr) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
return ApiNinePlus.extractContactNumbersAndAddresses(id, cr);
} else {
return ApiFivePlus.extractContactNumbersAndAddresses(id, cr);
}
}
public static List<String> extractContactImAddresses(String id, ContentResolver cr) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
return ApiFivePlus.extractContactNumbersAndAddresses(id, cr);
} else {
return null;
}
}
public static Cursor getContactsCursor(ContentResolver cr, List<String> contactsId) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
return ApiNinePlus.getContactsCursor(cr, null, contactsId);
} else {
return ApiFivePlus.getContactsCursor(cr, contactsId);
}
}
public static Cursor getContactsCursor(ContentResolver cr, String search, List<String> contactsId) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
return ApiNinePlus.getContactsCursor(cr, search, contactsId);
} else {
return ApiFivePlus.getContactsCursor(cr, contactsId);
}
}
public static Cursor getSIPContactsCursor(ContentResolver cr, List<String> contactsId) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
return ApiNinePlus.getSIPContactsCursor(cr, null, contactsId);
} else {
return ApiFivePlus.getSIPContactsCursor(cr, contactsId);
}
}
public static Cursor getSIPContactsCursor(ContentResolver cr, String search, List<String> contactsId) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
return ApiNinePlus.getSIPContactsCursor(cr, search, contactsId);
} else {
return ApiFivePlus.getSIPContactsCursor(cr, contactsId);
}
}
public static Cursor getImContactsCursor(ContentResolver cr) {
return ApiFivePlus.getSIPContactsCursor(cr,null);
}
public static int getCursorDisplayNameColumnIndex(Cursor cursor) {
if (Version.sdkAboveOrEqual(Version.API05_ECLAIR_20)) {
return ApiFivePlus.getCursorDisplayNameColumnIndex(cursor);
}
return -1;
}
public static LinphoneContact getContact(ContentResolver cr, Cursor cursor, int position) {
if (Version.sdkAboveOrEqual(Version.API05_ECLAIR_20)) {
return ApiFivePlus.getContact(cr, cursor, position);
}
return null;
}
public static InputStream getContactPictureInputStream(ContentResolver cr, String id) {
if (Version.sdkAboveOrEqual(Version.API05_ECLAIR_20)) {
return ApiFivePlus.getContactPictureInputStream(cr, id);
}
return null;
}
public static Uri findUriPictureOfContactAndSetDisplayName(LinphoneAddress address, ContentResolver cr) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
return ApiNinePlus.findUriPictureOfContactAndSetDisplayName(address, cr);
} else {
return ApiFivePlus.findUriPictureOfContactAndSetDisplayName(address, cr);
}
}
public static Notification createSimpleNotification(Context context, String title, String text, PendingIntent intent) {
Notification notif = null;
if (Version.sdkAboveOrEqual(Version.API21_LOLLIPOP_50)) {
return ApiTwentyOnePlus.createSimpleNotification(context, title, text, intent);
} else if (Version.sdkAboveOrEqual(Version.API16_JELLY_BEAN_41)) {
notif = ApiSixteenPlus.createSimpleNotification(context, title, text, intent);
} else if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
notif = ApiElevenPlus.createSimpleNotification(context, title, text, intent);
} else {
notif = ApiFivePlus.createSimpleNotification(context, title, text, intent);
notif = ApiElevenPlus.createSimpleNotification(context, title, text, intent);
}
return notif;
}
public static Notification createMessageNotification(Context context, int msgCount, String msgSender, String msg, Bitmap contactIcon, PendingIntent intent) {
Notification notif = null;
String title;
if (msgCount == 1) {
title = "Unread message from %s".replace("%s", msgSender);
} else {
title = "%i unread messages".replace("%i", String.valueOf(msgCount));
}
Notification notif = null;
if (Version.sdkAboveOrEqual(Version.API21_LOLLIPOP_50)) {
return ApiTwentyOnePlus.createMessageNotification(context, msgCount, msgSender, msg, contactIcon, intent);
} else if (Version.sdkAboveOrEqual(Version.API16_JELLY_BEAN_41)) {
notif = ApiSixteenPlus.createMessageNotification(context, msgCount, msgSender, msg, contactIcon, intent);
} else if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
notif = ApiElevenPlus.createMessageNotification(context, msgCount, msgSender, msg, contactIcon, intent);
} else {
notif = ApiFivePlus.createMessageNotification(context, title, msg, intent);
notif = ApiElevenPlus.createMessageNotification(context, msgCount, msgSender, msg, contactIcon, intent);
}
return notif;
}
public static Notification createInCallNotification(Context context, String title, String msg, int iconID, Bitmap contactIcon, String contactName, PendingIntent intent) {
Notification notif = null;
if (Version.sdkAboveOrEqual(Version.API21_LOLLIPOP_50)) {
return ApiTwentyOnePlus.createInCallNotification(context, title, msg, iconID, contactIcon, contactName, intent);
} else if (Version.sdkAboveOrEqual(Version.API16_JELLY_BEAN_41)) {
notif = ApiSixteenPlus.createInCallNotification(context, title, msg, iconID, contactIcon, contactName, intent);
} else if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
notif = ApiElevenPlus.createInCallNotification(context, title, msg, iconID, contactIcon, contactName, intent);
} else {
notif = ApiFivePlus.createInCallNotification(context, title, msg, iconID, intent);
notif = ApiElevenPlus.createInCallNotification(context, title, msg, iconID, contactIcon, contactName, intent);
}
return notif;
}
@ -211,20 +74,11 @@ public class Compatibility {
return ApiTwentyOnePlus.createNotification(context, title, message, icon, iconLevel, largeIcon, intent, isOngoingEvent,priority);
} else if (Version.sdkAboveOrEqual(Version.API16_JELLY_BEAN_41)) {
return ApiSixteenPlus.createNotification(context, title, message, icon, iconLevel, largeIcon, intent, isOngoingEvent,priority);
} else if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
return ApiElevenPlus.createNotification(context, title, message, icon, iconLevel, largeIcon, intent, isOngoingEvent);
} else {
return ApiFivePlus.createNotification(context, title, message, icon, iconLevel, intent, isOngoingEvent);
return ApiElevenPlus.createNotification(context, title, message, icon, iconLevel, largeIcon, intent, isOngoingEvent);
}
}
public static String refreshContactName(ContentResolver cr, String id) {
if (Version.sdkAboveOrEqual(Version.API05_ECLAIR_20)) {
return ApiFivePlus.refreshContactName(cr, id);
}
return null;
}
public static CompatibilityScaleGestureDetector getScaleGestureDetector(Context context, CompatibilityScaleGestureListener listener) {
if (Version.sdkAboveOrEqual(Version.API08_FROYO_22)) {
CompatibilityScaleGestureDetector csgd = new CompatibilityScaleGestureDetector(context);
@ -233,109 +87,13 @@ public class Compatibility {
}
return null;
}
public static void setPreferenceChecked(Preference preference, boolean checked) {
if (Version.sdkAboveOrEqual(Version.API14_ICE_CREAM_SANDWICH_40)) {
ApiFourteenPlus.setPreferenceChecked(preference, checked);
} else {
ApiFivePlus.setPreferenceChecked(preference, checked);
}
}
public static boolean isPreferenceChecked(Preference preference) {
if (Version.sdkAboveOrEqual(Version.API14_ICE_CREAM_SANDWICH_40)) {
return ApiFourteenPlus.isPreferenceChecked(preference);
} else {
return ApiFivePlus.isPreferenceChecked(preference);
}
}
public static void initPushNotificationService(Context context) {
if (Version.sdkAboveOrEqual(Version.API08_FROYO_22)) {
ApiEightPlus.initPushNotificationService(context);
}
}
public static void copyTextToClipboard(Context context, String msg) {
if(Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
ApiElevenPlus.copyTextToClipboard(context, msg);
} else {
ApiFivePlus.copyTextToClipboard(context, msg);
}
}
public static void addSipAddressToContact(Context context, ArrayList<ContentProviderOperation> ops, String sipAddress) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
ApiNinePlus.addSipAddressToContact(context, ops, sipAddress);
} else {
ApiFivePlus.addSipAddressToContact(context, ops, sipAddress);
}
}
public static void addSipAddressToContact(Context context, ArrayList<ContentProviderOperation> ops, String sipAddress, String rawContactID) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
ApiNinePlus.addSipAddressToContact(context, ops, sipAddress, rawContactID);
} else {
ApiFivePlus.addSipAddressToContact(context, ops, sipAddress, rawContactID);
}
}
public static void updateSipAddressForContact(ArrayList<ContentProviderOperation> ops, String oldSipAddress, String newSipAddress, String contactID) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
ApiNinePlus.updateSipAddressForContact(ops, oldSipAddress, newSipAddress, contactID);
} else {
ApiFivePlus.updateSipAddressForContact(ops, oldSipAddress, newSipAddress, contactID);
}
}
public static void deleteSipAddressFromContact(ArrayList<ContentProviderOperation> ops, String oldSipAddress, String contactID) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
ApiNinePlus.deleteSipAddressFromContact(ops, oldSipAddress, contactID);
} else {
ApiFivePlus.deleteSipAddressFromContact(ops, oldSipAddress, contactID);
}
}
public static void deleteImAddressFromContact(ArrayList<ContentProviderOperation> ops, String oldSipAddress, String contactID) {
ApiFivePlus.deleteSipAddressFromContact(ops, oldSipAddress, contactID);
}
@SuppressWarnings("deprecation")
public static void removeGlobalLayoutListener(ViewTreeObserver viewTreeObserver, OnGlobalLayoutListener keyboardListener) {
if (Version.sdkAboveOrEqual(Version.API16_JELLY_BEAN_41)) {
ApiSixteenPlus.removeGlobalLayoutListener(viewTreeObserver, keyboardListener);
} else {
ApiFivePlus.removeGlobalLayoutListener(viewTreeObserver, keyboardListener);
}
}
public static void hideNavigationBar(Activity activity)
{
if (Version.sdkAboveOrEqual(Version.API14_ICE_CREAM_SANDWICH_40)) {
ApiFourteenPlus.hideNavigationBar(activity);
}
}
public static void showNavigationBar(Activity activity)
{
if (Version.sdkAboveOrEqual(Version.API14_ICE_CREAM_SANDWICH_40)) {
ApiFourteenPlus.showNavigationBar(activity);
}
}
public static void setAudioManagerInCallMode(AudioManager manager) {
if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
ApiElevenPlus.setAudioManagerInCallMode(manager);
} else {
ApiFivePlus.setAudioManagerInCallMode(manager);
}
}
public static String getAudioManagerEventForBluetoothConnectionStateChangedEvent() {
if (Version.sdkAboveOrEqual(Version.API14_ICE_CREAM_SANDWICH_40)) {
return ApiFourteenPlus.getAudioManagerEventForBluetoothConnectionStateChangedEvent();
} else {
return ApiEightPlus.getAudioManagerEventForBluetoothConnectionStateChangedEvent();
viewTreeObserver.removeGlobalOnLayoutListener(keyboardListener);
}
}
@ -354,4 +112,12 @@ public class Compatibility {
return pm.isScreenOn();
}
}
@SuppressWarnings("deprecation")
public static Spanned fromHtml(String text) {
/*if (Version.sdkAboveOrEqual(Version.API24_NOUGAT_70)) {
return Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY);
}*/
return Html.fromHtml(text);
}
}

View file

@ -53,13 +53,13 @@ public class GCMService extends GCMBaseIntentService {
@Override
protected void onError(Context context, String errorId) {
initLogger(context);
Log.e("Error while registering push notification : " + errorId);
Log.e("[Push Notification] Error while registering: " + errorId);
}
@Override
protected void onMessage(Context context, Intent intent) {
initLogger(context);
Log.d("Push notification received");
Log.d("[Push Notification] Received");
if (!LinphoneService.isReady()) {
startService(new Intent(ACTION_MAIN).setClass(this, LinphoneService.class));
@ -79,7 +79,7 @@ public class GCMService extends GCMBaseIntentService {
@Override
protected void onRegistered(Context context, String regId) {
initLogger(context);
Log.d("Registered push notification : " + regId);
Log.d("[Push Notification] Registered: " + regId);
LinphonePreferences.instance().setPushNotificationRegistrationID(regId);
}
@ -87,7 +87,7 @@ public class GCMService extends GCMBaseIntentService {
@Override
protected void onUnregistered(Context context, String regId) {
initLogger(context);
Log.w("Unregistered push notification : " + regId);
Log.w("[Push Notification] Unregistered: " + regId);
LinphonePreferences.instance().setPushNotificationRegistrationID(null);
}

View file

@ -162,8 +162,7 @@ public class TutorialCardDavSync extends Activity implements OnClickListener, Li
}
@Override
public void onLinphoneFriendSyncStatusChanged(LinphoneFriendList list,
org.linphone.core.LinphoneFriendList.State status, String message) {
public void onLinphoneFriendSyncStatusChanged(LinphoneFriendList list, LinphoneFriendList.State status, String message) {
// TODO Auto-generated method stub
String msg = "Sync status changed: " + status.toString() + " (" + message + ")";
myLog(msg);

View file

@ -1,463 +0,0 @@
package org.linphone.ui;
/*
BubbleChat.java
Copyright (C) 2012 Belledonne Communications, Grenoble, France
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 2
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map.Entry;
import org.linphone.LinphoneActivity;
import org.linphone.LinphoneContact;
import org.linphone.LinphoneManager;
import org.linphone.LinphoneUtils;
import org.linphone.R;
import org.linphone.core.LinphoneBuffer;
import org.linphone.core.LinphoneChatMessage;
import org.linphone.core.LinphoneChatMessage.State;
import org.linphone.core.LinphoneContent;
import org.linphone.mediastream.Log;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.Html;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.style.ImageSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
/**
* @author Sylvain Berfini
*/
@SuppressLint("SimpleDateFormat")
public class BubbleChat implements LinphoneChatMessage.LinphoneChatMessageListener {
private static final HashMap<String, Integer> emoticons = new HashMap<String, Integer>();
private View view;
private ImageView statusView, contactPicture;
private LinphoneChatMessage nativeMessage;
private Context mContext;
private Button cancelUpload, acceptDownload;
private static final int SIZE_MAX = 512;
private ProgressBar progressBar, inprogress;
private Bitmap defaultBitmap;
@SuppressLint("InflateParams")
public BubbleChat(final Context context, LinphoneChatMessage message, LinphoneContact c) {
if (message == null) {
return;
}
nativeMessage = message;
mContext = context;
if (message.isOutgoing()) {
view = LayoutInflater.from(context).inflate(R.layout.chat_bubble_outgoing, null);
} else {
view = LayoutInflater.from(context).inflate(R.layout.chat_bubble_incoming, null);
}
view.setId(message.getStorageId());
defaultBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.chat_picture_over);
inprogress = (ProgressBar) view.findViewById(R.id.inprogress);
progressBar = (ProgressBar) view.findViewById(R.id.progress_bar);
LinphoneChatMessage.State status = message.getStatus();
statusView = (ImageView) view.findViewById(R.id.status);
if (statusView != null) {
if (status == LinphoneChatMessage.State.Delivered) {
statusView.setVisibility(View.INVISIBLE);
inprogress.setVisibility(View.GONE);
} else if (status == LinphoneChatMessage.State.NotDelivered) {
statusView.setVisibility(View.VISIBLE);
statusView.setImageResource(R.drawable.chat_message_not_delivered);
} else {
statusView.setVisibility(View.GONE);
inprogress.setVisibility(View.VISIBLE);
}
}
String externalBodyUrl = message.getExternalBodyUrl();
LinphoneContent fileTransferContent = message.getFileTransferInformation();
if(nativeMessage.isOutgoing()){
cancelUpload = (Button) view.findViewById(R.id.cancel_upload);
cancelUpload.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (LinphoneManager.getInstance().getMessageUploadPending() != null) {
progressBar.setVisibility(View.GONE);
progressBar.setProgress(0);
nativeMessage.cancelFileTransfer();
LinphoneManager.getInstance().setUploadPendingFileMessage(null);
}
}
});
}
if(LinphoneManager.getInstance().getMessageUploadPending() != null){
progressBar.setVisibility(View.VISIBLE);
LinphoneManager.addListener(this);
}
if (externalBodyUrl != null || fileTransferContent != null) {
String appData = message.getAppData();
ImageView imageView = (ImageView) view.findViewById(R.id.image);
if(nativeMessage.isOutgoing() && appData != null){
imageView.setVisibility(View.VISIBLE);
loadBitmap(appData, imageView);
RelativeLayout imageLayout = (RelativeLayout) view.findViewById(R.id.file_transfer_layout);
if(LinphoneManager.getInstance().getMessageUploadPending() != null && LinphoneManager.getInstance().getMessageUploadPending().getStorageId() == nativeMessage.getStorageId()){
inprogress.setVisibility(View.INVISIBLE);
imageLayout.setVisibility(View.VISIBLE);
nativeMessage.setListener(LinphoneManager.getInstance());
}
} else {
if (appData != null && !LinphoneManager.getInstance().isMessagePending(nativeMessage) &&
appData.contains(context.getString(R.string.temp_photo_name_with_date).split("%s")[0])) {
appData = null;
}
RelativeLayout imageLayout = (RelativeLayout) view.findViewById(R.id.file_transfer_layout);
acceptDownload = (Button) view.findViewById(R.id.accept_download);
if (appData == null) {
LinphoneManager.addListener(this);
imageLayout.setVisibility(View.VISIBLE);
acceptDownload.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mContext.getPackageManager().checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, mContext.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
v.setEnabled(false);
String extension = nativeMessage.getFileTransferInformation().getSubtype();
String filename = context.getString(R.string.temp_photo_name_with_date).replace("%s", String.valueOf(System.currentTimeMillis())) + "." + extension;
File file = new File(Environment.getExternalStorageDirectory(), filename);
nativeMessage.setAppData(filename);
LinphoneManager.getInstance().addDownloadMessagePending(nativeMessage);
nativeMessage.setListener(LinphoneManager.getInstance());
nativeMessage.setFileTransferFilepath(file.getPath());
nativeMessage.downloadFile();
} else {
Log.w("WRITE_EXTERNAL_STORAGE permission not granted, won't be able to store the downloaded file");
LinphoneActivity.instance().checkAndRequestExternalStoragePermission();
}
}
});
} else {
if (LinphoneManager.getInstance().isMessagePending(nativeMessage)) {
LinphoneManager.addListener(this);
acceptDownload.setEnabled(false);
imageLayout.setVisibility(View.VISIBLE);
} else {
LinphoneManager.removeListener(this);
imageLayout.setVisibility(View.GONE);
imageView.setVisibility(View.VISIBLE);
loadBitmap(appData, imageView);
}
}
}
} else {
TextView msgView = (TextView) view.findViewById(R.id.message);
if (msgView != null) {
Spanned text = null;
String msg = message.getText();
if (msg != null) {
text = getSmiledText(context, getTextWithHttpLinks(msg));
msgView.setText(text);
msgView.setMovementMethod(LinkMovementMethod.getInstance());
msgView.setVisibility(View.VISIBLE);
}
}
}
TextView contact = (TextView) view.findViewById(R.id.contact_header);
contactPicture = (ImageView) view.findViewById(R.id.contact_picture);
String displayName = nativeMessage.getFrom().getUserName();
if(!nativeMessage.isOutgoing()) {
if (c != null) {
displayName = c.getFullName();
LinphoneUtils.setImagePictureFromUri(view.getContext(), contactPicture, c.getPhotoUri(), c.getThumbnailUri());
} else {
contactPicture.setImageResource(R.drawable.avatar);
}
}
contact.setText(timestampToHumanDate(context, message.getTime()) + " - " + displayName);
}
public View getView() {
return view;
}
public void destroy() {
defaultBitmap.recycle();
}
private String timestampToHumanDate(Context context, long timestamp) {
try {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(timestamp);
SimpleDateFormat dateFormat;
if (isToday(cal)) {
dateFormat = new SimpleDateFormat(context.getResources().getString(R.string.today_date_format));
} else {
dateFormat = new SimpleDateFormat(context.getResources().getString(R.string.messages_date_format));
}
return dateFormat.format(cal.getTime());
} catch (NumberFormatException nfe) {
return String.valueOf(timestamp);
}
}
private boolean isToday(Calendar cal) {
return isSameDay(cal, Calendar.getInstance());
}
private boolean isSameDay(Calendar cal1, Calendar cal2) {
if (cal1 == null || cal2 == null) {
return false;
}
return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR));
}
public static Spannable getSmiledText(Context context, Spanned spanned) {
SpannableStringBuilder builder = new SpannableStringBuilder(spanned);
String text = spanned.toString();
for (Entry<String, Integer> entry : emoticons.entrySet()) {
String key = entry.getKey();
int indexOf = text.indexOf(key);
while (indexOf >= 0) {
int end = indexOf + key.length();
builder.setSpan(new ImageSpan(context, entry.getValue()), indexOf, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
indexOf = text.indexOf(key, end);
}
}
return builder;
}
public static Spanned getTextWithHttpLinks(String text) {
if (text.contains("<")) {
text = text.replace("<", "&lt;");
}
if (text.contains(">")) {
text = text.replace(">", "&gt;");
}
if (text.contains("http://")) {
int indexHttp = text.indexOf("http://");
int indexFinHttp = text.indexOf(" ", indexHttp) == -1 ? text.length() : text.indexOf(" ", indexHttp);
String link = text.substring(indexHttp, indexFinHttp);
String linkWithoutScheme = link.replace("http://", "");
text = text.replaceFirst(link, "<a href=\"" + link + "\">" + linkWithoutScheme + "</a>");
}
if (text.contains("https://")) {
int indexHttp = text.indexOf("https://");
int indexFinHttp = text.indexOf(" ", indexHttp) == -1 ? text.length() : text.indexOf(" ", indexHttp);
String link = text.substring(indexHttp, indexFinHttp);
String linkWithoutScheme = link.replace("https://", "");
text = text.replaceFirst(link, "<a href=\"" + link + "\">" + linkWithoutScheme + "</a>");
}
return Html.fromHtml(text);
}
public String getTextMessage() {
return nativeMessage.getText();
}
public State getStatus() {
return nativeMessage.getStatus();
}
public LinphoneChatMessage getNativeMessageObject() {
return nativeMessage;
}
public int getId() {
return nativeMessage.getStorageId();
}
public void loadBitmap(String path, ImageView imageView) {
if (cancelPotentialWork(path, imageView)) {
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
final AsyncBitmap asyncBitmap = new AsyncBitmap(mContext.getResources(), defaultBitmap, task);
imageView.setImageDrawable(asyncBitmap);
task.execute(path);
}
}
private class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
public String path;
public BitmapWorkerTask(ImageView imageView) {
path = null;
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
@Override
protected Bitmap doInBackground(String... params) {
path = params[0];
Bitmap bm = null;
if (path.startsWith("content")) {
try {
bm = MediaStore.Images.Media.getBitmap(mContext.getContentResolver(), Uri.parse(path));
} catch (FileNotFoundException e) {
Log.e(e);
} catch (IOException e) {
Log.e(e);
}
} else {
bm = BitmapFactory.decodeFile(path);
path = "file://" + path;
}
if (bm != null) {
bm = ThumbnailUtils.extractThumbnail(bm, SIZE_MAX, SIZE_MAX);
}
return bm;
}
// Once complete, see if ImageView is still around and set bitmap.
@Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask && imageView != null) {
imageView.setImageBitmap(bitmap);
imageView.setTag(path);
imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse((String)v.getTag()), "image/*");
mContext.startActivity(intent);
}
});
}
}
}
}
static class AsyncBitmap extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncBitmap(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
}
public static boolean cancelPotentialWork(String path, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
final String bitmapData = bitmapWorkerTask.path;
// If bitmapData is not yet set or it differs from the new data
if (bitmapData == null || bitmapData != path) {
// Cancel previous task
bitmapWorkerTask.cancel(true);
} else {
// The same work is already in progress
return false;
}
}
// No task associated with the ImageView, or an existing task was cancelled
return true;
}
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncBitmap) {
final AsyncBitmap asyncDrawable = (AsyncBitmap) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
@Override
public void onLinphoneChatMessageStateChanged(LinphoneChatMessage msg, State state) {
}
@Override
public void onLinphoneChatMessageFileTransferReceived(LinphoneChatMessage msg, LinphoneContent content, LinphoneBuffer buffer) {
}
@Override
public void onLinphoneChatMessageFileTransferSent(LinphoneChatMessage msg, LinphoneContent content, int offset, int size, LinphoneBuffer bufferToFill) {
}
@Override
public void onLinphoneChatMessageFileTransferProgressChanged(LinphoneChatMessage msg, LinphoneContent content, int offset, int total) {
if(nativeMessage.getStorageId() == msg.getStorageId())
progressBar.setProgress(offset * 100 / total);
}
}

@ -1 +1 @@
Subproject commit fbac73d6704f9f5697770188747639e519a28819
Subproject commit 921a21332c327ae99900267950be0bfd2d7c6126

@ -1 +1 @@
Subproject commit 086bb16bd79180265136ed9d4a89661049c95016
Subproject commit b553f58c3f23633b9c183af5784b2170b6b4aa91

@ -1 +1 @@
Subproject commit 29911ee5ba6053dd041ee02c6dc13b0134e890d2
Subproject commit f4eac4803eee43de3efa96ecca9450ba4f2bc7dc

@ -1 +1 @@
Subproject commit 61a3b8a98e6d35bff351b64e2c6717491792afb3
Subproject commit 958366ce1928bd0aabaf983082faa8c49e501c4a

View file

@ -40,7 +40,7 @@
</condition>
<exec executable="bash" unless:set="has.crashed">
<arg value="-c"/>
<arg value="adb shell run-as org.linphone cat /data/data/org.linphone/files/junit-report.xml > linphone-junit-report-${test.size}.xml"/>
<arg value="adb shell cat /data/data/org.linphone/files/junit-report.xml > linphone-junit-report-${test.size}.xml"/>
</exec>
<zip destfile="${archive.name}.zip">