diff --git a/.gitmodules b/.gitmodules index fa0235b31..6778bad1b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -38,9 +38,6 @@ [submodule "submodules/mssilk"] path = submodules/mssilk url = git://git.linphone.org/mssilk.git -[submodule "submodules/externals/vo-amrwbenc"] - path = submodules/externals/vo-amrwbenc - url = git://git.linphone.org/vo-amrwbenc.git [submodule "submodules/bcg729"] path = submodules/bcg729 url = git://git.linphone.org/bcg729.git @@ -104,3 +101,6 @@ [submodule "submodules/bcunit"] path = submodules/bcunit url = git://git.linphone.org/bcunit.git +[submodule "submodules/externals/vo-amrwbenc"] + path = submodules/externals/vo-amrwbenc + url = git://git.linphone.org/vo-amrwbenc.git diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 7d99c6672..909529b19 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -27,6 +27,7 @@ + @@ -43,6 +44,8 @@ + + @@ -75,7 +78,7 @@ - + @@ -94,7 +97,6 @@ @@ -104,7 +106,6 @@ diff --git a/custom_rules.xml b/custom_rules.xml index 8eb805335..45b003dc2 100644 --- a/custom_rules.xml +++ b/custom_rules.xml @@ -8,50 +8,6 @@ failonerror="false" /> - - Generate JNI header - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/prepare.py b/prepare.py index 1e2316165..468acddef 100755 --- a/prepare.py +++ b/prepare.py @@ -287,6 +287,12 @@ generate-mediastreamer2-apk: java-clean build copy-libs update-mediastreamer2-pr \techo "version.name=$(LINPHONE_ANDROID_VERSION)" > default.properties && \\ \tant debug +quick: +\tant clean +\tant debug +\tant installd +\tant run + install-apk: \tant installd @@ -328,7 +334,6 @@ run-all-tests: update-project \tant partial-clean \t$(MAKE) -C tests run-all-tests ANT_SILENT=$(ANT_SILENT) - pull-transifex: \ttx pull -af diff --git a/res/layout-sw533dp-land/assistant_linphone_login.xml b/res/layout-sw533dp-land/assistant_linphone_login.xml index 54239505d..8122c8ccd 100644 --- a/res/layout-sw533dp-land/assistant_linphone_login.xml +++ b/res/layout-sw533dp-land/assistant_linphone_login.xml @@ -78,6 +78,13 @@ android:layout_width="match_parent" android:layout_height="40dp" android:singleLine="true"/> + + diff --git a/res/values/non_localizable_custom.xml b/res/values/non_localizable_custom.xml index 4266988c7..437890f35 100644 --- a/res/values/non_localizable_custom.xml +++ b/res/values/non_localizable_custom.xml @@ -6,7 +6,7 @@ stun.linphone.org org.linphone - vnd.android.cursor.item/org.linphone.profile + vnd.android.cursor.item/org.linphone.profile false false @@ -68,10 +68,8 @@ linphone-android@belledonne-communications.com false - - linphone-android-photo-temp.jpg - linphone-android-photo-%s.jpg - + linphone-android-photo-temp + linphone-android-photo-%s false false diff --git a/res/values/non_localizable_strings.xml b/res/values/non_localizable_strings.xml index f204815ee..f3c4727f7 100644 --- a/res/values/non_localizable_strings.xml +++ b/res/values/non_localizable_strings.xml @@ -59,6 +59,7 @@ pref_wifi_only_key + pref_overlay_key pref_video_use_front_camera_key pref_video_codec_h263_key pref_video_codec_mpeg4_key @@ -71,6 +72,7 @@ pref_preferred_video_fps_key pref_bandwidth_limit_key pref_animation_enable_key + pref_service_notification_key pref_escape_plus_key pref_friendlist_subscribe_key pref_echo_cancellation_key @@ -197,4 +199,6 @@ Send logs Cancel + + pref_use_lime_encryption_key diff --git a/res/values/strings.xml b/res/values/strings.xml index f673d0845..f6ce8a7d5 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -20,7 +20,6 @@ dd/MM, HH:mm dd/MM HH:mm - linphone-mms-%s.jpg Username @@ -255,6 +254,8 @@ Video + Video overlay + Display call video in overlay when outside the application Use front camera Initiate video calls Always send video requests @@ -272,6 +273,15 @@ Send RFC2833 DTMFs Send SIP INFO DTMFs Voice mail URI + + + Chat + Sharing server + Do not edit unless you know what you are doing! + Use LIME encryption + Disabled + Mandatory + Preferred Network @@ -294,9 +304,9 @@ Debug Background mode Enable Animations + Enable service notification Start at boot time Incoming call hangup (in seconds) - Sharing server Remote provisioning Primary account Display name diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index 8eab25ca6..be34cd865 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -111,12 +111,17 @@ android:title="@string/pref_bandwidth_limit" android:key="@string/pref_bandwidth_limit_key" android:numeric="integer" /> + + - + @@ -136,6 +141,20 @@ android:key="@string/pref_voice_mail_key"/> + + + + + + + + @@ -202,6 +221,10 @@ android:title="@string/pref_background_mode" android:key="@string/pref_background_mode_key"/> + + @@ -215,10 +238,6 @@ android:key="@string/pref_incoming_call_timeout_key" android:layout="@layout/hidden"/> - - diff --git a/src/org/linphone/CallActivity.java b/src/org/linphone/CallActivity.java index afdbd8b60..321d0a80a 100644 --- a/src/org/linphone/CallActivity.java +++ b/src/org/linphone/CallActivity.java @@ -37,6 +37,7 @@ import org.linphone.ui.Numpad; import android.Manifest; import android.app.Activity; import android.app.Dialog; +import android.app.Fragment; import android.app.FragmentTransaction; import android.content.Context; import android.content.Intent; @@ -55,7 +56,6 @@ import android.os.CountDownTimer; import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; -import android.app.Fragment; import android.support.v4.app.ActivityCompat; import android.support.v4.widget.DrawerLayout; import android.view.Gravity; @@ -87,6 +87,7 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve private final static int SECONDS_BEFORE_DENYING_CALL_UPDATE = 30000; private static final int PERMISSIONS_REQUEST_CAMERA = 202; private static final int PERMISSIONS_ENABLED_CAMERA = 203; + private static final int PERMISSIONS_ENABLED_MIC = 204; private static CallActivity instance; @@ -180,18 +181,14 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve if (state == State.IncomingReceived) { startIncomingCallActivity(); return; - } - - if (state == State.Paused || state == State.PausedByRemote || state == State.Pausing) { + } else if (state == State.Paused || state == State.PausedByRemote || state == State.Pausing) { if(LinphoneManager.getLc().getCurrentCall() != null) { enabledVideoButton(false); } if(isVideoEnabled(call)){ showAudioView(); } - } - - if (state == State.Resuming) { + } else if (state == State.Resuming) { if(LinphonePreferences.instance().isVideoEnabled()){ status.refreshStatusItems(call, isVideoEnabled(call)); if(call.getCurrentParamsCopy().getVideoEnabled()){ @@ -201,9 +198,7 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve if(LinphoneManager.getLc().getCurrentCall() != null) { enabledVideoButton(true); } - } - - if (state == State.StreamsRunning) { + } else if (state == State.StreamsRunning) { switchVideo(isVideoEnabled(call)); enableAndRefreshInCallActions(); @@ -211,9 +206,7 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve videoProgress.setVisibility(View.GONE); status.refreshStatusItems(call, isVideoEnabled(call)); } - } - - if (state == State.CallUpdatedByRemote) { + } else if (state == State.CallUpdatedByRemote) { // If the correspondent proposes video while audio call boolean videoEnabled = LinphonePreferences.instance().isVideoEnabled(); if (!videoEnabled) { @@ -454,21 +447,29 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve } public void checkAndRequestPermission(String permission, int result) { - if (getPackageManager().checkPermission(permission, getPackageName()) != PackageManager.PERMISSION_GRANTED) { - if (!ActivityCompat.shouldShowRequestPermissionRationale(this,permission)){ - ActivityCompat.requestPermissions(this, new String[]{permission}, result); + int permissionGranted = getPackageManager().checkPermission(permission, getPackageName()); + Log.i("[Permission] " + permission + " is " + (permissionGranted == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + + if (permissionGranted != PackageManager.PERMISSION_GRANTED) { + if (LinphonePreferences.instance().firstTimeAskingForPermission(permission) || ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) { + Log.i("[Permission] Asking for " + permission); + ActivityCompat.requestPermissions(this, new String[] { permission }, result); } } } @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, String[] permissions, final int[] grantResults) { + for (int i = 0; i < permissions.length; i++) { + Log.i("[Permission] " + permissions[i] + " is " + (grantResults[i] == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + } + switch (requestCode) { case PERMISSIONS_REQUEST_CAMERA: UIThreadDispatcher.dispatch(new Runnable() { @Override public void run() { - acceptCallUpdate(true); + acceptCallUpdate(grantResults[0] == PackageManager.PERMISSION_GRANTED); } }); break; @@ -476,12 +477,21 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve UIThreadDispatcher.dispatch(new Runnable() { @Override public void run() { - enabledOrDisabledVideo(false); + disableVideo(grantResults[0] != PackageManager.PERMISSION_GRANTED); + } + }); + break; + case PERMISSIONS_ENABLED_MIC: + UIThreadDispatcher.dispatch(new Runnable() { + @Override + public void run() { + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + toggleMicro(); + } } }); break; } - LinphonePreferences.instance().neverAskCameraPerm(); } public void createInCallStats() { @@ -527,6 +537,9 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve video.setImageResource(R.drawable.camera_button); } } + if (getPackageManager().checkPermission(Manifest.permission.CAMERA, getPackageName()) != PackageManager.PERMISSION_GRANTED) { + video.setImageResource(R.drawable.camera_button); + } if (isSpeakerEnabled) { speaker.setImageResource(R.drawable.speaker_selected); @@ -534,6 +547,9 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve speaker.setImageResource(R.drawable.speaker_default); } + if (getPackageManager().checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName()) != PackageManager.PERMISSION_GRANTED) { + isMicMuted = true; + } if (isMicMuted) { micro.setImageResource(R.drawable.micro_selected); } else { @@ -613,14 +629,28 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve } if (id == R.id.video) { - if (getPackageManager().checkPermission(Manifest.permission.CAMERA, getPackageName()) == PackageManager.PERMISSION_GRANTED) { - enabledOrDisabledVideo(isVideoEnabled(LinphoneManager.getLc().getCurrentCall())); + int camera = getPackageManager().checkPermission(Manifest.permission.CAMERA, getPackageName()); + Log.i("[Permission] Camera permission is " + (camera == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + + if (camera == PackageManager.PERMISSION_GRANTED) { + disableVideo(isVideoEnabled(LinphoneManager.getLc().getCurrentCall())); } else { - checkAndRequestPermission(Manifest.permission.CAMERA, PERMISSIONS_ENABLED_CAMERA); + if (LinphonePreferences.instance().firstTimeAskingForPermission(Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) { + checkAndRequestPermission(Manifest.permission.CAMERA, PERMISSIONS_ENABLED_CAMERA); + } } } else if (id == R.id.micro) { - toggleMicro(); + int recordAudio = getPackageManager().checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName()); + Log.i("[Permission] Record audio permission is " + (recordAudio == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + + if (recordAudio == PackageManager.PERMISSION_GRANTED) { + toggleMicro(); + } else { + if (LinphonePreferences.instance().firstTimeAskingForPermission(Manifest.permission.RECORD_AUDIO) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) { + checkAndRequestPermission(Manifest.permission.RECORD_AUDIO, PERMISSIONS_ENABLED_MIC); + } + } } else if (id == R.id.speaker) { toggleSpeaker(); @@ -727,13 +757,13 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve } } - private void enabledOrDisabledVideo(final boolean isVideoEnabled) { + private void disableVideo(final boolean videoDisabled) { final LinphoneCall call = LinphoneManager.getLc().getCurrentCall(); if (call == null) { return; } - if (isVideoEnabled) { + if (videoDisabled) { LinphoneCallParams params = LinphoneManager.getLc().createCallParams(call); params.setVideoEnabled(false); LinphoneManager.getLc().updateCall(call, params); @@ -1387,19 +1417,24 @@ public class CallActivity extends Activity implements OnClickListener, SensorEve delete.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { - if (getPackageManager().checkPermission(Manifest.permission.CAMERA, getPackageName()) == PackageManager.PERMISSION_GRANTED || LinphonePreferences.instance().cameraPermAsked()) { + int camera = getPackageManager().checkPermission(Manifest.permission.CAMERA, getPackageName()); + Log.i("[Permission] Camera permission is " + (camera == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + + if (camera == PackageManager.PERMISSION_GRANTED) { CallActivity.instance().acceptCallUpdate(true); } else { - checkAndRequestPermission(Manifest.permission.CAMERA, PERMISSIONS_REQUEST_CAMERA); + if (LinphonePreferences.instance().firstTimeAskingForPermission(Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(CallActivity.this, Manifest.permission.CAMERA)) { + checkAndRequestPermission(Manifest.permission.CAMERA, PERMISSIONS_REQUEST_CAMERA); + } else { + CallActivity.instance().acceptCallUpdate(false); + } } dialog.dismiss(); } }); - cancel.setOnClickListener(new - - OnClickListener() { + cancel.setOnClickListener(new OnClickListener() { @Override public void onClick (View view){ if (CallActivity.isInstanciated()) { diff --git a/src/org/linphone/CallIncomingActivity.java b/src/org/linphone/CallIncomingActivity.java index 2e55230c4..e33b052ad 100644 --- a/src/org/linphone/CallIncomingActivity.java +++ b/src/org/linphone/CallIncomingActivity.java @@ -18,6 +18,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone; +import java.util.ArrayList; import java.util.List; import org.linphone.core.LinphoneAddress; @@ -29,12 +30,15 @@ import org.linphone.core.LinphoneCoreListenerBase; import org.linphone.mediastream.Log; import org.linphone.ui.LinphoneSliders.LinphoneSliderTriggered; +import android.Manifest; import android.app.Activity; import android.content.Context; import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.os.PowerManager; +import android.support.v4.app.ActivityCompat; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -45,7 +49,6 @@ import android.widget.TextView; import android.widget.Toast; public class CallIncomingActivity extends Activity implements LinphoneSliderTriggered { - private static CallIncomingActivity instance; private TextView name, number; @@ -186,9 +189,6 @@ public class CallIncomingActivity extends Activity implements LinphoneSliderTrig } }); - - - mListener = new LinphoneCoreListenerBase(){ @Override public void callState(LinphoneCore lc, LinphoneCall call, State state, String message) { @@ -202,7 +202,6 @@ public class CallIncomingActivity extends Activity implements LinphoneSliderTrig } }; - super.onCreate(savedInstanceState); instance = this; } @@ -234,6 +233,8 @@ public class CallIncomingActivity extends Activity implements LinphoneSliderTrig finish(); return; } + + LinphoneAddress address = mCall.getRemoteAddress(); LinphoneContact contact = ContactsManager.getInstance().findContactFromAddress(address); if (contact != null) { @@ -244,6 +245,12 @@ public class CallIncomingActivity extends Activity implements LinphoneSliderTrig } number.setText(address.asStringUriOnly()); } + + @Override + protected void onStart() { + super.onStart(); + checkAndRequestCallPermissions(); + } @Override protected void onPause() { @@ -320,4 +327,41 @@ public class CallIncomingActivity extends Activity implements LinphoneSliderTrig public void onRightHandleTriggered() { } + + private void checkAndRequestCallPermissions() { + ArrayList permissionsList = new ArrayList(); + + int recordAudio = getPackageManager().checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName()); + Log.i("[Permission] Record audio permission is " + (recordAudio == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + int camera = getPackageManager().checkPermission(Manifest.permission.CAMERA, getPackageName()); + Log.i("[Permission] Camera permission is " + (camera == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + + if (recordAudio != PackageManager.PERMISSION_GRANTED) { + if (LinphonePreferences.instance().firstTimeAskingForPermission(Manifest.permission.RECORD_AUDIO) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) { + Log.i("[Permission] Asking for record audio"); + permissionsList.add(Manifest.permission.RECORD_AUDIO); + } + } + if (LinphonePreferences.instance().shouldInitiateVideoCall() || LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests()) { + if (camera != PackageManager.PERMISSION_GRANTED) { + if (LinphonePreferences.instance().firstTimeAskingForPermission(Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) { + Log.i("[Permission] Asking for camera"); + permissionsList.add(Manifest.permission.CAMERA); + } + } + } + + if (permissionsList.size() > 0) { + String[] permissions = new String[permissionsList.size()]; + permissions = permissionsList.toArray(permissions); + ActivityCompat.requestPermissions(this, permissions, 0); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + for (int i = 0; i < permissions.length; i++) { + Log.i("[Permission] " + permissions[i] + " is " + (grantResults[i] == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + } + } } \ No newline at end of file diff --git a/src/org/linphone/CallOutgoingActivity.java b/src/org/linphone/CallOutgoingActivity.java index 62a107206..b3ad9b00c 100644 --- a/src/org/linphone/CallOutgoingActivity.java +++ b/src/org/linphone/CallOutgoingActivity.java @@ -18,6 +18,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone; +import java.util.ArrayList; import java.util.List; import org.linphone.core.LinphoneAddress; @@ -26,20 +27,27 @@ import org.linphone.core.LinphoneCall.State; import org.linphone.core.LinphoneCallParams; import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCoreListenerBase; +import org.linphone.core.Reason; import org.linphone.mediastream.Log; +import android.Manifest; import android.app.Activity; import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; import android.os.Bundle; +import android.support.v4.app.ActivityCompat; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; -import android.view.KeyEvent; +import android.view.ViewGroup; import android.view.WindowManager; import android.widget.ImageView; import android.widget.TextView; +import android.widget.Toast; public class CallOutgoingActivity extends Activity implements OnClickListener{ - private static CallOutgoingActivity instance; private TextView name, number; @@ -88,16 +96,8 @@ public class CallOutgoingActivity extends Activity implements OnClickListener{ mListener = new LinphoneCoreListenerBase(){ @Override - public void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State state, String message) { - if (LinphoneManager.getLc().getCallsNb() == 0) { - finish(); - return; - } - if (call == mCall && State.CallEnd == state) { - finish(); - } - - if (call == mCall && (State.Connected == state)){ + public void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State state, String message) { + if (call == mCall && State.Connected == state) { if (!LinphoneActivity.isInstanciated()) { return; } @@ -109,6 +109,22 @@ public class CallOutgoingActivity extends Activity implements OnClickListener{ } finish(); return; + } else if (state == State.Error) { + // Convert LinphoneCore message for internalization + if (message != null && call.getErrorInfo().getReason() == Reason.Declined) { + displayCustomToast(getString(R.string.error_call_declined), Toast.LENGTH_SHORT); + } else if (message != null && call.getErrorInfo().getReason() == Reason.NotFound) { + displayCustomToast(getString(R.string.error_user_not_found), Toast.LENGTH_SHORT); + } else if (message != null && call.getErrorInfo().getReason() == Reason.Media) { + displayCustomToast(getString(R.string.error_incompatible_media), Toast.LENGTH_SHORT); + } else if (message != null) { + displayCustomToast(getString(R.string.error_unknown) + " - " + message, Toast.LENGTH_SHORT); + } + } + + if (LinphoneManager.getLc().getCallsNb() == 0) { + finish(); + return; } } }; @@ -152,6 +168,12 @@ public class CallOutgoingActivity extends Activity implements OnClickListener{ } number.setText(address.asStringUriOnly()); } + + @Override + protected void onStart() { + super.onStart(); + checkAndRequestCallPermissions(); + } @Override protected void onPause() { @@ -204,7 +226,58 @@ public class CallOutgoingActivity extends Activity implements OnClickListener{ return super.onKeyDown(keyCode, event); } + public void displayCustomToast(final String message, final int duration) { + LayoutInflater inflater = getLayoutInflater(); + View layout = inflater.inflate(R.layout.toast, (ViewGroup) findViewById(R.id.toastRoot)); + + TextView toastText = (TextView) layout.findViewById(R.id.toastMessage); + toastText.setText(message); + + final Toast toast = new Toast(getApplicationContext()); + toast.setGravity(Gravity.CENTER, 0, 0); + toast.setDuration(duration); + toast.setView(layout); + toast.show(); + } + private void decline() { LinphoneManager.getLc().terminateCall(mCall); } + + private void checkAndRequestCallPermissions() { + ArrayList permissionsList = new ArrayList(); + + int recordAudio = getPackageManager().checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName()); + Log.i("[Permission] Record audio permission is " + (recordAudio == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + int camera = getPackageManager().checkPermission(Manifest.permission.CAMERA, getPackageName()); + Log.i("[Permission] Camera permission is " + (camera == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + + if (recordAudio != PackageManager.PERMISSION_GRANTED) { + if (LinphonePreferences.instance().firstTimeAskingForPermission(Manifest.permission.RECORD_AUDIO) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) { + Log.i("[Permission] Asking for record audio"); + permissionsList.add(Manifest.permission.RECORD_AUDIO); + } + } + if (LinphonePreferences.instance().shouldInitiateVideoCall() || LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests()) { + if (camera != PackageManager.PERMISSION_GRANTED) { + if (LinphonePreferences.instance().firstTimeAskingForPermission(Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) { + Log.i("[Permission] Asking for camera"); + permissionsList.add(Manifest.permission.CAMERA); + } + } + } + + if (permissionsList.size() > 0) { + String[] permissions = new String[permissionsList.size()]; + permissions = permissionsList.toArray(permissions); + ActivityCompat.requestPermissions(this, permissions, 0); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + for (int i = 0; i < permissions.length; i++) { + Log.i("[Permission] " + permissions[i] + " is " + (grantResults[i] == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + } + } } diff --git a/src/org/linphone/CallVideoFragment.java b/src/org/linphone/CallVideoFragment.java index 1bc890fd6..31d65389e 100644 --- a/src/org/linphone/CallVideoFragment.java +++ b/src/org/linphone/CallVideoFragment.java @@ -38,7 +38,7 @@ import android.view.SurfaceView; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewGroup; -//import android.opengl.GLSurfaceView; +import android.widget.RelativeLayout; /** * @author Sylvain Berfini @@ -52,6 +52,7 @@ public class CallVideoFragment extends Fragment implements OnGestureListener, On private float mZoomCenterX, mZoomCenterY; private CompatibilityScaleGestureDetector mScaleDetector; private CallActivity inCallActivity; + private int previewX, previewY; @SuppressWarnings("deprecation") // Warning useless because value is ignored and automatically set by new APIs. @Override @@ -61,7 +62,7 @@ public class CallVideoFragment extends Fragment implements OnGestureListener, On if (LinphoneManager.getLc().hasCrappyOpenGL()) { view = inflater.inflate(R.layout.video_no_opengl, container, false); } else { - view = inflater.inflate(R.layout.video, container, false); + view = inflater.inflate(R.layout.video, container, false); } mVideoView = (SurfaceView) view.findViewById(R.id.videoSurface); @@ -104,6 +105,30 @@ public class CallVideoFragment extends Fragment implements OnGestureListener, On } }); + mCaptureView.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + switch (motionEvent.getAction()) { + case MotionEvent.ACTION_DOWN: + previewX = (int) motionEvent.getX(); + previewY = (int) motionEvent.getY(); + break; + case MotionEvent.ACTION_MOVE: + int x = (int) motionEvent.getX(); + int y = (int) motionEvent.getY(); + RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)mCaptureView.getLayoutParams(); + lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, 0); // Clears the rule, as there is no removeRule until API 17. + lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0); + int left = lp.leftMargin + (x - previewX); + int top = lp.topMargin + (y - previewY); + lp.leftMargin = left; + lp.topMargin = top; + view.setLayoutParams(lp); + break; + } + return true; + } + }); return view; } @@ -134,6 +159,9 @@ public class CallVideoFragment extends Fragment implements OnGestureListener, On public void onResume() { super.onResume(); + if (LinphonePreferences.instance().isOverlayEnabled()) { + LinphoneService.instance().destroyOverlay(); + } if (androidVideoWindowImpl != null) { synchronized (androidVideoWindowImpl) { LinphoneManager.getLc().setVideoWindow(androidVideoWindowImpl); @@ -155,6 +183,9 @@ public class CallVideoFragment extends Fragment implements OnGestureListener, On LinphoneManager.getLc().setVideoWindow(null); } } + if (LinphonePreferences.instance().isOverlayEnabled()) { + LinphoneService.instance().createOverlay(); + } super.onPause(); } diff --git a/src/org/linphone/ChatFragment.java b/src/org/linphone/ChatFragment.java index 687d9d581..6c5cdafd8 100644 --- a/src/org/linphone/ChatFragment.java +++ b/src/org/linphone/ChatFragment.java @@ -192,7 +192,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC @Override public void onClick(View v) { pickImage(); - LinphoneActivity.instance().checkAndRequestCameraPermission(); + LinphoneActivity.instance().checkAndRequestPermissionsToSendImage(); } }); //registerForContextMenu(sendImage); @@ -482,7 +482,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC if (adapter != null) { adapter.refreshHistory(); } else { - adapter = new ChatMessageAdapter(getActivity().getApplicationContext()); + adapter = new ChatMessageAdapter(getActivity()); } } messagesList.setAdapter(adapter); @@ -710,7 +710,6 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC editList.setVisibility(View.VISIBLE); isEditMode = true; redrawMessageList(); - //TODO refaire la liste } if(id == R.id.start_call){ LinphoneActivity.instance().setAddresGoToDialerAndCall(sipUri, LinphoneUtils.getUsernameFromAddress(sipUri), null); @@ -742,10 +741,6 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC chatRoom.sendChatMessage(message); lAddress = chatRoom.getPeerAddress(); - if (LinphoneActivity.isInstanciated()) { - LinphoneActivity.instance().onMessageSent(sipUri, messageToSend); - } - message.setListener(LinphoneManager.getInstance()); if (newChatConversation) { exitNewConversationMode(lAddress.asStringUriOnly()); @@ -819,9 +814,21 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC } private void copyTextMessageToClipboard(int id) { - String msg = LinphoneActivity.instance().getChatStorage().getTextMessageForId(chatRoom, id); - if (msg != null) { - Compatibility.copyTextToClipboard(getActivity(), msg); + LinphoneChatMessage message = null; + for (int i = 0; i < adapter.getCount(); i++) { + LinphoneChatMessage msg = adapter.getItem(i); + if (msg.getStorageId() == id) { + message = msg; + break; + } + } + + String txt = null; + if (message != null) { + txt = message.getText(); + } + if (txt != null) { + Compatibility.copyTextToClipboard(getActivity(), txt); LinphoneActivity.instance().displayCustomToast(getString(R.string.text_copied_to_clipboard), Toast.LENGTH_SHORT); } } @@ -901,7 +908,12 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC } ByteArrayOutputStream stream = new ByteArrayOutputStream(); - bm.compress(Bitmap.CompressFormat.PNG, 100, stream); + String extension = LinphoneUtils.getExtensionFromFileName(path); + if (extension != null && extension.toLowerCase(Locale.getDefault()).equals("png")) { + bm.compress(Bitmap.CompressFormat.PNG, 100, stream); + } else { + bm.compress(Bitmap.CompressFormat.JPEG, 100, stream); + } byte[] byteArray = stream.toByteArray(); return byteArray; } @@ -913,8 +925,9 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC } mUploadingImageStream = new ByteArrayInputStream(result); - LinphoneContent content = LinphoneCoreFactory.instance().createLinphoneContent("image", "jpeg", result, null); String fileName = path.substring(path.lastIndexOf("/") + 1); + String extension = LinphoneUtils.getExtensionFromFileName(fileName); + LinphoneContent content = LinphoneCoreFactory.instance().createLinphoneContent("image", extension, result, null); content.setName(fileName); LinphoneChatMessage message = chatRoom.createFileTransferMessage(content); diff --git a/src/org/linphone/ChatListFragment.java b/src/org/linphone/ChatListFragment.java index 497281f5f..c303e0554 100644 --- a/src/org/linphone/ChatListFragment.java +++ b/src/org/linphone/ChatListFragment.java @@ -57,7 +57,7 @@ import android.widget.TextView; */ public class ChatListFragment extends Fragment implements OnClickListener, OnItemClickListener { private LayoutInflater mInflater; - private List mConversations, mDrafts; + private List mConversations; private ListView chatList; private TextView noChatHistory; private ImageView edit, selectAll, deselectAll, delete, newDiscussion, cancel, backInCall; @@ -164,7 +164,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte } private void hideAndDisplayMessageIfNoChat() { - if (mConversations.size() == 0 && mDrafts.size() == 0) { + if (mConversations.size() == 0) { noChatHistory.setVisibility(View.VISIBLE); chatList.setVisibility(View.GONE); edit.setEnabled(false); @@ -179,8 +179,6 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte public void refresh() { mConversations = LinphoneActivity.instance().getChatList(); - mDrafts = LinphoneActivity.instance().getDraftChatList(); - mConversations.removeAll(mDrafts); hideAndDisplayMessageIfNoChat(); } @@ -240,8 +238,6 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte LinphoneActivity.instance().removeFromChatList(sipUri); mConversations = LinphoneActivity.instance().getChatList(); - mDrafts = LinphoneActivity.instance().getDraftChatList(); - mConversations.removeAll(mDrafts); hideAndDisplayMessageIfNoChat(); return true; } diff --git a/src/org/linphone/ChatStorage.java b/src/org/linphone/ChatStorage.java deleted file mode 100644 index a685620ae..000000000 --- a/src/org/linphone/ChatStorage.java +++ /dev/null @@ -1,409 +0,0 @@ -package org.linphone; -/* -ChatStorage.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. -*/ -import java.io.ByteArrayOutputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import org.linphone.core.LinphoneChatMessage; -import org.linphone.core.LinphoneChatRoom; -import org.linphone.mediastream.Log; - -import android.content.ContentValues; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.PackageManager.NameNotFoundException; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.graphics.Bitmap; -import android.graphics.Bitmap.CompressFormat; -import android.graphics.BitmapFactory; -import android.preference.PreferenceManager; - -/** - * @author Sylvain Berfini - */ -public class ChatStorage { - private static final int INCOMING = 1; - private static final int OUTGOING = 0; - private static final int READ = 1; - private static final int NOT_READ = 0; - - private static ChatStorage instance; - private Context context; - private SQLiteDatabase db; - private boolean useNativeAPI; - private static final String TABLE_NAME = "chat"; - private static final String DRAFT_TABLE_NAME = "chat_draft"; - - public synchronized static final ChatStorage getInstance() { - if (instance == null) - instance = new ChatStorage(LinphoneService.instance().getApplicationContext()); - return instance; - } - - public void restartChatStorage() { - if (instance != null) - instance.close(); - instance = new ChatStorage(LinphoneService.instance().getApplicationContext()); - } - - private boolean isVersionUsingNewChatStorage() { - try { - return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode >= 2200; - } catch (NameNotFoundException e) { - Log.e(e); - } - return true; - } - - private ChatStorage(Context c) { - context = c; - boolean useLinphoneStorage = true; - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(LinphoneService.instance()); - boolean updateNeeded = prefs.getBoolean(c.getString(R.string.pref_first_time_linphone_chat_storage), !LinphonePreferences.instance().isFirstLaunch()); - updateNeeded = updateNeeded && !isVersionUsingNewChatStorage(); - useNativeAPI = useLinphoneStorage && !updateNeeded; - Log.d("Using native API: " + useNativeAPI); - - if (!useNativeAPI) { - ChatHelper chatHelper = new ChatHelper(context); - db = chatHelper.getWritableDatabase(); - } - } - - public void close() { - if (!useNativeAPI) { - db.close(); - } - } - - public void updateMessageStatus(String to, String message, int status) { - if (useNativeAPI) { - return; - } - - String[] whereArgs = { String.valueOf(OUTGOING), to, message }; - Cursor c = db.query(TABLE_NAME, null, "direction LIKE ? AND remoteContact LIKE ? AND message LIKE ?", whereArgs, null, null, "id DESC"); - - String id = null; - if (c.moveToFirst()) { - try { - id = c.getString(c.getColumnIndex("id")); - } catch (Exception e) { - Log.e(e); - } - } - c.close(); - - if (id != null && id.length() > 0) { - int intID = Integer.parseInt(id); - updateMessageStatus(to, intID, status); - } - } - - public void updateMessageStatus(String to, int id, int status) { - if (useNativeAPI) { - return; - } - - ContentValues values = new ContentValues(); - values.put("status", status); - - db.update(TABLE_NAME, values, "id LIKE " + id, null); - } - - public int saveTextMessage(String from, String to, String message, long time) { - if (useNativeAPI) { - return -1; - } - - ContentValues values = new ContentValues(); - if (from.equals("")) { - values.put("localContact", from); - values.put("remoteContact", to); - values.put("direction", OUTGOING); - values.put("read", READ); - values.put("status", LinphoneChatMessage.State.InProgress.toInt()); - } else if (to.equals("")) { - values.put("localContact", to); - values.put("remoteContact", from); - values.put("direction", INCOMING); - values.put("read", NOT_READ); - values.put("status", LinphoneChatMessage.State.Idle.toInt()); - } - values.put("message", message); - values.put("time", time); - return (int) db.insert(TABLE_NAME, null, values); - } - - public int saveImageMessage(String from, String to, Bitmap image, String url, long time) { - if (useNativeAPI) { - return -1; - } - - ContentValues values = new ContentValues(); - if (from.equals("")) { - values.put("localContact", from); - values.put("remoteContact", to); - values.put("direction", OUTGOING); - values.put("read", READ); - values.put("status", LinphoneChatMessage.State.InProgress.toInt()); - } else if (to.equals("")) { - values.put("localContact", to); - values.put("remoteContact", from); - values.put("direction", INCOMING); - values.put("read", NOT_READ); - values.put("status", LinphoneChatMessage.State.Idle.toInt()); - } - values.put("url", url); - - if (image != null) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - image.compress(CompressFormat.JPEG, 100, baos); - values.put("image", baos.toByteArray()); - } - - values.put("time", time); - return (int) db.insert(TABLE_NAME, null, values); - } - - public void saveImage(int id, Bitmap image) { - if (useNativeAPI) { - //Handled before this point - return; - } - - if (image == null) - return; - - ContentValues values = new ContentValues(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - image.compress(CompressFormat.JPEG, 100, baos); - values.put("image", baos.toByteArray()); - - db.update(TABLE_NAME, values, "id LIKE " + id, null); - } - - public int saveDraft(String to, String message) { - if (useNativeAPI) { - //TODO - return -1; - } - - ContentValues values = new ContentValues(); - values.put("remoteContact", to); - values.put("message", message); - return (int) db.insert(DRAFT_TABLE_NAME, null, values); - } - - public void updateDraft(String to, String message) { - if (useNativeAPI) { - //TODO - return; - } - - ContentValues values = new ContentValues(); - values.put("message", message); - - db.update(DRAFT_TABLE_NAME, values, "remoteContact LIKE \"" + to + "\"", null); - } - - public void deleteDraft(String to) { - if (useNativeAPI) { - //TODO - return; - } - - db.delete(DRAFT_TABLE_NAME, "remoteContact LIKE \"" + to + "\"", null); - } - - public String getDraft(String to) { - if (useNativeAPI) { - //TODO - return ""; - } - - Cursor c = db.query(DRAFT_TABLE_NAME, null, "remoteContact LIKE \"" + to + "\"", null, null, null, "id ASC"); - - String message = null; - while (c.moveToNext()) { - try { - message = c.getString(c.getColumnIndex("message")); - } catch (Exception e) { - Log.e(e); - } - } - c.close(); - - return message; - } - - public List getDrafts() { - List drafts = new ArrayList(); - - if (useNativeAPI) { - //TODO - } else { - Cursor c = db.query(DRAFT_TABLE_NAME, null, null, null, null, null, "id ASC"); - - while (c.moveToNext()) { - try { - String to = c.getString(c.getColumnIndex("remoteContact")); - drafts.add(to); - } catch (Exception e) { - Log.e(e); - } - } - c.close(); - } - - return drafts; - } - - public String getTextMessageForId(LinphoneChatRoom chatroom, int id) { - String message = null; - - if (useNativeAPI) { - LinphoneChatMessage msg = getMessage(chatroom, id); - - if (msg != null) { - message = msg.getText(); - } - } else { - Cursor c = db.query(TABLE_NAME, null, "id LIKE " + id, null, null, null, null); - - if (c.moveToFirst()) { - try { - message = c.getString(c.getColumnIndex("message")); - } catch (Exception e) { - Log.e(e); - } - } - c.close(); - } - - return message; - } - - public LinphoneChatMessage getMessage(LinphoneChatRoom chatroom, int id) { - LinphoneChatMessage[] history = chatroom.getHistory(); - for (LinphoneChatMessage msg : history) { - if (msg.getStorageId() == id) { - return msg; - } - } - return null; - } - - public void removeDiscussion(String correspondent) { - LinphoneChatRoom chatroom = LinphoneManager.getLc().getOrCreateChatRoom(correspondent); - chatroom.deleteHistory(); - } - - public ArrayList getChatList() { - ArrayList chatList = new ArrayList(); - - LinphoneChatRoom[] chats = LinphoneManager.getLc().getChatRooms(); - List rooms = new ArrayList(); - - for (LinphoneChatRoom chatroom : chats) { - if (chatroom.getHistorySize() > 0) { - rooms.add(chatroom); - } - } - - if (rooms.size() > 1) { - Collections.sort(rooms, new Comparator() { - @Override - public int compare(LinphoneChatRoom a, LinphoneChatRoom b) { - LinphoneChatMessage[] messagesA = a.getHistory(1); - LinphoneChatMessage[] messagesB = b.getHistory(1); - long atime = messagesA[0].getTime(); - long btime = messagesB[0].getTime(); - - if (atime > btime) - return -1; - else if (btime > atime) - return 1; - else - return 0; - } - }); - } - - for (LinphoneChatRoom chatroom : rooms) { - chatList.add(chatroom.getPeerAddress().asStringUriOnly()); - } - - return chatList; - } - - public void deleteMessage(LinphoneChatRoom chatroom, int id) { - if (useNativeAPI) { - LinphoneChatMessage msg = getMessage(chatroom, id); - if (msg != null){ - chatroom.deleteMessage(msg); - } - } else { - db.delete(TABLE_NAME, "id LIKE " + id, null); - } - } - - public void markMessageAsRead(int id) { - if (!useNativeAPI) { - ContentValues values = new ContentValues(); - values.put("read", READ); - db.update(TABLE_NAME, values, "id LIKE " + id, null); - } - } - - public void markConversationAsRead(LinphoneChatRoom chatroom) { - if (useNativeAPI) { - chatroom.markAsRead(); - } - } - - - class ChatHelper extends SQLiteOpenHelper { - - private static final int DATABASE_VERSION = 15; - private static final String DATABASE_NAME = "linphone-android"; - - ChatHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + TABLE_NAME + " (id INTEGER PRIMARY KEY AUTOINCREMENT, localContact TEXT NOT NULL, remoteContact TEXT NOT NULL, direction INTEGER, message TEXT, image BLOB, url TEXT, time NUMERIC, read INTEGER, status INTEGER);"); - db.execSQL("CREATE TABLE " + DRAFT_TABLE_NAME + " (id INTEGER PRIMARY KEY AUTOINCREMENT, remoteContact TEXT NOT NULL, message TEXT);"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME + ";"); - db.execSQL("DROP TABLE IF EXISTS " + DRAFT_TABLE_NAME + ";"); - onCreate(db); - } - } -} diff --git a/src/org/linphone/ContactDetailsFragment.java b/src/org/linphone/ContactDetailsFragment.java index 1c2c8d11c..b77fa72e9 100644 --- a/src/org/linphone/ContactDetailsFragment.java +++ b/src/org/linphone/ContactDetailsFragment.java @@ -129,6 +129,9 @@ public class ContactDetailsFragment extends Fragment implements OnClickListener if (displayednumberOrAddress.startsWith("sip:")) { displayednumberOrAddress = displayednumberOrAddress.replace("sip:", ""); } + if (displayednumberOrAddress.contains("@")) { + displayednumberOrAddress = displayednumberOrAddress.split("@")[0]; + } TextView label = (TextView) v.findViewById(R.id.address_label); if (noa.isSIPAddress()) { diff --git a/src/org/linphone/ContactEditorFragment.java b/src/org/linphone/ContactEditorFragment.java index e94aefdc0..01865a037 100644 --- a/src/org/linphone/ContactEditorFragment.java +++ b/src/org/linphone/ContactEditorFragment.java @@ -25,6 +25,8 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import org.linphone.core.LinphoneCore; +import org.linphone.core.LinphoneProxyConfig; import org.linphone.mediastream.Log; import org.linphone.mediastream.Version; @@ -34,12 +36,14 @@ import android.app.Dialog; import android.app.Fragment; import android.content.Context; import android.content.Intent; +import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.Parcelable; +import android.provider.ContactsContract.DisplayPhoto; import android.provider.MediaStore; import android.text.Editable; import android.text.InputType; @@ -122,6 +126,10 @@ public class ContactEditorFragment extends Fragment { ok.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { + LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); + LinphoneProxyConfig lpc = lc != null ? lc.getDefaultProxyConfig() : null; + String defaultDomain = lpc != null ? lpc.getDomain() : null; + if (isNewContact) { boolean areAllFielsEmpty = true; for (LinphoneNumberOrAddress nounoa : numbersAndAddresses) { @@ -140,8 +148,16 @@ public class ContactEditorFragment extends Fragment { if (photoToAdd != null) { contact.setPhoto(photoToAdd); } - for (LinphoneNumberOrAddress numberOrAddress : numbersAndAddresses) { - contact.addOrUpdateNumberOrAddress(numberOrAddress); + for (LinphoneNumberOrAddress noa : numbersAndAddresses) { + if (noa.isSIPAddress() && noa.getValue() != null) { + if (!noa.getValue().contains("@") && defaultDomain != null) { + noa.setValue(noa.getValue() + "@" + defaultDomain); + } + if (!noa.getValue().startsWith("sip:")) { + noa.setValue("sip:" + noa.getValue()); + } + } + contact.addOrUpdateNumberOrAddress(noa); } contact.save(); getFragmentManager().popBackStackImmediate(); @@ -372,10 +388,31 @@ public class ContactEditorFragment extends Fragment { image = BitmapFactory.decodeFile(filePath); } + Bitmap scaledPhoto; + int size = getThumbnailSize(); + if (size > 0) { + scaledPhoto = Bitmap.createScaledBitmap(image, size, size, false); + } else { + scaledPhoto = Bitmap.createBitmap(image); + } + image.recycle(); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); - image.compress(Bitmap.CompressFormat.PNG , 75, stream); - photoToAdd = stream.toByteArray(); - contactPicture.setImageBitmap(image); + scaledPhoto.compress(Bitmap.CompressFormat.PNG , 0, stream); + contactPicture.setImageBitmap(scaledPhoto); + photoToAdd = stream.toByteArray(); + } + + private int getThumbnailSize() { + int value = -1; + Cursor c = LinphoneActivity.instance().getContentResolver().query(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, new String[] { DisplayPhoto.THUMBNAIL_MAX_DIM }, null, null, null); + try { + c.moveToFirst(); + value = c.getInt(0); + } catch (Exception e) { + Log.e(e); + } + return value; } private LinearLayout initNumbersFields(final LinphoneContact contact) { @@ -449,6 +486,9 @@ public class ContactEditorFragment extends Fragment { firstSipAddressIndex = controls.getChildCount(); } numberOrAddress = numberOrAddress.replace("sip:", ""); + if (numberOrAddress.contains("@")) { + numberOrAddress = numberOrAddress.split("@")[0]; + } } if ((getResources().getBoolean(R.bool.hide_phone_numbers_in_editor) && !isSIP) || (getResources().getBoolean(R.bool.hide_sip_addresses_in_editor) && isSIP)) { if (forceAddNumber) diff --git a/src/org/linphone/ContactsManager.java b/src/org/linphone/ContactsManager.java index 78b7ef8b5..48d42e69d 100644 --- a/src/org/linphone/ContactsManager.java +++ b/src/org/linphone/ContactsManager.java @@ -174,7 +174,7 @@ public class ContactsManager extends ContentObserver { } public void initializeSyncAccount(Context context, ContentResolver contentResolver) { - initializeContactManager(context,contentResolver); + initializeContactManager(context, contentResolver); AccountManager accountManager = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE); Account[] accounts = accountManager.getAccountsByType(context.getPackageName()); @@ -236,7 +236,7 @@ public class ContactsManager extends ContentObserver { public List fetchContactsAsync() { List contacts = new ArrayList(); - if (mAccount != null && hasContactsAccess()) { + if (hasContactsAccess()) { Cursor c = Compatibility.getContactsCursor(contentResolver, null); if (c != null) { while (c.moveToNext()) { @@ -330,6 +330,7 @@ public class ContactsManager extends ContentObserver { Log.e(e); } } + public String getString(int resourceID) { return context.getString(resourceID); } diff --git a/src/org/linphone/KeepAliveHandler.java b/src/org/linphone/KeepAliveHandler.java index 7c3ec6fee..9b554e9fa 100644 --- a/src/org/linphone/KeepAliveHandler.java +++ b/src/org/linphone/KeepAliveHandler.java @@ -18,6 +18,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +import org.linphone.core.LinphoneCoreFactory; import org.linphone.mediastream.Log; import android.content.BroadcastReceiver; @@ -25,10 +26,13 @@ import android.content.Context; import android.content.Intent; public class KeepAliveHandler extends BroadcastReceiver { - @Override public void onReceive(Context context, Intent intent) { - //Log.i("Keep alive handler invoked"); //TODO FIXME Crash since the log rework + boolean isDebugEnabled = LinphonePreferences.instance().isDebugEnabled(); + LinphoneCoreFactory.instance().enableLogCollection(isDebugEnabled); + LinphoneCoreFactory.instance().setDebugMode(isDebugEnabled, context.getString(R.string.app_name)); + + Log.i("Keep alive handler invoked"); if (LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null) { //first refresh registers LinphoneManager.getLc().refreshRegisters(); @@ -38,9 +42,6 @@ public class KeepAliveHandler extends BroadcastReceiver { } catch (InterruptedException e) { //Log.e("Cannot sleep for 2s", e); //TODO FIXME Crash since the log rework } - } - } - } diff --git a/src/org/linphone/KeepAliveReceiver.java b/src/org/linphone/KeepAliveReceiver.java index 321953df1..06f7e532b 100644 --- a/src/org/linphone/KeepAliveReceiver.java +++ b/src/org/linphone/KeepAliveReceiver.java @@ -29,10 +29,8 @@ import android.content.Intent; * Purpose of this receiver is to disable keep alives when screen is off * */ public class KeepAliveReceiver extends BroadcastReceiver { - @Override public void onReceive(Context context, Intent intent) { - if (!LinphoneService.isReady()) { Log.i("Keep alive broadcast received while Linphone service not ready"); return; @@ -43,7 +41,5 @@ public class KeepAliveReceiver extends BroadcastReceiver { LinphoneManager.getLc().enableKeepAlive(false); } } - } - } diff --git a/src/org/linphone/LinphoneActivity.java b/src/org/linphone/LinphoneActivity.java index da1e71f3e..e33e90058 100644 --- a/src/org/linphone/LinphoneActivity.java +++ b/src/org/linphone/LinphoneActivity.java @@ -23,11 +23,14 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import org.linphone.LinphoneManager.AddressType; import org.linphone.assistant.AssistantActivity; import org.linphone.assistant.RemoteProvisioningLoginActivity; +import org.linphone.compatibility.Compatibility; import org.linphone.core.CallDirection; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneAuthInfo; @@ -61,11 +64,11 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.graphics.Bitmap; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; +import android.provider.Settings; import android.support.v4.app.ActivityCompat; import android.support.v4.widget.DrawerLayout; import android.view.Gravity; @@ -97,14 +100,10 @@ import android.widget.Toast; public class LinphoneActivity extends Activity implements OnClickListener, ContactPicked, ActivityCompat.OnRequestPermissionsResultCallback { public static final String PREF_FIRST_LAUNCH = "pref_first_launch"; private static final int SETTINGS_ACTIVITY = 123; - private static final int FIRST_LOGIN_ACTIVITY = 101; - private static final int REMOTE_PROVISIONING_LOGIN_ACTIVITY = 102; private static final int CALL_ACTIVITY = 19; - private static final int PERMISSIONS_REQUEST_CONTACTS = 200; - private static final int PERMISSIONS_REQUEST_RECORD_AUDIO = 201; - private static final int PERMISSIONS_REQUEST_RECORD_AUDIO_INCOMING_CALL = 203; - private static final int PERMISSIONS_REQUEST_EXTERNAL_FILE_STORAGE = 204; - private static final int PERMISSIONS_REQUEST_CAMERA = 205; + private static final int PERMISSIONS_REQUEST_OVERLAY = 206; + private static final int PERMISSIONS_REQUEST_SYNC = 207; + private static final int PERMISSIONS_REQUEST_CONTACTS = 208; private static LinphoneActivity instance; @@ -140,17 +139,6 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta return instance; throw new RuntimeException("LinphoneActivity not instantiated yet"); } - - @Override - protected void onSaveInstanceState(Bundle outState) { - outState.putSerializable("CurrentFragment", currentFragment); - super.onSaveInstanceState(outState); - } - - @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); - } @Override protected void onCreate(Bundle savedInstanceState) { @@ -171,18 +159,29 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta Intent wizard = new Intent(); wizard.setClass(this, RemoteProvisioningLoginActivity.class); wizard.putExtra("Domain", LinphoneManager.getInstance().wizardLoginViewDomain); - startActivityForResult(wizard, REMOTE_PROVISIONING_LOGIN_ACTIVITY); - } else if (savedInstanceState == null && (useFirstLoginActivity && LinphonePreferences.instance().isFirstLaunch() || LinphoneManager.getLc().getProxyConfigList().length == 0)) { + startActivity(wizard); + finish(); + return; + } else if (savedInstanceState == null && (useFirstLoginActivity && LinphonePreferences.instance().isFirstLaunch())) { if (LinphonePreferences.instance().getAccountCount() > 0) { LinphonePreferences.instance().firstLaunchSuccessful(); } else { - startActivityForResult(new Intent().setClass(this, AssistantActivity.class), FIRST_LOGIN_ACTIVITY); + startActivity(new Intent().setClass(this, AssistantActivity.class)); + finish(); + return; } } + + if (getIntent() != null && getIntent().getExtras() != null) { + newProxyConfig = getIntent().getExtras().getBoolean("isNewProxyConfig"); + } - //TODO rework - if (getResources().getBoolean(R.bool.use_linphone_tag) && getPackageManager().checkPermission(Manifest.permission.WRITE_SYNC_SETTINGS, getPackageName()) == PackageManager.PERMISSION_GRANTED) { - ContactsManager.getInstance().initializeSyncAccount(getApplicationContext(), getContentResolver()); + if (getResources().getBoolean(R.bool.use_linphone_tag)) { + if (getPackageManager().checkPermission(Manifest.permission.WRITE_SYNC_SETTINGS, getPackageName()) != PackageManager.PERMISSION_GRANTED) { + checkSyncPermission(); + } else { + ContactsManager.getInstance().initializeSyncAccount(getApplicationContext(), getContentResolver()); + } } else { ContactsManager.getInstance().initializeContactManager(getApplicationContext(), getContentResolver()); } @@ -198,8 +197,6 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta currentFragment = nextFragment = FragmentsAvailable.EMPTY; if (savedInstanceState == null) { changeCurrentFragment(FragmentsAvailable.DIALER, getIntent().getExtras()); - } else { - changeCurrentFragment(((FragmentsAvailable)savedInstanceState.getSerializable("CurrentFragment")), getIntent().getExtras()); } mListener = new LinphoneCoreListenerBase(){ @@ -243,28 +240,10 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta @Override public void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State state, String message) { if (state == State.IncomingReceived) { - if (getPackageManager().checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName()) == PackageManager.PERMISSION_GRANTED || LinphonePreferences.instance().audioPermAsked()) { - startActivity(new Intent(LinphoneActivity.instance(), CallIncomingActivity.class)); - } else { - checkAndRequestAudioPermission(true); - } + startActivity(new Intent(LinphoneActivity.instance(), CallIncomingActivity.class)); } else if (state == State.OutgoingInit || state == State.OutgoingProgress) { - if (getPackageManager().checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName()) == PackageManager.PERMISSION_GRANTED || LinphonePreferences.instance().audioPermAsked()) { - startActivity(new Intent(LinphoneActivity.instance(), CallOutgoingActivity.class)); - } else { - checkAndRequestAudioPermission(false); - } + startActivity(new Intent(LinphoneActivity.instance(), CallOutgoingActivity.class)); } else if (state == State.CallEnd || state == State.Error || state == State.CallReleased) { - // Convert LinphoneCore message for internalization - if (message != null && call.getErrorInfo().getReason() == Reason.Declined) { - displayCustomToast(getString(R.string.error_call_declined), Toast.LENGTH_SHORT); - } else if (message != null && call.getErrorInfo().getReason() == Reason.NotFound) { - displayCustomToast(getString(R.string.error_user_not_found), Toast.LENGTH_SHORT); - } else if (message != null && call.getErrorInfo().getReason() == Reason.Media) { - displayCustomToast(getString(R.string.error_incompatible_media), Toast.LENGTH_SHORT); - } else if (message != null && state == State.Error) { - displayCustomToast(getString(R.string.error_unknown) + " - " + message, Toast.LENGTH_SHORT); - } resetClassicMenuLayoutAndGoBackToCallIfStillRunning(); } @@ -381,6 +360,7 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta newFragment = new HistoryDetailFragment(); break; case CONTACTS_LIST: + checkAndRequestReadContactsPermission(); newFragment = new ContactsListFragment(); if (isTablet()) { ((ContactsListFragment) newFragment).displayFirstContact(); @@ -826,43 +806,52 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta } public List getChatList() { - return getChatStorage().getChatList(); - } + ArrayList chatList = new ArrayList(); - public List getDraftChatList() { - return getChatStorage().getDrafts(); + LinphoneChatRoom[] chats = LinphoneManager.getLc().getChatRooms(); + List rooms = new ArrayList(); + + for (LinphoneChatRoom chatroom : chats) { + if (chatroom.getHistorySize() > 0) { + rooms.add(chatroom); + } + } + + if (rooms.size() > 1) { + Collections.sort(rooms, new Comparator() { + @Override + public int compare(LinphoneChatRoom a, LinphoneChatRoom b) { + LinphoneChatMessage[] messagesA = a.getHistory(1); + LinphoneChatMessage[] messagesB = b.getHistory(1); + long atime = messagesA[0].getTime(); + long btime = messagesB[0].getTime(); + + if (atime > btime) + return -1; + else if (btime > atime) + return 1; + else + return 0; + } + }); + } + + for (LinphoneChatRoom chatroom : rooms) { + chatList.add(chatroom.getPeerAddress().asStringUriOnly()); + } + + return chatList; } public void removeFromChatList(String sipUri) { - getChatStorage().removeDiscussion(sipUri); - } - - public void removeFromDrafts(String sipUri) { - getChatStorage().deleteDraft(sipUri); + LinphoneChatRoom chatroom = LinphoneManager.getLc().getOrCreateChatRoom(sipUri); + chatroom.deleteHistory(); } public void updateMissedChatCount() { displayMissedChats(getUnreadMessageCount()); } - public int onMessageSent(String to, String message) { - getChatStorage().deleteDraft(to); - return getChatStorage().saveTextMessage("", to, message, System.currentTimeMillis()); - } - - public int onMessageSent(String to, Bitmap image, String imageURL) { - getChatStorage().deleteDraft(to); - return getChatStorage().saveImageMessage("", to, image, imageURL, System.currentTimeMillis()); - } - - public void onMessageStateChanged(String to, String message, int newState) { - getChatStorage().updateMessageStatus(to, message, newState); - } - - public void onImageMessageStateChanged(String to, int id, int newState) { - getChatStorage().updateMessageStatus(to, id, newState); - } - public void displayMissedCalls(final int missedCallsCount) { if (missedCallsCount > 0) { missedCalls.setText(missedCallsCount + ""); @@ -893,8 +882,6 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta } } - - public void displayCustomToast(final String message, final int duration) { LayoutInflater inflater = getLayoutInflater(); View layout = inflater.inflate(R.layout.toast, (ViewGroup) findViewById(R.id.toastRoot)); @@ -1066,10 +1053,6 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta return currentFragment; } - public ChatStorage getChatStorage() { - return ChatStorage.getInstance(); - } - public void addContact(String displayName, String sipUri) { Bundle extras = new Bundle(); @@ -1079,19 +1062,17 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta public void editContact(LinphoneContact contact) { - Bundle extras = new Bundle(); - extras.putSerializable("Contact", contact); - changeCurrentFragment(FragmentsAvailable.CONTACT_EDITOR, extras); - + Bundle extras = new Bundle(); + extras.putSerializable("Contact", contact); + changeCurrentFragment(FragmentsAvailable.CONTACT_EDITOR, extras); } public void editContact(LinphoneContact contact, String sipAddress) { - - Bundle extras = new Bundle(); - extras.putSerializable("Contact", contact); - extras.putSerializable("NewSipAdress", sipAddress); - changeCurrentFragment(FragmentsAvailable.CONTACT_EDITOR, extras); + Bundle extras = new Bundle(); + extras.putSerializable("Contact", contact); + extras.putSerializable("NewSipAdress", sipAddress); + changeCurrentFragment(FragmentsAvailable.CONTACT_EDITOR, extras); } public void quit() { @@ -1129,6 +1110,10 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta } else { resetClassicMenuLayoutAndGoBackToCallIfStillRunning(); } + } else if (requestCode == PERMISSIONS_REQUEST_OVERLAY) { + if (Compatibility.canDrawOverlays(this)) { + LinphonePreferences.instance().enableOverlay(true); + } } else { super.onActivityResult(requestCode, resultCode, data); } @@ -1169,101 +1154,125 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta } return true; } + + public boolean checkAndRequestOverlayPermission() { + Log.i("[Permission] Draw overlays permission is " + (Compatibility.canDrawOverlays(this) ? "granted" : "denied")); + if (!Compatibility.canDrawOverlays(this)) { + Log.i("[Permission] Asking for overlay"); + Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); + startActivityForResult(intent, PERMISSIONS_REQUEST_OVERLAY); + return false; + } + return true; + } + + public void checkAndRequestReadExternalStoragePermission() { + checkAndRequestPermission(Manifest.permission.READ_EXTERNAL_STORAGE, 0); + } public void checkAndRequestExternalStoragePermission() { - if (LinphonePreferences.instance().writeExternalStoragePermAsked()) { - return; - } - checkAndRequestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, PERMISSIONS_REQUEST_EXTERNAL_FILE_STORAGE); + checkAndRequestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, 0); } public void checkAndRequestCameraPermission() { - if (LinphonePreferences.instance().cameraPermAsked()) { - return; - } - checkAndRequestPermission(Manifest.permission.CAMERA, PERMISSIONS_REQUEST_CAMERA); + checkAndRequestPermission(Manifest.permission.CAMERA, 0); } public void checkAndRequestReadContactsPermission() { - if (LinphonePreferences.instance().readContactsPermAsked()) { - return; - } checkAndRequestPermission(Manifest.permission.READ_CONTACTS, PERMISSIONS_REQUEST_CONTACTS); } public void checkAndRequestWriteContactsPermission() { - if (LinphonePreferences.instance().writeContactsPermAsked()) { - return; - } - checkAndRequestPermission(Manifest.permission.WRITE_CONTACTS, PERMISSIONS_REQUEST_CONTACTS); + checkAndRequestPermission(Manifest.permission.WRITE_CONTACTS, 0); } - public void checkAndRequestAudioPermission(boolean isIncomingCall) { - if (LinphonePreferences.instance().audioPermAsked()) { - return; + public void checkAndRequestPermissionsToSendImage() { + ArrayList permissionsList = new ArrayList(); + + int readExternalStorage = getPackageManager().checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE, getPackageName()); + Log.i("[Permission] Read external storage permission is " + (readExternalStorage == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + int camera = getPackageManager().checkPermission(Manifest.permission.CAMERA, getPackageName()); + Log.i("[Permission] Camera permission is " + (camera == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + + if (readExternalStorage != PackageManager.PERMISSION_GRANTED) { + if (LinphonePreferences.instance().firstTimeAskingForPermission(Manifest.permission.READ_EXTERNAL_STORAGE) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) { + Log.i("[Permission] Asking for read external storage"); + permissionsList.add(Manifest.permission.READ_EXTERNAL_STORAGE); + } } - checkAndRequestPermission(Manifest.permission.RECORD_AUDIO, isIncomingCall ? PERMISSIONS_REQUEST_RECORD_AUDIO_INCOMING_CALL : PERMISSIONS_REQUEST_RECORD_AUDIO); - if (LinphonePreferences.instance().shouldInitiateVideoCall() || - LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests()) { - checkAndRequestCameraPermission(); + if (camera != PackageManager.PERMISSION_GRANTED) { + if (LinphonePreferences.instance().firstTimeAskingForPermission(Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) { + Log.i("[Permission] Asking for camera"); + permissionsList.add(Manifest.permission.CAMERA); + } } + if (permissionsList.size() > 0) { + String[] permissions = new String[permissionsList.size()]; + permissions = permissionsList.toArray(permissions); + ActivityCompat.requestPermissions(this, permissions, 0); + } + } + + private void checkSyncPermission() { + checkAndRequestPermission(Manifest.permission.WRITE_SYNC_SETTINGS, PERMISSIONS_REQUEST_SYNC); } public void checkAndRequestPermission(String permission, int result) { - if (getPackageManager().checkPermission(permission, getPackageName()) != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions(this, new String[]{permission}, result); + int permissionGranted = getPackageManager().checkPermission(permission, getPackageName()); + Log.i("[Permission] " + permission + " is " + (permissionGranted == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + + if (permissionGranted != PackageManager.PERMISSION_GRANTED) { + if (LinphonePreferences.instance().firstTimeAskingForPermission(permission) || ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) { + Log.i("[Permission] Asking for " + permission); + ActivityCompat.requestPermissions(this, new String[] { permission }, result); + } } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - if (grantResults.length > 0) { - for (int i = 0; i < grantResults.length; i++) { - if (grantResults[i] == PackageManager.PERMISSION_DENIED) { - if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])) { - if (permissions[i].equals(Manifest.permission.RECORD_AUDIO)) { - LinphonePreferences.instance().neverAskAudioPerm(); - } else if (permissions[i].equals(Manifest.permission.CAMERA)) { - LinphonePreferences.instance().neverAskCameraPerm(); - } else if (permissions[i].equals(Manifest.permission.READ_CONTACTS)) { - LinphonePreferences.instance().neverAskReadContactsPerm(); - } else if (permissions[i].equals(Manifest.permission.WRITE_CONTACTS)) { - LinphonePreferences.instance().neverAskWriteContactsPerm(); - } else if (permissions[i].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - LinphonePreferences.instance().neverAskWriteExternalStoragePerm(); - } - } else { - //TODO: show dialog explaining what we need the permission for - } - } else { - if (permissions[i].equals(Manifest.permission.RECORD_AUDIO)) { - LinphonePreferences.instance().neverAskAudioPerm(); - } else if (permissions[i].equals(Manifest.permission.CAMERA)) { - LinphonePreferences.instance().neverAskCameraPerm(); - } else if (permissions[i].equals(Manifest.permission.READ_CONTACTS)) { - LinphonePreferences.instance().neverAskReadContactsPerm(); - } else if (permissions[i].equals(Manifest.permission.WRITE_CONTACTS)) { - LinphonePreferences.instance().neverAskWriteContactsPerm(); - } else if (permissions[i].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - LinphonePreferences.instance().neverAskWriteExternalStoragePerm(); - } - } - } + for (int i = 0; i < permissions.length; i++) { + Log.i("[Permission] " + permissions[i] + " is " + (grantResults[i] == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); } + switch (requestCode) { - case PERMISSIONS_REQUEST_RECORD_AUDIO: - startActivity(new Intent(this, CallOutgoingActivity.class)); + case PERMISSIONS_REQUEST_SYNC: + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + ContactsManager.getInstance().initializeSyncAccount(getApplicationContext(), getContentResolver()); + } else { + ContactsManager.getInstance().initializeContactManager(getApplicationContext(), getContentResolver()); + } break; - case PERMISSIONS_REQUEST_RECORD_AUDIO_INCOMING_CALL: - startActivity(new Intent(this, CallIncomingActivity.class)); + case PERMISSIONS_REQUEST_CONTACTS: + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + ContactsManager.getInstance().enableContactsAccess(); + ContactsManager.getInstance().fetchContacts(); + fetchedContactsOnce = true; + } break; } } + + @Override + protected void onStart() { + super.onStart(); + + int contacts = getPackageManager().checkPermission(Manifest.permission.READ_CONTACTS, getPackageName()); + Log.i("[Permission] Contacts permission is " + (contacts == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + + if (contacts == PackageManager.PERMISSION_GRANTED && !fetchedContactsOnce) { + ContactsManager.getInstance().enableContactsAccess(); + ContactsManager.getInstance().fetchContacts(); + fetchedContactsOnce = true; + } else { + checkAndRequestReadContactsPermission(); + } + } @Override protected void onResume() { super.onResume(); - + if (!LinphoneService.isReady()) { startService(new Intent(Intent.ACTION_MAIN).setClass(this, LinphoneService.class)); } @@ -1273,14 +1282,6 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta lc.addListener(mListener); } - if (getPackageManager().checkPermission(Manifest.permission.READ_CONTACTS, getPackageName()) == PackageManager.PERMISSION_GRANTED && !fetchedContactsOnce) { - ContactsManager.getInstance().enableContactsAccess(); - ContactsManager.getInstance().fetchContacts(); - fetchedContactsOnce = true; - } else { - checkAndRequestReadContactsPermission(); - } - refreshAccounts(); updateMissedChatCount(); @@ -1296,18 +1297,11 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta if (LinphoneManager.getLc().getCalls().length > 0) { LinphoneCall call = LinphoneManager.getLc().getCalls()[0]; LinphoneCall.State callState = call.getState(); + if (callState == State.IncomingReceived) { - if (getPackageManager().checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName()) == PackageManager.PERMISSION_GRANTED || LinphonePreferences.instance().audioPermAsked()) { - startActivity(new Intent(this, CallIncomingActivity.class)); - } else { - checkAndRequestAudioPermission(true); - } + startActivity(new Intent(this, CallIncomingActivity.class)); } else if (callState == State.OutgoingInit || callState == State.OutgoingProgress || callState == State.OutgoingRinging) { - if (getPackageManager().checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName()) == PackageManager.PERMISSION_GRANTED || LinphonePreferences.instance().audioPermAsked()) { - startActivity(new Intent(this, CallOutgoingActivity.class)); - } else { - checkAndRequestAudioPermission(false); - } + startActivity(new Intent(this, CallOutgoingActivity.class)); } else { if (call.getCurrentParamsCopy().getVideoEnabled()) { startVideoActivity(call); @@ -1349,7 +1343,7 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - + Bundle extras = intent.getExtras(); if (extras != null && extras.getBoolean("GoToChat", false)) { LinphoneService.instance().removeMessageNotification(); @@ -1386,11 +1380,7 @@ public class LinphoneActivity extends Activity implements OnClickListener, Conta if (CallActivity.isInstanciated()) { CallActivity.instance().startIncomingCallActivity(); } else { - if (getPackageManager().checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName()) == PackageManager.PERMISSION_GRANTED || LinphonePreferences.instance().audioPermAsked()) { - startActivity(new Intent(this, CallIncomingActivity.class)); - } else { - checkAndRequestAudioPermission(true); - } + startActivity(new Intent(this, CallIncomingActivity.class)); } } } diff --git a/src/org/linphone/LinphoneContact.java b/src/org/linphone/LinphoneContact.java index ba76ea319..43453a62b 100644 --- a/src/org/linphone/LinphoneContact.java +++ b/src/org/linphone/LinphoneContact.java @@ -48,10 +48,11 @@ public class LinphoneContact implements Serializable, Comparable addresses; private transient ArrayList changesToCommit; + private transient ArrayList changesToCommit2; private boolean hasSipAddress; public LinphoneContact() { @@ -60,6 +61,7 @@ public class LinphoneContact implements Serializable, Comparable(); + changesToCommit2 = new ArrayList(); hasSipAddress = false; } @@ -138,12 +140,12 @@ public class LinphoneContact implements Serializable, Comparable 0) { + public void save() { + if (isAndroidContact() && ContactsManager.getInstance().hasContactsAccess() && changesToCommit.size() > 0) { try { ContactsManager.getInstance().getContentResolver().applyBatch(ContactsContract.AUTHORITY, changesToCommit); + createLinphoneTagIfNeeded(); } catch (Exception e) { Log.e(e); } finally { changesToCommit = new ArrayList(); + changesToCommit2 = new ArrayList(); } } + if (isLinphoneFriend()) { boolean hasAddr = false; LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); @@ -387,17 +432,22 @@ public class LinphoneContact implements Serializable, Comparable getAddressesAndNumbersForAndroidContact(String id) { + private List getAddressesAndNumbersForAndroidContact() { List result = new ArrayList(); ContentResolver resolver = ContactsManager.getInstance().getContentResolver(); String select = ContactsContract.Data.CONTACT_ID + " =? AND (" + ContactsContract.Data.MIMETYPE + "=? OR " + ContactsContract.Data.MIMETYPE + "=?)"; String[] projection = new String[] { ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS, ContactsContract.Data.MIMETYPE }; // PHONE_NUMBER == SIP_ADDRESS == "data1"... - Cursor c = resolver.query(ContactsContract.Data.CONTENT_URI, projection, select, new String[]{ id, ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE }, null); + Cursor c = resolver.query(ContactsContract.Data.CONTENT_URI, projection, select, new String[]{ getAndroidId(), ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE }, null); if (c != null) { while (c.moveToNext()) { String mime = c.getString(c.getColumnIndex(ContactsContract.Data.MIMETYPE)); @@ -552,12 +602,14 @@ public class LinphoneContact implements Serializable, Comparable batch = new ArrayList(); + + batch.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, ContactsManager.getInstance().getString(R.string.sync_account_type)) + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, ContactsManager.getInstance().getString(R.string.sync_account_name)) + .withValue(ContactsContract.RawContacts.AGGREGATION_MODE, ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT) + .build()); + + batch.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, getFullName()) + .build()); + + batch.add(ContentProviderOperation.newUpdate(ContactsContract.AggregationExceptions.CONTENT_URI) + .withValue(ContactsContract.AggregationExceptions.TYPE, ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER) + .withValue(ContactsContract.AggregationExceptions.RAW_CONTACT_ID1, androidRawId) + .withValueBackReference(ContactsContract.AggregationExceptions.RAW_CONTACT_ID2, 0) + .build()); + + if (changesToCommit2.size() > 0) { + for(ContentProviderOperation cpo : changesToCommit2) { + batch.add(cpo); + } + } + + try { + ContactsManager.getInstance().getContentResolver().applyBatch(ContactsContract.AUTHORITY, batch); + androidTagId = findLinphoneRawContactId(); + } catch (Exception e) { + Log.e(e); + } + } } diff --git a/src/org/linphone/LinphoneLauncherActivity.java b/src/org/linphone/LinphoneLauncherActivity.java index d3a56d61e..a7fa589ba 100644 --- a/src/org/linphone/LinphoneLauncherActivity.java +++ b/src/org/linphone/LinphoneLauncherActivity.java @@ -20,7 +20,6 @@ package org.linphone; import static android.content.Intent.ACTION_MAIN; -import org.linphone.mediastream.Log; import org.linphone.assistant.RemoteProvisioningActivity; import org.linphone.tutorials.TutorialLauncherActivity; @@ -83,7 +82,6 @@ public class LinphoneLauncherActivity extends Activity { }, 1000); } - private class ServiceWaitThread extends Thread { public void run() { while (!LinphoneService.isReady()) { diff --git a/src/org/linphone/LinphoneManager.java b/src/org/linphone/LinphoneManager.java index d397904e8..cbeced18b 100644 --- a/src/org/linphone/LinphoneManager.java +++ b/src/org/linphone/LinphoneManager.java @@ -29,10 +29,12 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.Timer; import java.util.TimerTask; @@ -85,6 +87,7 @@ import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.BroadcastReceiver; import android.content.ContentResolver; +import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -110,6 +113,7 @@ import android.os.PowerManager.WakeLock; import android.os.Vibrator; import android.preference.CheckBoxPreference; import android.provider.MediaStore; +import android.provider.MediaStore.Images; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.telephony.TelephonyManager; @@ -354,6 +358,40 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag public void setUploadingImageStream(ByteArrayInputStream array){ this.mUploadingImageStream = array; } + + private void storeImage(LinphoneChatMessage msg) { + if (msg == null || msg.getFileTransferInformation() == null || msg.getAppData() == null) return; + File file = new File(Environment.getExternalStorageDirectory(), msg.getAppData()); + Bitmap bm = BitmapFactory.decodeFile(file.getPath()); + if (bm == null) return; + + ContentValues values = new ContentValues(); + values.put(Images.Media.TITLE, file.getName()); + String extension = msg.getFileTransferInformation().getSubtype(); + values.put(Images.Media.MIME_TYPE, "image/" + extension); + ContentResolver cr = getContext().getContentResolver(); + Uri path = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + + OutputStream stream; + try { + stream = cr.openOutputStream(path); + if (extension != null && extension.toLowerCase(Locale.getDefault()).equals("png")) { + bm.compress(Bitmap.CompressFormat.PNG, 100, stream); + } else { + bm.compress(Bitmap.CompressFormat.JPEG, 100, stream); + } + + stream.close(); + file.delete(); + bm.recycle(); + + msg.setAppData(path.toString()); + } catch (FileNotFoundException e) { + Log.e(e); + } catch (IOException e) { + Log.e(e); + } + } @Override public void onLinphoneChatMessageStateChanged(LinphoneChatMessage msg, LinphoneChatMessage.State state) { @@ -362,17 +400,7 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag mUploadPendingFileMessage = null; mUploadingImageStream = null; } else { - File file = new File(Environment.getExternalStorageDirectory(), msg.getAppData()); - try { - Bitmap bm = BitmapFactory.decodeFile(file.getPath()); - if (bm != null) { - String url = MediaStore.Images.Media.insertImage(getContext().getContentResolver(), file.getPath(), file.getName(), null); - msg.setAppData(url); - file.delete(); - } - } catch (FileNotFoundException e) { - Log.e(e); - } + storeImage(msg); removePendingMessage(msg); } } @@ -862,13 +890,16 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag mLastNetworkType=curtype; } } + + if (mLc.isNetworkReachable()) { + // When network isn't available, push informations might not be set. This should fix the issue. + LinphonePreferences prefs = LinphonePreferences.instance(); + prefs.setPushNotificationEnabled(prefs.isPushNotificationEnabled()); + } } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private void doDestroy() { - if (LinphoneService.isReady()) // indeed, no need to crash - ChatStorage.getInstance().close(); - BluetoothManager.getInstance().destroy(); try { mTimer.cancel(); @@ -943,13 +974,6 @@ public class LinphoneManager implements LinphoneCoreListener, LinphoneChatMessag LinphoneAddress from = message.getFrom(); String textMessage = message.getText(); - String url = message.getExternalBodyUrl(); - if (textMessage != null && textMessage.length() > 0) { - ChatStorage.getInstance().saveTextMessage(from.asStringUriOnly(), "", textMessage, message.getTime()); - } else if (url != null && url.length() > 0) { - ChatStorage.getInstance().saveImageMessage(from.asStringUriOnly(), "", null, message.getExternalBodyUrl(), message.getTime()); - } - try { LinphoneContact contact = ContactsManager.getInstance().findContactFromAddress(from); if (!mServiceContext.getResources().getBoolean(R.bool.disable_chat_message_notification)) { diff --git a/src/org/linphone/LinphonePreferences.java b/src/org/linphone/LinphonePreferences.java index 8c8395074..52f44440c 100644 --- a/src/org/linphone/LinphonePreferences.java +++ b/src/org/linphone/LinphonePreferences.java @@ -19,12 +19,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -import java.util.ArrayList; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.util.ArrayList; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneAddress.TransportType; @@ -32,6 +32,7 @@ import org.linphone.core.LinphoneAuthInfo; import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCore.AdaptiveRateAlgorithm; import org.linphone.core.LinphoneCore.FirewallPolicy; +import org.linphone.core.LinphoneCore.LinphoneLimeState; import org.linphone.core.LinphoneCore.MediaEncryption; import org.linphone.core.LinphoneCore.Transports; import org.linphone.core.LinphoneCoreException; @@ -1017,7 +1018,7 @@ public class LinphonePreferences { lpc.edit(); lpc.setContactUriParameters(contactInfos); lpc.done(); - Log.d("Push notif infos added to proxy config"); + Log.d("Push notif infos added to proxy config " + lpc.getAddress().asStringUriOnly()); } lc.refreshRegisters(); } @@ -1027,7 +1028,7 @@ public class LinphonePreferences { lpc.edit(); lpc.setContactUriParameters(null); lpc.done(); - Log.d("Push notif infos removed from proxy config"); + Log.d("Push notif infos removed from proxy config " + lpc.getAddress().asStringUriOnly()); } lc.refreshRegisters(); } @@ -1287,46 +1288,6 @@ public class LinphonePreferences { return getConfig().getString("app", "debug_popup_magic", null); } - public Boolean audioPermAsked(){ - return getConfig().getBool("app", "audio_perm", false); - } - - public void neverAskAudioPerm(){ - getConfig().setBool("app", "audio_perm", true); - } - - public Boolean cameraPermAsked(){ - return getConfig().getBool("app", "camera_perm", false); - } - - public void neverAskCameraPerm(){ - getConfig().setBool("app", "camera_perm", true); - } - - public Boolean readContactsPermAsked(){ - return getConfig().getBool("app", "read_contacts_perm", false); - } - - public void neverAskReadContactsPerm(){ - getConfig().setBool("app", "read_contacts_perm", true); - } - - public Boolean writeContactsPermAsked(){ - return getConfig().getBool("app", "write_contacts_perm", false); - } - - public void neverAskWriteContactsPerm(){ - getConfig().setBool("app", "write_contacts_perm", true); - } - - public Boolean writeExternalStoragePermAsked(){ - return getConfig().getBool("app", "write_external_storage_perm", false); - } - - public void neverAskWriteExternalStoragePerm(){ - getConfig().setBool("app", "write_external_storage_perm", true); - } - public String getActivityToLaunchOnIncomingReceived() { return getConfig().getString("app", "incoming_call_activity", "org.linphone.LinphoneActivity"); } @@ -1334,4 +1295,36 @@ public class LinphonePreferences { public void setActivityToLaunchOnIncomingReceived(String name) { getConfig().setString("app", "incoming_call_activity", name); } + + public boolean getServiceNotificationVisibility() { + return getConfig().getBool("app", "show_service_notification", true); + } + + public void setServiceNotificationVisibility(boolean enable) { + getConfig().setBool("app", "show_service_notification", enable); + } + + public boolean isOverlayEnabled() { + return getConfig().getBool("app", "display_overlay", false); + } + + public void enableOverlay(boolean enable) { + getConfig().setBool("app", "display_overlay", enable); + } + + public LinphoneLimeState getLimeEncryption() { + return getLc().getLimeEncryption(); + } + + public void setLimeEncryption(LinphoneLimeState lime) { + getLc().setLimeEncryption(lime); + } + + public boolean firstTimeAskingForPermission(String permission) { + boolean firstTime = getConfig().getBool("app", permission, true); + if (firstTime) { + getConfig().setBool("app", permission, false); + } + return firstTime; + } } diff --git a/src/org/linphone/LinphoneService.java b/src/org/linphone/LinphoneService.java index 47dedb3bc..07c468ba0 100644 --- a/src/org/linphone/LinphoneService.java +++ b/src/org/linphone/LinphoneService.java @@ -34,6 +34,7 @@ import org.linphone.core.LinphoneCoreListenerBase; import org.linphone.core.LinphoneProxyConfig; import org.linphone.mediastream.Log; import org.linphone.mediastream.Version; +import org.linphone.ui.LinphoneOverlay; import android.annotation.TargetApi; import android.app.Activity; @@ -46,7 +47,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; -import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; @@ -56,6 +56,7 @@ import android.os.IBinder; import android.os.SystemClock; import android.provider.ContactsContract; import android.provider.MediaStore; +import android.view.WindowManager; /** * @@ -118,6 +119,8 @@ public final class LinphoneService extends Service { private boolean mDisableRegistrationStatus; private LinphoneCoreListenerBase mListener; public static int notifcationsPriority = (Version.sdkAboveOrEqual(Version.API16_JELLY_BEAN_41) ? Notification.PRIORITY_MIN : 0); + private WindowManager mWindowManager; + private LinphoneOverlay mOverlay; public int getMessageNotifCount() { return mMsgNotifCount; @@ -126,6 +129,31 @@ public final class LinphoneService extends Service { public void resetMessageNotifCount() { mMsgNotifCount = 0; } + + private boolean displayServiceNotification() { + return LinphonePreferences.instance().getServiceNotificationVisibility(); + } + + public void showServiceNotification() { + startForegroundCompat(NOTIF_ID, mNotif); + + LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); + if (lc == null) return; + LinphoneProxyConfig lpc = lc.getDefaultProxyConfig(); + if (lpc != null) { + if (lpc.isRegistered()) { + sendNotification(IC_LEVEL_ORANGE, R.string.notification_registered); + } else { + sendNotification(IC_LEVEL_ORANGE, R.string.notification_register_failure); + } + } else { + sendNotification(IC_LEVEL_ORANGE, R.string.notification_started); + } + } + + public void hideServiceNotification() { + stopForegroundCompat(NOTIF_ID); + } @SuppressWarnings("unchecked") @Override @@ -182,6 +210,10 @@ public final class LinphoneService extends Service { if (state == LinphoneCall.State.IncomingReceived) { onIncomingReceived(); } + + if (state == State.CallEnd || state == State.CallReleased || state == State.Error) { + destroyOverlay(); + } if (state == State.StreamsRunning) { // Workaround bug current call seems to be updated after state changed to streams running @@ -195,7 +227,7 @@ public final class LinphoneService extends Service { @Override public void globalState(LinphoneCore lc,LinphoneCore.GlobalState state, String message) { - if (state == GlobalState.GlobalOn) { + if (state == GlobalState.GlobalOn && displayServiceNotification()) { sendNotification(IC_LEVEL_ORANGE, R.string.notification_started); } } @@ -207,15 +239,15 @@ public final class LinphoneService extends Service { // return; // } if (!mDisableRegistrationStatus) { - if (state == RegistrationState.RegistrationOk && LinphoneManager.getLc().getDefaultProxyConfig() != null && LinphoneManager.getLc().getDefaultProxyConfig().isRegistered()) { + if (displayServiceNotification() && state == RegistrationState.RegistrationOk && LinphoneManager.getLc().getDefaultProxyConfig() != null && LinphoneManager.getLc().getDefaultProxyConfig().isRegistered()) { sendNotification(IC_LEVEL_ORANGE, R.string.notification_registered); } - if ((state == RegistrationState.RegistrationFailed || state == RegistrationState.RegistrationCleared) && (LinphoneManager.getLc().getDefaultProxyConfig() == null || !LinphoneManager.getLc().getDefaultProxyConfig().isRegistered())) { + if (displayServiceNotification() && (state == RegistrationState.RegistrationFailed || state == RegistrationState.RegistrationCleared) && (LinphoneManager.getLc().getDefaultProxyConfig() == null || !LinphoneManager.getLc().getDefaultProxyConfig().isRegistered())) { sendNotification(IC_LEVEL_ORANGE, R.string.notification_register_failure); } - if (state == RegistrationState.RegistrationNone) { + if (displayServiceNotification() && state == RegistrationState.RegistrationNone) { sendNotification(IC_LEVEL_ORANGE, R.string.notification_started); } } @@ -241,7 +273,9 @@ public final class LinphoneService extends Service { getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, ContactsManager.getInstance()); - startForegroundCompat(NOTIF_ID, mNotif); + if (displayServiceNotification()) { + startForegroundCompat(NOTIF_ID, mNotif); + } if (!mTestDelayElapsed) { // Only used when testing. Simulates a 5 seconds delay for launching service @@ -259,8 +293,30 @@ public final class LinphoneService extends Service { , SystemClock.elapsedRealtime()+600000 , 600000 , mkeepAlivePendingIntent); - } + mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); + } + + public void createOverlay() { + if (mOverlay != null) destroyOverlay(); + + LinphoneCall call = LinphoneManager.getLc().getCurrentCall(); + if (call == null || !call.getCurrentParamsCopy().getVideoEnabled()) return; + + mOverlay = new LinphoneOverlay(this); + WindowManager.LayoutParams params = mOverlay.getWindowManagerLayoutParams(); + params.x = 0; + params.y = 0; + mWindowManager.addView(mOverlay, params); + } + + public void destroyOverlay() { + if (mOverlay != null) { + mWindowManager.removeViewImmediate(mOverlay); + mOverlay.destroy(); + } + mOverlay = null; + } private enum IncallIconState {INCALL, PAUSE, VIDEO, IDLE} private IncallIconState mCurrentIncallIconState = IncallIconState.IDLE; @@ -505,7 +561,7 @@ public final class LinphoneService extends Service { mDisableRegistrationStatus = true; } - public synchronized void sendNotification(int level, int textId) { + private synchronized void sendNotification(int level, int textId) { String text = getString(textId); if (text.contains("%s") && LinphoneManager.getLc() != null) { // Test for null lc is to avoid a NPE when Android mess up badly with the String resources. @@ -559,6 +615,7 @@ public final class LinphoneService extends Service { @Override public synchronized void onDestroy() { + destroyOverlay(); LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); if (lc != null) { lc.removeListener(mListener); @@ -593,10 +650,10 @@ public final class LinphoneService extends Service { Intent notifIntent = new Intent(this, incomingReceivedActivity); mNotifContentIntent = PendingIntent.getActivity(this, 0, notifIntent, PendingIntent.FLAG_UPDATE_CURRENT); - if (mNotif != null) { + /*if (mNotif != null) { mNotif.contentIntent = mNotifContentIntent; } - notifyWrapper(NOTIF_ID, mNotif); + notifyWrapper(NOTIF_ID, mNotif);*/ } protected void onIncomingReceived() { diff --git a/src/org/linphone/LinphoneUtils.java b/src/org/linphone/LinphoneUtils.java index 8d3668418..42661832b 100644 --- a/src/org/linphone/LinphoneUtils.java +++ b/src/org/linphone/LinphoneUtils.java @@ -455,5 +455,14 @@ public final class LinphoneUtils { Log.e(e); } } + + public static String getExtensionFromFileName(String fileName) { + String extension = null; + int i = fileName.lastIndexOf('.'); + if (i > 0) { + extension = fileName.substring(i+1); + } + return extension; + } } diff --git a/src/org/linphone/PhoneStateChangedReceiver.java b/src/org/linphone/PhoneStateChangedReceiver.java index 26452669f..226f6ae33 100644 --- a/src/org/linphone/PhoneStateChangedReceiver.java +++ b/src/org/linphone/PhoneStateChangedReceiver.java @@ -18,8 +18,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone; -import org.linphone.mediastream.Log; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -32,26 +30,18 @@ import android.telephony.TelephonyManager; * */ public class PhoneStateChangedReceiver extends BroadcastReceiver { - @Override public void onReceive(Context context, Intent intent) { - - final String extraState = intent.getStringExtra(TelephonyManager.EXTRA_STATE); if (TelephonyManager.EXTRA_STATE_RINGING.equals(extraState) || TelephonyManager.EXTRA_STATE_OFFHOOK.equals(extraState)) { LinphoneManager.setGsmIdle(false); if (!LinphoneManager.isInstanciated()) { - Log.i("GSM call state changed but manager not instantiated"); return; } LinphoneManager.getLc().pauseAllCalls(); } else if (TelephonyManager.EXTRA_STATE_IDLE.equals(extraState)) { LinphoneManager.setGsmIdle(true); } - - - // do nothing } - } diff --git a/src/org/linphone/SettingsFragment.java b/src/org/linphone/SettingsFragment.java index 14aa5661a..652a9ed54 100644 --- a/src/org/linphone/SettingsFragment.java +++ b/src/org/linphone/SettingsFragment.java @@ -19,12 +19,14 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +import java.io.File; import java.util.ArrayList; import java.util.List; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCore.EcCalibratorStatus; +import org.linphone.core.LinphoneCore.LinphoneLimeState; import org.linphone.core.LinphoneCore.MediaEncryption; import org.linphone.core.LinphoneCoreException; import org.linphone.core.LinphoneCoreListenerBase; @@ -37,10 +39,10 @@ import org.linphone.tools.OpenH264DownloadHelper; import org.linphone.purchase.InAppPurchaseActivity; import org.linphone.ui.LedPreference; import org.linphone.ui.PreferencesListFragment; -import android.content.Intent; import android.app.AlertDialog; import android.content.DialogInterface; +import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.preference.CheckBoxPreference; @@ -56,21 +58,19 @@ import android.preference.PreferenceScreen; * @author Sylvain Berfini */ public class SettingsFragment extends PreferencesListFragment { - private static final int WIZARD_INTENT = 1; private static final int STORE_INTENT = 2; private LinphonePreferences mPrefs; private Handler mHandler = new Handler(); private LinphoneCoreListenerBase mListener; - public SettingsFragment() { - super(R.xml.preferences); - mPrefs = LinphonePreferences.instance(); - } - @Override public void onCreate(Bundle bundle) { super.onCreate(bundle); + mPrefs = LinphonePreferences.instance(); + removePreviousPreferencesFile(); // Required when updating the preferences order + addPreferencesFromResource(R.xml.preferences); + // Init the settings page interface initSettings(); setListeners(); @@ -100,6 +100,11 @@ public class SettingsFragment extends PreferencesListFragment { } }; } + + private void removePreviousPreferencesFile() { + File dir = new File(LinphoneActivity.instance().getFilesDir().getAbsolutePath() + "shared_prefs"); + dir.delete(); + } // Inits the values or the listener on some settings private void initSettings() { @@ -107,6 +112,7 @@ public class SettingsFragment extends PreferencesListFragment { initAudioSettings(); initVideoSettings(); initCallSettings(); + initChatSettings(); initNetworkSettings(); initAdvancedSettings(); @@ -134,6 +140,7 @@ public class SettingsFragment extends PreferencesListFragment { setAudioPreferencesListener(); setVideoPreferencesListener(); setCallPreferencesListener(); + setChatPreferencesListener(); setNetworkPreferencesListener(); setAdvancedPreferencesListener(); } @@ -436,6 +443,30 @@ public class SettingsFragment extends PreferencesListFragment { pref.setSummary(value); pref.setValue(value); } + + private void initLimeEncryptionPreference(ListPreference pref) { + List entries = new ArrayList(); + List values = new ArrayList(); + entries.add(getString(R.string.lime_encryption_entry_disabled)); + values.add(LinphoneLimeState.Disabled.toString()); + + LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); + if (lc == null || !lc.isLimeEncryptionAvailable()) { + setListPreferenceValues(pref, entries, values); + pref.setEnabled(false); + return; + } + + entries.add(getString(R.string.lime_encryption_entry_mandatory)); + values.add(LinphoneLimeState.Mandatory.toString()); + entries.add(getString(R.string.lime_encryption_entry_preferred)); + values.add(LinphoneLimeState.Preferred.toString()); + setListPreferenceValues(pref, entries, values); + + LinphoneLimeState lime = mPrefs.getLimeEncryption(); + pref.setSummary(lime.toString()); + pref.setValue(lime.toString()); + } private static void setListPreferenceValues(ListPreference pref, List entries, List values) { CharSequence[] contents = new CharSequence[entries.size()]; @@ -639,6 +670,7 @@ public class SettingsFragment extends PreferencesListFragment { ((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()); ((CheckBoxPreference) findPreference(getString(R.string.pref_video_automatically_accept_video_key))).setChecked(mPrefs.shouldAutomaticallyAcceptVideoRequests()); + ((CheckBoxPreference) findPreference(getString(R.string.pref_overlay_key))).setChecked(mPrefs.isOverlayEnabled()); } private void updateVideoPreferencesAccordingToPreset() { @@ -732,6 +764,21 @@ public class SettingsFragment extends PreferencesListFragment { return true; } }); + + findPreference(getString(R.string.pref_overlay_key)).setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + boolean enable = (Boolean) newValue; + if (enable) { + if (LinphoneActivity.instance().checkAndRequestOverlayPermission()) { + mPrefs.enableOverlay(true); + } + } else { + mPrefs.enableOverlay(false); + } + return true; + } + }); } private void initCallSettings() { @@ -788,6 +835,47 @@ public class SettingsFragment extends PreferencesListFragment { }); } + private void initChatSettings() { + setPreferenceDefaultValueAndSummary(R.string.pref_image_sharing_server_key, mPrefs.getSharingPictureServerUrl()); + initLimeEncryptionPreference((ListPreference) findPreference(getString(R.string.pref_use_lime_encryption_key))); + } + + private void setChatPreferencesListener() { + findPreference(getString(R.string.pref_image_sharing_server_key)).setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + String value = (String) newValue; + mPrefs.setSharingPictureServerUrl(value); + preference.setSummary(value); + return true; + } + }); + + findPreference(getString(R.string.pref_use_lime_encryption_key)).setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + String value = newValue.toString(); + LinphoneLimeState lime = LinphoneLimeState.Disabled; + if (value.equals(LinphoneLimeState.Mandatory.toString())) + lime = LinphoneLimeState.Mandatory; + else if (value.equals(LinphoneLimeState.Preferred.toString())) + lime = LinphoneLimeState.Preferred; + mPrefs.setLimeEncryption(lime); + + lime = mPrefs.getLimeEncryption(); + if (lime == LinphoneLimeState.Disabled) { + preference.setSummary(getString(R.string.lime_encryption_entry_disabled)); + } else if (lime == LinphoneLimeState.Mandatory) { + preference.setSummary(getString(R.string.lime_encryption_entry_mandatory)); + } else if (lime == LinphoneLimeState.Preferred) { + preference.setSummary(getString(R.string.lime_encryption_entry_preferred)); + } + + return true; + } + }); + } + private void initNetworkSettings() { initMediaEncryptionPreference((ListPreference) findPreference(getString(R.string.pref_media_encryption_key))); @@ -929,8 +1017,8 @@ public class SettingsFragment extends PreferencesListFragment { ((CheckBoxPreference)findPreference(getString(R.string.pref_debug_key))).setChecked(mPrefs.isDebugEnabled()); ((CheckBoxPreference)findPreference(getString(R.string.pref_background_mode_key))).setChecked(mPrefs.isBackgroundModeEnabled()); ((CheckBoxPreference)findPreference(getString(R.string.pref_animation_enable_key))).setChecked(mPrefs.areAnimationsEnabled()); + ((CheckBoxPreference)findPreference(getString(R.string.pref_service_notification_key))).setChecked(mPrefs.getServiceNotificationVisibility()); ((CheckBoxPreference)findPreference(getString(R.string.pref_autostart_key))).setChecked(mPrefs.isAutoStartEnabled()); - setPreferenceDefaultValueAndSummary(R.string.pref_image_sharing_server_key, mPrefs.getSharingPictureServerUrl()); setPreferenceDefaultValueAndSummary(R.string.pref_remote_provisioning_key, mPrefs.getRemoteProvisioningUrl()); setPreferenceDefaultValueAndSummary(R.string.pref_display_name_key, mPrefs.getDefaultDisplayName()); setPreferenceDefaultValueAndSummary(R.string.pref_user_name_key, mPrefs.getDefaultUsername()); @@ -964,6 +1052,20 @@ public class SettingsFragment extends PreferencesListFragment { } }); + findPreference(getString(R.string.pref_service_notification_key)).setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + boolean value = (Boolean) newValue; + mPrefs.setServiceNotificationVisibility(value); + if (value) { + LinphoneService.instance().showServiceNotification(); + } else { + LinphoneService.instance().hideServiceNotification(); + } + return true; + } + }); + findPreference(getString(R.string.pref_autostart_key)).setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { @@ -973,16 +1075,6 @@ public class SettingsFragment extends PreferencesListFragment { } }); - findPreference(getString(R.string.pref_image_sharing_server_key)).setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - String value = (String) newValue; - mPrefs.setSharingPictureServerUrl(value); - preference.setSummary(value); - return true; - } - }); - findPreference(getString(R.string.pref_remote_provisioning_key)).setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { diff --git a/src/org/linphone/assistant/AssistantActivity.java b/src/org/linphone/assistant/AssistantActivity.java index 505cc2fd6..a7636db01 100644 --- a/src/org/linphone/assistant/AssistantActivity.java +++ b/src/org/linphone/assistant/AssistantActivity.java @@ -44,6 +44,7 @@ import android.app.FragmentTransaction; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.graphics.drawable.ColorDrawable; @@ -200,7 +201,8 @@ private static AssistantActivity instance; if (getResources().getBoolean(R.bool.setup_cancel_move_to_back)) { moveTaskToBack(true); } else { - setResult(Activity.RESULT_CANCELED); + LinphonePreferences.instance().firstLaunchSuccessful(); + startActivity(new Intent().setClass(this, LinphoneActivity.class)); finish(); } } else if (id == R.id.back) { @@ -216,7 +218,8 @@ private static AssistantActivity instance; if (getResources().getBoolean(R.bool.setup_cancel_move_to_back)) { moveTaskToBack(true); } else { - setResult(Activity.RESULT_CANCELED); + LinphonePreferences.instance().firstLaunchSuccessful(); + startActivity(new Intent().setClass(this, LinphoneActivity.class)); finish(); } } else if (currentFragment == AssistantFragmentsEnum.LOGIN @@ -241,8 +244,12 @@ private static AssistantActivity instance; } public void checkAndRequestAudioPermission() { - if (getPackageManager().checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName()) != PackageManager.PERMISSION_GRANTED) { - if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) { + int recordAudio = getPackageManager().checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName()); + Log.i("[Permission] Record audio permission is " + (recordAudio == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + + if (recordAudio != PackageManager.PERMISSION_GRANTED) { + if (LinphonePreferences.instance().firstTimeAskingForPermission(Manifest.permission.RECORD_AUDIO) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) { + Log.i("[Permission] Asking for record audio"); ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, PERMISSIONS_REQUEST_RECORD_AUDIO); } } @@ -250,8 +257,12 @@ private static AssistantActivity instance; @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + for (int i = 0; i < permissions.length; i++) { + Log.i("[Permission] " + permissions[i] + " is " + (grantResults[i] == PackageManager.PERMISSION_GRANTED ? "granted" : "denied")); + } + if (requestCode == PERMISSIONS_REQUEST_RECORD_AUDIO) { - if (getPackageManager().checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName()) == PackageManager.PERMISSION_GRANTED) { + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { launchEchoCancellerCalibration(true); } else { isEchoCalibrationFinished(); @@ -541,10 +552,7 @@ private static AssistantActivity instance; public void success() { mPrefs.firstLaunchSuccessful(); - if(LinphoneActivity.instance() != null) { - LinphoneActivity.instance().isNewProxyConfig(); - setResult(Activity.RESULT_OK); - } + startActivity(new Intent().setClass(this, LinphoneActivity.class).putExtra("isNewProxyConfig", true)); finish(); } diff --git a/src/org/linphone/compatibility/ApiElevenPlus.java b/src/org/linphone/compatibility/ApiElevenPlus.java index d967bc6d3..d8c0a1fe4 100644 --- a/src/org/linphone/compatibility/ApiElevenPlus.java +++ b/src/org/linphone/compatibility/ApiElevenPlus.java @@ -69,6 +69,7 @@ public class ApiElevenPlus { | Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE) .setWhen(System.currentTimeMillis()) + .setNumber(msgCount) .setLargeIcon(contactIcon).getNotification(); return notif; diff --git a/src/org/linphone/compatibility/ApiNinePlus.java b/src/org/linphone/compatibility/ApiNinePlus.java index 70dd126cf..80c49202e 100644 --- a/src/org/linphone/compatibility/ApiNinePlus.java +++ b/src/org/linphone/compatibility/ApiNinePlus.java @@ -3,12 +3,9 @@ package org.linphone.compatibility; import java.util.ArrayList; import java.util.List; -import org.linphone.Contact; import org.linphone.LinphoneContact; -import org.linphone.LinphoneUtils; import org.linphone.R; import org.linphone.core.LinphoneAddress; -import org.linphone.mediastream.Log; import android.annotation.TargetApi; import android.content.ContentProviderOperation; @@ -195,89 +192,4 @@ public class ApiNinePlus { cursor.close(); return null; } - - //Linphone Contacts Tag - public static void addLinphoneContactTag(Context context, ArrayList ops, String newAddress, String rawContactId){ - if(rawContactId != null) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId) - .withValue(ContactsContract.Data.MIMETYPE, context.getString(R.string.sync_mimetype)) - .withValue(ContactsContract.Data.DATA1, newAddress) - .withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name)) - .withValue(ContactsContract.Data.DATA3, newAddress) - .build() - ); - } - } - public static void updateLinphoneContactTag(Context context, ArrayList ops, String newAddress, String oldAddress, String rawContactId){ - if(rawContactId != null) { - ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) - .withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.DATA1 + "=? ", new String[]{rawContactId, oldAddress}) - .withValue(ContactsContract.Data.DATA1, newAddress) - .withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name)) - .withValue(ContactsContract.Data.DATA3, newAddress) - .build()); - } - } - - public static void deleteLinphoneContactTag(ArrayList ops , String oldAddress, String rawContactId){ - if(rawContactId != null) { - String select = ContactsContract.Data.RAW_CONTACT_ID + "=? AND " - + ContactsContract.Data.DATA1 + "= ?"; - String[] args = new String[]{rawContactId, oldAddress}; - - ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) - .withSelection(select, args) - .build()); - } - } - - public static void createLinphoneContactTag(Context context, ContentResolver contentResolver, Contact contact, String rawContactId){ - ArrayList ops = new ArrayList(); - - if (contact != null) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) - .withValue(ContactsContract.RawContacts.AGGREGATION_MODE, ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT) - .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, context.getString(R.string.sync_account_type)) - .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, context.getString(R.string.sync_account_name)) - .build() - ); - - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, contact.getName()) - .build() - ); - - List numbersOrAddresses = contact.getNumbersOrAddresses(); - for (String numberOrAddress : numbersOrAddresses) { - if (LinphoneUtils.isSipAddress(numberOrAddress)) { - if (numberOrAddress.startsWith("sip:")){ - numberOrAddress = numberOrAddress.substring(4); - } - - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, context.getString(R.string.sync_mimetype)) - .withValue(ContactsContract.Data.DATA1, numberOrAddress) - .withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name)) - .withValue(ContactsContract.Data.DATA3, numberOrAddress) - .build() - ); - } - } - - ops.add(ContentProviderOperation.newUpdate(ContactsContract.AggregationExceptions.CONTENT_URI) - .withValue(ContactsContract.AggregationExceptions.TYPE, ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER) - .withValue(ContactsContract.AggregationExceptions.RAW_CONTACT_ID1, rawContactId) - .withValueBackReference(ContactsContract.AggregationExceptions.RAW_CONTACT_ID2, 0).build()); - - try { - contentResolver.applyBatch(ContactsContract.AUTHORITY, ops); - } catch (Exception e) { - Log.e(e); - } - } - } } diff --git a/src/org/linphone/compatibility/ApiSixteenPlus.java b/src/org/linphone/compatibility/ApiSixteenPlus.java index ac9ccd86d..a97cb04e1 100644 --- a/src/org/linphone/compatibility/ApiSixteenPlus.java +++ b/src/org/linphone/compatibility/ApiSixteenPlus.java @@ -56,6 +56,7 @@ public class ApiSixteenPlus { | Notification.DEFAULT_VIBRATE) .setWhen(System.currentTimeMillis()) .setLargeIcon(contactIcon) + .setNumber(msgCount) .build(); return notif; diff --git a/src/org/linphone/compatibility/ApiTwentyOnePlus.java b/src/org/linphone/compatibility/ApiTwentyOnePlus.java index e1a842b4c..8247caa5f 100644 --- a/src/org/linphone/compatibility/ApiTwentyOnePlus.java +++ b/src/org/linphone/compatibility/ApiTwentyOnePlus.java @@ -54,6 +54,7 @@ public class ApiTwentyOnePlus { .setCategory(Notification.CATEGORY_MESSAGE) .setVisibility(Notification.VISIBILITY_PRIVATE) .setPriority(Notification.PRIORITY_HIGH) + .setNumber(msgCount) .build(); return notif; diff --git a/src/org/linphone/compatibility/Compatibility.java b/src/org/linphone/compatibility/Compatibility.java index da9f9b934..9a6f1ec5a 100644 --- a/src/org/linphone/compatibility/Compatibility.java +++ b/src/org/linphone/compatibility/Compatibility.java @@ -21,7 +21,6 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; -import org.linphone.Contact; import org.linphone.LinphoneContact; import org.linphone.core.LinphoneAddress; import org.linphone.mediastream.Version; @@ -38,6 +37,7 @@ import android.graphics.Bitmap; import android.media.AudioManager; import android.net.Uri; import android.preference.Preference; +import android.provider.Settings; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnGlobalLayoutListener; /** @@ -300,32 +300,6 @@ public class Compatibility { ApiFivePlus.deleteSipAddressFromContact(ops, oldSipAddress, contactID); } - //Linphone Contacts Tag - public static void addLinphoneContactTag(Context context, ArrayList ops, String newSipAddress, String rawContactId) { - if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) { - ApiNinePlus.addLinphoneContactTag(context, ops, newSipAddress, rawContactId); - } - } - - public static void updateLinphoneContactTag(Context context, ArrayList ops, String newSipAddress, String oldSipAddress, String rawContactId) { - if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) { - ApiNinePlus.updateLinphoneContactTag(context, ops, newSipAddress, oldSipAddress, rawContactId); - } - } - - public static void deleteLinphoneContactTag(ArrayList ops, String oldSipAddress, String rawContactId) { - if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) { - ApiNinePlus.deleteLinphoneContactTag(ops, oldSipAddress, rawContactId); - } - } - - public static void createLinphoneContactTag(Context context, ContentResolver contentResolver, Contact contact, String rawContactId) { - if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) { - ApiNinePlus.createLinphoneContactTag(context, contentResolver, contact, rawContactId); - } - } - //End of Linphone Contact Tag - public static void removeGlobalLayoutListener(ViewTreeObserver viewTreeObserver, OnGlobalLayoutListener keyboardListener) { if (Version.sdkAboveOrEqual(Version.API16_JELLY_BEAN_41)) { ApiSixteenPlus.removeGlobalLayoutListener(viewTreeObserver, keyboardListener); @@ -363,4 +337,11 @@ public class Compatibility { return ApiEightPlus.getAudioManagerEventForBluetoothConnectionStateChangedEvent(); } } + + public static boolean canDrawOverlays(Context context) { + if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) { + return Settings.canDrawOverlays(context); + } + return true; + } } diff --git a/src/org/linphone/gcm/GCMService.java b/src/org/linphone/gcm/GCMService.java index 59906ef77..7d6c24936 100644 --- a/src/org/linphone/gcm/GCMService.java +++ b/src/org/linphone/gcm/GCMService.java @@ -25,6 +25,7 @@ import org.linphone.LinphonePreferences; import org.linphone.LinphoneService; import org.linphone.R; import org.linphone.UIThreadDispatcher; +import org.linphone.core.LinphoneCoreFactory; import org.linphone.mediastream.Log; import android.content.Context; @@ -44,12 +45,19 @@ public class GCMService extends GCMBaseIntentService { @Override protected void onError(Context context, String errorId) { + boolean isDebugEnabled = LinphonePreferences.instance().isDebugEnabled(); + LinphoneCoreFactory.instance().enableLogCollection(isDebugEnabled); + LinphoneCoreFactory.instance().setDebugMode(isDebugEnabled, context.getString(R.string.app_name)); Log.e("Error while registering push notification : " + errorId); } @Override protected void onMessage(Context context, Intent intent) { + boolean isDebugEnabled = LinphonePreferences.instance().isDebugEnabled(); + LinphoneCoreFactory.instance().enableLogCollection(isDebugEnabled); + LinphoneCoreFactory.instance().setDebugMode(isDebugEnabled, context.getString(R.string.app_name)); Log.d("Push notification received"); + if (!LinphoneService.isReady()) { startService(new Intent(ACTION_MAIN).setClass(this, LinphoneService.class)); } else if (LinphoneManager.isInstanciated() && LinphoneManager.getLc().getCallsNb() == 0) { @@ -68,13 +76,21 @@ public class GCMService extends GCMBaseIntentService { @Override protected void onRegistered(Context context, String regId) { + boolean isDebugEnabled = LinphonePreferences.instance().isDebugEnabled(); + LinphoneCoreFactory.instance().enableLogCollection(isDebugEnabled); + LinphoneCoreFactory.instance().setDebugMode(isDebugEnabled, context.getString(R.string.app_name)); Log.d("Registered push notification : " + regId); + LinphonePreferences.instance().setPushNotificationRegistrationID(regId); } @Override protected void onUnregistered(Context context, String regId) { + boolean isDebugEnabled = LinphonePreferences.instance().isDebugEnabled(); + LinphoneCoreFactory.instance().enableLogCollection(isDebugEnabled); + LinphoneCoreFactory.instance().setDebugMode(isDebugEnabled, context.getString(R.string.app_name)); Log.w("Unregistered push notification : " + regId); + LinphonePreferences.instance().setPushNotificationRegistrationID(null); } diff --git a/src/org/linphone/ui/BubbleChat.java b/src/org/linphone/ui/BubbleChat.java index 50b3d4ea6..712c31933 100644 --- a/src/org/linphone/ui/BubbleChat.java +++ b/src/org/linphone/ui/BubbleChat.java @@ -173,7 +173,8 @@ public class BubbleChat implements LinphoneChatMessage.LinphoneChatMessageListen public void onClick(View v) { if (mContext.getPackageManager().checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, mContext.getPackageName()) == PackageManager.PERMISSION_GRANTED) { v.setEnabled(false); - String filename = context.getString(R.string.temp_photo_name_with_date).replace("%s", String.valueOf(System.currentTimeMillis())); + 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); diff --git a/src/org/linphone/ui/LinphoneOverlay.java b/src/org/linphone/ui/LinphoneOverlay.java new file mode 100644 index 000000000..6cb15ac0d --- /dev/null +++ b/src/org/linphone/ui/LinphoneOverlay.java @@ -0,0 +1,119 @@ +package org.linphone.ui; + +import org.linphone.LinphoneActivity; +import org.linphone.LinphoneManager; +import org.linphone.LinphoneService; +import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneCallParams; +import org.linphone.mediastream.video.AndroidVideoWindowImpl; + +import android.content.Context; +import android.content.Intent; +import android.graphics.PixelFormat; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.SurfaceView; +import android.view.View; +import android.view.WindowManager; + +public class LinphoneOverlay extends org.linphone.mediastream.video.display.GL2JNIView { + private WindowManager wm; + private WindowManager.LayoutParams params; + private DisplayMetrics metrics; + private float x; + private float y; + private float touchX; + private float touchY; + private AndroidVideoWindowImpl androidVideoWindowImpl; + + public LinphoneOverlay(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs); + wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + params = new WindowManager.LayoutParams( + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.TYPE_PHONE, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSLUCENT); + params.gravity = Gravity.TOP | Gravity.LEFT; + metrics = new DisplayMetrics(); + wm.getDefaultDisplay().getMetrics(metrics); + + androidVideoWindowImpl = new AndroidVideoWindowImpl(this, null, new AndroidVideoWindowImpl.VideoWindowListener() { + public void onVideoRenderingSurfaceReady(AndroidVideoWindowImpl vw, SurfaceView surface) { + LinphoneManager.getLc().setVideoWindow(vw); + } + + public void onVideoRenderingSurfaceDestroyed(AndroidVideoWindowImpl vw) { + + } + + public void onVideoPreviewSurfaceReady(AndroidVideoWindowImpl vw, SurfaceView surface) { + } + + public void onVideoPreviewSurfaceDestroyed(AndroidVideoWindowImpl vw) { + } + }); + + LinphoneCall call = LinphoneManager.getLc().getCurrentCall(); + LinphoneCallParams callParams = call.getCurrentParamsCopy(); + params.width = callParams.getReceivedVideoSize().width; + params.height = callParams.getReceivedVideoSize().height; + LinphoneManager.getLc().setVideoWindow(androidVideoWindowImpl); + + setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + Context context = LinphoneService.instance(); + Intent intent = new Intent(context, LinphoneActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + }); + } + + public LinphoneOverlay(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public LinphoneOverlay(Context context) { + this(context, null); + } + + public void destroy() { + androidVideoWindowImpl.release(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + x = event.getRawX(); + y = event.getRawY(); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touchX = event.getX(); + touchY = event.getY(); + break; + case MotionEvent.ACTION_MOVE: + updateViewPostion(); + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + touchX = touchY = 0; + break; + default: + break; + } + return super.onTouchEvent(event); + } + + private void updateViewPostion() { + params.x = Math.min(Math.max(0, (int) (x - touchX)), metrics.widthPixels - getMeasuredWidth()); + params.y = Math.min(Math.max(0, (int) (y - touchY)), metrics.heightPixels - getMeasuredHeight()); + wm.updateViewLayout(this, params); + } + + public WindowManager.LayoutParams getWindowManagerLayoutParams() { + return params; + } +} diff --git a/tests/src/org/linphone/test/Chat.java b/tests/src/org/linphone/test/Chat.java index f6754e166..0db97b052 100644 --- a/tests/src/org/linphone/test/Chat.java +++ b/tests/src/org/linphone/test/Chat.java @@ -2,7 +2,6 @@ package org.linphone.test; import junit.framework.Assert; -import org.linphone.ChatStorage; import org.linphone.LinphoneActivity; import org.linphone.core.LinphoneChatMessage; import org.linphone.core.LinphoneChatMessage.State; @@ -29,9 +28,9 @@ public class Chat extends SampleTest { public void testAEmptyChatHistory() { goToChat(); - ChatStorage chatStorage = ChatStorage.getInstance(); - for (String conversation : chatStorage.getChatList()) { - chatStorage.removeDiscussion(conversation); + LinphoneChatRoom[] chats = LinphoneTestManager.getInstance().getLc().getChatRooms(); + for (LinphoneChatRoom chatroom : chats) { + chatroom.deleteHistory(); } Assert.assertEquals(0, LinphoneActivity.instance().getUnreadMessageCount());