CallFragment: add MVP model
Refactor CallFragment by adding MVP model, moving video management from
RingApplication to HardwareServiceImpl and removing use of
VideoCallbackManager
Change-Id: Ie75a0d33fa138590911d19d113df362ade29b9f4
Reviewed-by: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
Tuleap: #1360
diff --git a/ring-android/app/src/main/java/cx/ring/application/RingApplication.java b/ring-android/app/src/main/java/cx/ring/application/RingApplication.java
index a1f5dc8..7f766b7 100644
--- a/ring-android/app/src/main/java/cx/ring/application/RingApplication.java
+++ b/ring-android/app/src/main/java/cx/ring/application/RingApplication.java
@@ -26,19 +26,11 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.graphics.ImageFormat;
-import android.hardware.Camera;
import android.media.AudioManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.WindowManager;
-import java.io.IOException;
-import java.lang.ref.WeakReference;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
@@ -61,7 +53,6 @@
import cx.ring.service.CallManagerCallBack;
import cx.ring.service.ConfigurationManagerCallback;
import cx.ring.service.LocalService;
-import cx.ring.service.VideoManagerCallback;
import cx.ring.services.AccountService;
import cx.ring.services.CallService;
import cx.ring.services.ConferenceService;
@@ -78,7 +69,6 @@
private final static String TAG = RingApplication.class.getName();
public final static String DRING_CONNECTION_CHANGED = BuildConfig.APPLICATION_ID + ".event.DRING_CONNECTION_CHANGE";
- public final static String VIDEO_EVENT = BuildConfig.APPLICATION_ID + ".event.VIDEO_EVENT";
public static final int PERMISSIONS_REQUEST = 57;
private RingInjectionComponent mRingInjectionComponent;
@@ -87,20 +77,12 @@
// Android Specific callbacks handlers. They rely on low level services notifications
private ConfigurationManagerCallback mConfigurationCallback;
private CallManagerCallBack mCallManagerCallBack;
- public VideoManagerCallback mVideoManagerCallback;
// true Daemon callbacks handlers. The notify the Android ones
private Callback mCallAndConferenceCallbackHandler;
private ConfigurationCallback mAccountAndContactCallbackHandler;
private PresenceCallback mPresenceCallbackHandler;
private VideoCallback mHardwareCallbackHandler;
-
- public final Map<String, RingApplication.Shm> videoInputs = new HashMap<>();
- public static WeakReference<SurfaceHolder> mCameraPreviewSurface = new WeakReference<>(null);
- public static Map<String, WeakReference<SurfaceHolder>> videoSurfaces = Collections.synchronizedMap(new HashMap<String, WeakReference<SurfaceHolder>>());
- private Camera previewCamera = null;
- public RingApplication.VideoParams previewParams = null;
-
/**
* Handler to run tasks that needs to be on main thread (UI updates)
*/
@@ -187,8 +169,6 @@
return;
}
- final boolean isVideoAllowed = mDeviceRuntimeService.hasVideoPermission();
-
Future<Boolean> startResult = mExecutor.submit(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
@@ -196,7 +176,6 @@
// observe them)
mConfigurationCallback = new ConfigurationManagerCallback(getApplicationContext());
mCallManagerCallBack = new CallManagerCallBack(getApplicationContext());
- mVideoManagerCallback = new VideoManagerCallback(RingApplication.this);
// mCallAndConferenceCallbackHandler is a wrapper to handle CallCallbacks and ConferenceCallbacks
mCallAndConferenceCallbackHandler = mDaemonService.getDaemonCallbackHandler(
@@ -213,7 +192,6 @@
mConferenceService.addObserver(mCallManagerCallBack);
mAccountService.addObserver(mConfigurationCallback);
mContactService.addObserver(mConfigurationCallback);
- mHardwareService.addObserver(mVideoManagerCallback);
mDaemonService.startDaemon(
mCallAndConferenceCallbackHandler,
@@ -224,8 +202,9 @@
ringerModeChanged(((AudioManager) getSystemService(Context.AUDIO_SERVICE)).getRingerMode());
registerReceiver(ringerModeListener, RINGER_FILTER);
- if (isVideoAllowed) {
- mVideoManagerCallback.init();
+ if(mDeviceRuntimeService.hasVideoPermission()) {
+ //initVideo is called here to give time to the application to initialize hardware cameras
+ mHardwareService.initVideo();
}
return true;
@@ -246,23 +225,6 @@
mAccountService.loadAccountsFromDaemon(mPreferencesService.isConnectedWifiAndMobile());
}
- public void restartVideo() {
-
- final boolean isVideoAllowed = mDeviceRuntimeService.hasVideoPermission();
-
- Future<Boolean> result = mExecutor.submit(new Callable<Boolean>() {
- @Override
- public Boolean call() throws Exception {
- if (isVideoAllowed) {
- mVideoManagerCallback.init();
- }
- return true;
- }
- });
-
- FutureUtils.getFutureResult(result);
- }
-
public void terminateDaemon() {
Future<Boolean> stopResult = mExecutor.submit(new Callable<Boolean>() {
@Override
@@ -271,7 +233,6 @@
mDaemonService.stopDaemon();
mConfigurationCallback = null;
mCallManagerCallBack = null;
- mVideoManagerCallback = null;
Intent intent = new Intent(DRING_CONNECTION_CHANGED);
intent.putExtra("connected", mDaemonService.isStarted());
sendBroadcast(intent);
@@ -339,245 +300,4 @@
public void permissionHasBeenAsked(String permission) {
mPermissionsBeingAsked.remove(permission);
}
-
- public void decodingStarted(String id, String shmPath, int width, int height, boolean isMixer) {
- Log.i(TAG, "DRingService.decodingStarted() " + id + " " + width + "x" + height);
- Shm shm = new Shm();
- shm.id = id;
- shm.path = shmPath;
- shm.w = width;
- shm.h = height;
- shm.mixer = isMixer;
- videoInputs.put(id, shm);
- WeakReference<SurfaceHolder> weakSurfaceHolder = videoSurfaces.get(id);
- if (weakSurfaceHolder != null) {
- SurfaceHolder holder = weakSurfaceHolder.get();
- if (holder != null) {
- startVideo(shm, holder);
- }
- }
- }
-
- public void decodingStopped(String id) {
- Log.i(TAG, "DRingService.decodingStopped() " + id);
- Shm shm = videoInputs.remove(id);
- if (shm != null) {
- stopVideo(shm);
- }
- }
-
- public void startVideo(Shm input, SurfaceHolder holder) {
- Log.i(TAG, "DRingService.startVideo() " + input.id);
-
- input.window = mHardwareService.startVideo(input.id, holder.getSurface(), input.w, input.h);
-
- if (input.window == 0) {
- Log.i(TAG, "DRingService.startVideo() no window ! " + input.id);
- Intent intent = new Intent(VIDEO_EVENT);
- intent.putExtra("start", true);
- sendBroadcast(intent);
- return;
- }
-
- Intent intent = new Intent(VIDEO_EVENT);
- intent.putExtra("started", true);
- intent.putExtra("call", input.id);
- intent.putExtra("width", input.w);
- intent.putExtra("height", input.h);
- sendBroadcast(intent);
- }
-
- public void stopVideo(Shm input) {
- Log.i(TAG, "DRingService.stopVideo() " + input.id);
- if (input.window != 0) {
- mHardwareService.stopVideo(input.id, input.window);
- input.window = 0;
- }
-
- Intent intent = new Intent(VIDEO_EVENT);
- intent.putExtra("started", false);
- intent.putExtra("call", input.id);
- sendBroadcast(intent);
- }
-
- static public int rotationToDegrees(int rotation) {
- switch (rotation) {
- case Surface.ROTATION_0:
- return 0;
- case Surface.ROTATION_90:
- return 90;
- case Surface.ROTATION_180:
- return 180;
- case Surface.ROTATION_270:
- return 270;
- }
- return 0;
- }
-
- public void setVideoRotation(VideoParams videoParams, Camera.CameraInfo info) {
- WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
- int rotation = rotationToDegrees(windowManager.getDefaultDisplay().getRotation());
- if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
- videoParams.rotation = (info.orientation + rotation + 360) % 360;
- } else {
- videoParams.rotation = (info.orientation - rotation + 360) % 360;
- }
- }
-
- private void setCameraDisplayOrientation(int camId, android.hardware.Camera camera) {
- android.hardware.Camera.CameraInfo info =
- new android.hardware.Camera.CameraInfo();
- android.hardware.Camera.getCameraInfo(camId, info);
- WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
- int rotation = rotationToDegrees(windowManager.getDefaultDisplay().getRotation());
- int result;
- if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
- result = (info.orientation + rotation) % 360;
- result = (360 - result) % 360; // compensate the mirror
- } else { // back-facing
- result = (info.orientation - rotation + 360) % 360;
- }
- camera.setDisplayOrientation(result);
- }
-
- public void startCapture(final VideoParams videoParams) {
- stopCapture();
-
- SurfaceHolder surface = mCameraPreviewSurface.get();
- if (surface == null) {
- Log.w(TAG, "Can't start capture: no surface registered.");
- previewParams = videoParams;
- Intent intent = new Intent(VIDEO_EVENT);
- intent.putExtra("start", true);
- sendBroadcast(intent);
- return;
- }
-
- if (videoParams == null) {
- Log.w(TAG, "startCapture: no video parameters ");
- return;
- }
- Log.d(TAG, "startCapture " + videoParams.id + " " + videoParams.width + "x" + videoParams.height + " rot" + videoParams.rotation);
-
- final Camera preview;
- try {
- preview = Camera.open(videoParams.id);
- setCameraDisplayOrientation(videoParams.id, preview);
- } catch (Exception e) {
- Log.e(TAG, e.getMessage());
- return;
- }
-
- try {
- surface.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
- preview.setPreviewDisplay(surface);
- } catch (IOException e) {
- Log.e(TAG, e.getMessage());
- return;
- }
-
- Camera.Parameters parameters = preview.getParameters();
- parameters.setPreviewFormat(videoParams.format);
- parameters.setPreviewSize(videoParams.width, videoParams.height);
- parameters.setRotation(0);
-
- for (int[] fps : parameters.getSupportedPreviewFpsRange()) {
- if (videoParams.rate >= fps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] &&
- videoParams.rate <= fps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]) {
- parameters.setPreviewFpsRange(fps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
- fps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
- }
- }
-
- try {
- preview.setParameters(parameters);
- } catch (RuntimeException e) {
- Log.e(TAG, "Error while settings preview parameters", e);
- }
-
- preview.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
- @Override
- public void onPreviewFrame(byte[] data, Camera camera) {
- mHardwareService.setVideoFrame(data, videoParams.width, videoParams.height, videoParams.rotation);
- preview.addCallbackBuffer(data);
- }
- });
-
- // enqueue first buffer
- int bufferSize = parameters.getPreviewSize().width * parameters.getPreviewSize().height * ImageFormat.getBitsPerPixel(parameters.getPreviewFormat()) / 8;
- preview.addCallbackBuffer(new byte[bufferSize]);
-
- preview.setErrorCallback(new Camera.ErrorCallback() {
- @Override
- public void onError(int error, Camera cam) {
- Log.w(TAG, "Camera onError " + error);
- if (preview == cam) {
- stopCapture();
- }
- }
- });
- preview.startPreview();
-
- previewCamera = preview;
- previewParams = videoParams;
-
- Intent intent = new Intent(VIDEO_EVENT);
- intent.putExtra("camera", videoParams.id == 1);
- intent.putExtra("started", true);
- intent.putExtra("width", videoParams.rotWidth);
- intent.putExtra("height", videoParams.rotHeight);
- sendBroadcast(intent);
- }
-
- public void stopCapture() {
- Log.d(TAG, "stopCapture " + previewCamera);
- if (previewCamera != null) {
- final Camera preview = previewCamera;
- final VideoParams p = previewParams;
- previewCamera = null;
- preview.setPreviewCallback(null);
- preview.setErrorCallback(null);
- preview.stopPreview();
- preview.release();
-
- Intent intent = new Intent(VIDEO_EVENT);
- intent.putExtra("camera", p.id == 1);
- intent.putExtra("started", false);
- intent.putExtra("width", p.width);
- intent.putExtra("height", p.height);
- sendBroadcast(intent);
- }
- }
-
- static public class Shm {
- String id;
- String path;
- int w, h;
- boolean mixer;
- public long window = 0;
- }
-
- static public class VideoParams {
- public VideoParams(int id, int format, int width, int height, int rate) {
- this.id = id;
- this.format = format;
- this.width = width;
- this.height = height;
- this.rate = rate;
- }
-
- public int id;
- int format;
-
- // size as captured by Android
- public int width;
- public int height;
-
- //size, rotated, as seen by the daemon
- public int rotWidth;
- public int rotHeight;
-
- int rate;
- int rotation;
- }
}
diff --git a/ring-android/app/src/main/java/cx/ring/client/CallActivity.java b/ring-android/app/src/main/java/cx/ring/client/CallActivity.java
index e73417b..a79ba0b 100644
--- a/ring-android/app/src/main/java/cx/ring/client/CallActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/client/CallActivity.java
@@ -24,67 +24,29 @@
import android.app.FragmentManager;
import android.app.FragmentTransaction;
-import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
import android.content.res.Configuration;
-import android.graphics.PixelFormat;
-import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
-import android.util.Pair;
import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-
-import javax.inject.Inject;
import cx.ring.BuildConfig;
import cx.ring.R;
-import cx.ring.application.RingApplication;
-import cx.ring.facades.ConversationFacade;
import cx.ring.fragments.CallFragment;
-import cx.ring.model.CallContact;
-import cx.ring.model.Conference;
-import cx.ring.model.Conversation;
-import cx.ring.model.SipCall;
import cx.ring.model.Uri;
-import cx.ring.model.Account;
-import cx.ring.service.IDRingService;
-import cx.ring.service.LocalService;
-import cx.ring.services.AccountService;
-import cx.ring.utils.CallProximityManager;
+import cx.ring.services.NotificationService;
-import static cx.ring.service.LocalService.Callbacks;
-
-public class CallActivity extends AppCompatActivity implements Callbacks, CallFragment.ConversationCallbacks, CallProximityManager.ProximityDirector {
+public class CallActivity extends AppCompatActivity {
static final String TAG = CallActivity.class.getSimpleName();
public static final String ACTION_CALL = BuildConfig.APPLICATION_ID + ".action.call";
- @Inject
- AccountService mAccountService;
-
- @Inject
- ConversationFacade mConversationFacade;
-
- private boolean init = false;
private View mMainView;
- private LocalService service;
-
- private CallFragment mCurrentCallFragment;
- private Conference mDisplayedConference;
- private String mSavedConferenceId = null;
/* result code sent in case of call failure */
public static int RESULT_FAILURE = -10;
- private CallProximityManager mProximityManager = null;
private int currentOrientation = Configuration.ORIENTATION_PORTRAIT;
private boolean dimmed = false;
@@ -92,20 +54,6 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (savedInstanceState != null)
- mSavedConferenceId = savedInstanceState.getString("conference", null);
- Log.d(TAG, "CallActivity onCreate " + mSavedConferenceId);
-
- // Dependency injection
- ((RingApplication) getApplication()).getRingInjectionComponent().inject(this);
-
- int flags = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
- Window w = getWindow();
- w.setFlags(flags, flags);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- w.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- w.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
- }
setContentView(R.layout.activity_call_layout);
@@ -122,20 +70,41 @@
}
});
- Intent intent = new Intent(this, LocalService.class);
- bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
- }
+ android.net.Uri u = getIntent().getData();
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- Window window = getWindow();
- window.setFormat(PixelFormat.RGBA_8888);
+ String action = getIntent().getAction();
+ if (Intent.ACTION_CALL.equals(action) || ACTION_CALL.equals(action)) {
+ Uri number = new Uri(u.getSchemeSpecificPart());
+ Log.d(TAG, "number " + number);
+
+ boolean hasVideo = getIntent().getBooleanExtra("video", false);
+ String accountId = getIntent().getStringExtra("account");
+
+
+ // Reload a new view
+ FragmentManager fragmentManager = getFragmentManager();
+ FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+ CallFragment callFragment = CallFragment.newInstance(CallFragment.ACTION_PLACE_CALL,
+ accountId,
+ number,
+ hasVideo);
+ fragmentTransaction.replace(R.id.main_call_layout, callFragment).commit();
+
+ } else if (Intent.ACTION_VIEW.equals(action)) {
+ String confId = getIntent().getStringExtra(NotificationService.KEY_CALL_ID);
+ Log.d(TAG, "conf " + confId);
+ // Reload a new view
+ FragmentManager fragmentManager = getFragmentManager();
+ FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+ CallFragment callFragment = CallFragment.newInstance(CallFragment.ACTION_GET_CALL,
+ confId);
+ fragmentTransaction.replace(R.id.main_call_layout, callFragment).commit();
+ }
+
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
- Log.d(TAG, "onConfigurationChanged " + newConfig.screenWidthDp);
currentOrientation = newConfig.orientation;
@@ -148,12 +117,6 @@
showSystemUI();
}
- // Reload a new view
- FragmentManager fragmentManager = getFragmentManager();
- FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
- mCurrentCallFragment = new CallFragment();
- fragmentTransaction.replace(R.id.main_call_layout, mCurrentCallFragment).commit();
-
super.onConfigurationChanged(newConfig);
}
@@ -188,199 +151,10 @@
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
- if (hasFocus && currentOrientation == Configuration.ORIENTATION_LANDSCAPE)
+ if (hasFocus && currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
hideSystemUI();
- else
+ } else {
showSystemUI();
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- if (mDisplayedConference != null)
- outState.putString("conference", mDisplayedConference.getId());
- }
-
- private Handler mHandler = new Handler();
- private Runnable mUpdateTimeTask = new Runnable() {
- @Override
- public void run() {
- if (mCurrentCallFragment != null)
- mCurrentCallFragment.updateTime();
- mHandler.postAtTime(this, SystemClock.uptimeMillis() + 1000);
}
- };
-
- /* activity no more in foreground */
- @Override
- protected void onPause() {
- super.onPause();
- mHandler.removeCallbacks(mUpdateTimeTask);
- }
-
- @Override
- protected void onDestroy() {
- Log.d(TAG, "CallActivity onDestroy");
- unbindService(mConnection);
- if (mProximityManager != null) {
- mProximityManager.stopTracking();
- mProximityManager.release(0);
- }
-
- super.onDestroy();
- }
-
- /**
- * Defines callbacks for service binding, passed to bindService()
- */
- private ServiceConnection mConnection = new ServiceConnection() {
- @SuppressWarnings("unchecked")
- @Override
- public void onServiceConnected(ComponentName className, IBinder binder) {
- service = ((LocalService.LocalBinder) binder).getService();
-
- if (!init) {
- mProximityManager = new CallProximityManager(CallActivity.this, CallActivity.this);
- mProximityManager.startTracking();
-
- if (mSavedConferenceId != null) {
- mDisplayedConference = mConversationFacade.getConference(mSavedConferenceId);
- } else {
- checkExternalCall();
- }
-
- if (mDisplayedConference == null || mDisplayedConference.getParticipants().isEmpty()) {
- Log.e(TAG, "Conference displayed is null or empty");
- CallActivity.this.finish();
- return;
- }
-
- Log.i(TAG, "CallActivity onCreate in:" + mDisplayedConference.isIncoming() +
- " out:" + mDisplayedConference.isOnGoing());
- CallContact contact = mDisplayedConference.getParticipants().get(0).getContact();
- if (null != contact) {
- Log.i(TAG, "CallActivity onCreate contact:" + contact.getDisplayName());
- }
-
- init = true;
- }
-
- FragmentManager fragmentManager = getFragmentManager();
- FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
- mCurrentCallFragment = new CallFragment();
- fragmentTransaction.add(R.id.main_call_layout, mCurrentCallFragment).commit();
- hideSystemUI();
- }
-
- @Override
- public void onServiceDisconnected(ComponentName arg0) {
-
- }
- };
-
- private Pair<Account, Uri> guess(Uri number, String account_id) {
- Account a = mAccountService.getAccount(account_id);
- Conversation conv = mConversationFacade.findOrStartConversationByNumber(number);
-
- // Guess account from number
- if (a == null && number != null)
- a = mAccountService.guessAccount(number);
-
- // Guess number from account/call history
- if (a != null && (number == null || number.isEmpty()))
- number = new Uri(conv.getLastNumberUsed(a.getAccountID()));
-
- // If no account found, use first active
- if (a == null)
- a = mAccountService.getAccounts().get(0);
-
- // If no number found, use first from contact
- if (number == null || number.isEmpty())
- number = conv.getContact().getPhones().get(0).getNumber();
-
- return new Pair<>(a, number);
- }
-
- private boolean checkExternalCall() {
- Log.d(TAG, "intent " + getIntent().toString());
-
- if (getIntent() == null) {
- terminateCall();
- return false;
- }
-
- android.net.Uri u = getIntent().getData();
- if (u == null) {
- terminateCall();
- return false;
- }
-
- Log.d(TAG, "uri " + u.toString());
-
- String action = getIntent().getAction();
- if (Intent.ACTION_CALL.equals(action) || ACTION_CALL.equals(action)) {
- Uri number = new Uri(u.getSchemeSpecificPart());
- Log.d(TAG, "number " + number);
-
- boolean hasVideo = getIntent().getBooleanExtra("video", false);
- Pair<Account, Uri> g = guess(number, getIntent().getStringExtra("account"));
-
- SipCall call = new SipCall(null, g.first.getAccountID(), g.second, SipCall.Direction.OUTGOING);
- call.muteVideo(!hasVideo);
-
- mDisplayedConference = mConversationFacade.placeCall(call);
- } else if (Intent.ACTION_VIEW.equals(action)) {
- String conf_id = u.getLastPathSegment();
- Log.d(TAG, "conf " + conf_id);
- mDisplayedConference = mConversationFacade.getConference(conf_id);
- }
-
- return false;
- }
-
- @Override
- public IDRingService getRemoteService() {
- return service.getRemoteService();
- }
-
- @Override
- public LocalService getService() {
- return service;
- }
-
- @Override
- public Conference getDisplayedConference() {
- return mDisplayedConference;
- }
-
- @Override
- public void updateDisplayedConference(Conference c) {
- mDisplayedConference = c;
- }
-
- @Override
- public void terminateCall() {
- mHandler.removeCallbacks(mUpdateTimeTask);
- finish();
- }
-
- @Override
- public void startTimer() {
- mHandler.postDelayed(mUpdateTimeTask, 0);
- }
-
- @Override
- public boolean shouldActivateProximity() {
- return true;
- }
-
- @Override
- public void onProximityTrackingChanged(boolean acquired) {
- }
-
- @Override
- public void onBackPressed() {
- mCurrentCallFragment.onBackPressed();
- super.onBackPressed();
}
}
diff --git a/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java b/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java
index bf7c03c..091abe7 100644
--- a/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java
@@ -34,6 +34,7 @@
import butterknife.ButterKnife;
import cx.ring.R;
import cx.ring.facades.ConversationFacade;
+import cx.ring.fragments.CallFragment;
import cx.ring.fragments.ConversationFragment;
public class ConversationActivity extends AppCompatActivity {
@@ -96,8 +97,8 @@
if (mConversationFragment == null) {
Bundle bundle = new Bundle();
- bundle.putString("conversationID", getIntent().getData().getLastPathSegment());
- bundle.putString("number", getIntent().getStringExtra("number"));
+ bundle.putString(ConversationFragment.KEY_CONVERSATION_ID, getIntent().getData().getLastPathSegment());
+ bundle.putString(CallFragment.KEY_NUMBER, getIntent().getStringExtra(CallFragment.KEY_NUMBER));
mConversationFragment = new ConversationFragment();
mConversationFragment.setArguments(bundle);
diff --git a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java
index ac3f9e1..2acf485 100644
--- a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java
@@ -67,6 +67,7 @@
import cx.ring.about.AboutFragment;
import cx.ring.application.RingApplication;
import cx.ring.fragments.AccountsManagementFragment;
+import cx.ring.fragments.CallFragment;
import cx.ring.fragments.ConversationFragment;
import cx.ring.fragments.SmartListFragment;
import cx.ring.model.Account;
@@ -80,6 +81,7 @@
import cx.ring.service.LocalService;
import cx.ring.services.AccountService;
import cx.ring.services.DeviceRuntimeService;
+import cx.ring.services.HardwareService;
import cx.ring.services.NotificationService;
import cx.ring.services.PreferencesService;
import cx.ring.settings.SettingsFragment;
@@ -133,6 +135,9 @@
PreferencesService mPreferencesService;
@Inject
+ HardwareService mHardwareService;
+
+ @Inject
AccountService mAccountService;
@Inject
@@ -314,7 +319,7 @@
}
if (fContent instanceof SmartListFragment) {
Bundle bundle = new Bundle();
- bundle.putString("conversationID", intent.getStringExtra("conversationID"));
+ bundle.putString(ConversationFragment.KEY_CONVERSATION_ID, intent.getStringExtra(ConversationFragment.KEY_CONVERSATION_ID));
startConversationTablet(bundle);
}
}
@@ -462,8 +467,10 @@
case Manifest.permission.CAMERA:
sharedPref.edit().putBoolean(getString(R.string.pref_systemCamera_key), grantResults[i] == PackageManager.PERMISSION_GRANTED).apply();
// permissions have changed, video params should be reset
- ((RingApplication) getApplication()).restartVideo();
- break;
+ final boolean isVideoAllowed = mDeviceRuntimeService.hasVideoPermission();
+ if (isVideoAllowed) {
+ mHardwareService.initVideo();
+ }
}
}
@@ -822,7 +829,7 @@
Intent intent = new Intent(Intent.ACTION_VIEW)
.setClass(HomeActivity.this, ConversationActivity.class)
.setData(Uri.withAppendedPath(ContentUriHandler.CONVERSATION_CONTENT_URI, c.getIds().get(0)))
- .putExtra("number", selected);
+ .putExtra(CallFragment.KEY_NUMBER, selected);
startActivityForResult(intent, HomeActivity.REQUEST_CODE_CONVERSATION);
}
});
diff --git a/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java b/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java
index 82ebee0..4a71a61 100755
--- a/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java
+++ b/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java
@@ -23,14 +23,16 @@
import cx.ring.about.AboutFragment;
import cx.ring.about.AboutPresenter;
+import cx.ring.account.RegisterNameDialog;
import cx.ring.account.RingAccountSummaryFragment;
import cx.ring.account.RingAccountSummaryPresenter;
import cx.ring.application.RingApplication;
import cx.ring.client.AccountEditionActivity;
import cx.ring.client.AccountWizard;
-import cx.ring.client.CallActivity;
import cx.ring.client.HomeActivity;
import cx.ring.contactrequests.BlackListFragment;
+import cx.ring.contactrequests.PendingContactRequestsFragment;
+import cx.ring.facades.ConversationFacade;
import cx.ring.fragments.AccountMigrationFragment;
import cx.ring.fragments.AccountsManagementFragment;
import cx.ring.fragments.AdvancedAccountFragment;
@@ -38,7 +40,6 @@
import cx.ring.fragments.ConversationFragment;
import cx.ring.fragments.MediaPreferenceFragment;
import cx.ring.fragments.ProfileCreationFragment;
-import cx.ring.account.RegisterNameDialog;
import cx.ring.fragments.RingAccountCreationFragment;
import cx.ring.fragments.SIPAccountCreationFragment;
import cx.ring.fragments.SecurityAccountFragment;
@@ -48,12 +49,10 @@
import cx.ring.service.BootReceiver;
import cx.ring.service.DRingService;
import cx.ring.service.LocalService;
-import cx.ring.service.VideoManagerCallback;
import cx.ring.services.AccountService;
import cx.ring.services.CallService;
import cx.ring.services.ConferenceService;
import cx.ring.services.ContactServiceImpl;
-import cx.ring.facades.ConversationFacade;
import cx.ring.services.DaemonService;
import cx.ring.services.DeviceRuntimeServiceImpl;
import cx.ring.services.HardwareService;
@@ -65,7 +64,6 @@
import cx.ring.settings.SettingsPresenter;
import cx.ring.share.ShareFragment;
import cx.ring.share.SharePresenter;
-import cx.ring.contactrequests.PendingContactRequestsFragment;
import dagger.Component;
@Singleton
@@ -75,8 +73,6 @@
void inject(RingNavigationFragment view);
- void inject(CallActivity activity);
-
void inject(HomeActivity activity);
void inject(AccountWizard activity);
@@ -121,8 +117,6 @@
void inject(DRingService service);
- void inject(VideoManagerCallback callback);
-
void inject(DeviceRuntimeServiceImpl service);
void inject(DaemonService service);
diff --git a/ring-android/app/src/main/java/cx/ring/dependencyinjection/ServiceInjectionModule.java b/ring-android/app/src/main/java/cx/ring/dependencyinjection/ServiceInjectionModule.java
index c37500c..4c392cb 100755
--- a/ring-android/app/src/main/java/cx/ring/dependencyinjection/ServiceInjectionModule.java
+++ b/ring-android/app/src/main/java/cx/ring/dependencyinjection/ServiceInjectionModule.java
@@ -39,6 +39,7 @@
import cx.ring.services.DeviceRuntimeService;
import cx.ring.services.DeviceRuntimeServiceImpl;
import cx.ring.services.HardwareService;
+import cx.ring.services.HardwareServiceImpl;
import cx.ring.services.HistoryService;
import cx.ring.services.HistoryServiceImpl;
import cx.ring.services.LogService;
@@ -139,8 +140,8 @@
@Provides
@Singleton
- HardwareService provideHardwareService(DaemonService daemonService) {
- HardwareService hardwareService = new HardwareService();
+ HardwareService provideHardwareService(Context context) {
+ HardwareServiceImpl hardwareService = new HardwareServiceImpl(context);
mRingApplication.getRingInjectionComponent().inject(hardwareService);
return hardwareService;
}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java
index b140d66..1ca65ab 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java
@@ -1,8 +1,7 @@
/*
- * Copyright (C) 2004-2016 Savoir-faire Linux Inc.
+ * Copyright (C) 2017 Savoir-faire Linux Inc.
*
- * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
- * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ * Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,36 +17,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-
package cx.ring.fragments;
-import android.app.Activity;
-import android.app.Fragment;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
-import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
-import android.media.AudioManager;
-import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.RemoteException;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.v4.app.NotificationManagerCompat;
-import android.support.v4.content.res.ResourcesCompat;
-import android.support.v7.app.ActionBar;
-import android.text.Editable;
import android.text.TextUtils;
-import android.text.TextWatcher;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -58,16 +42,13 @@
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
-import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
-import android.widget.ViewSwitcher;
+import com.bumptech.glide.Glide;
import com.skyfishjy.library.RippleBackground;
-import java.lang.ref.WeakReference;
-import java.util.HashMap;
import java.util.Locale;
import javax.inject.Inject;
@@ -76,92 +57,72 @@
import butterknife.ButterKnife;
import butterknife.OnClick;
import cx.ring.R;
-import cx.ring.adapters.ContactDetailsTask;
import cx.ring.application.RingApplication;
+import cx.ring.call.CallPresenter;
+import cx.ring.call.CallView;
import cx.ring.client.ConversationActivity;
import cx.ring.client.HomeActivity;
-import cx.ring.facades.ConversationFacade;
-import cx.ring.interfaces.CallInterface;
import cx.ring.model.CallContact;
-import cx.ring.model.Conference;
-import cx.ring.model.SecureSipCall;
-import cx.ring.model.ServiceEvent;
import cx.ring.model.SipCall;
-import cx.ring.service.CallManagerCallBack;
-import cx.ring.service.IDRingService;
+import cx.ring.model.Uri;
+import cx.ring.mvp.BaseFragment;
import cx.ring.service.LocalService;
-import cx.ring.services.AccountService;
-import cx.ring.services.NotificationService;
import cx.ring.utils.ActionHelper;
-import cx.ring.utils.BitmapUtils;
-import cx.ring.utils.BlockchainInputHandler;
+import cx.ring.utils.CircleTransform;
import cx.ring.utils.ContentUriHandler;
import cx.ring.utils.KeyboardVisibilityManager;
-import cx.ring.utils.Observable;
-import cx.ring.utils.Observer;
-import cx.ring.utils.VCardUtils;
-import ezvcard.VCard;
-import ezvcard.property.Photo;
-public class CallFragment extends Fragment implements CallInterface, ContactDetailsTask.DetailsLoadedCallback, Observer<ServiceEvent> {
+public class CallFragment extends BaseFragment<CallPresenter> implements CallView {
- static final private String TAG = CallFragment.class.getSimpleName();
+ public static final String TAG = CallFragment.class.getSimpleName();
- public static final int REQUEST_TRANSFER = 10;
+ public static final String ACTION_PLACE_CALL = "PLACE_CALL";
+ public static final String ACTION_GET_CALL = "GET_CALL";
- //~ Regular expression to match DTMF supported characters : 0 to 9, A, B, C, D, * and #
- public static final String DTMF_SUPPORTED_CHARS_REGEX = "^[a-dA-D0-9#*]*$";
-
- // Screen wake lock for incoming call
- private WakeLock mScreenWakeLock;
+ public static final String KEY_ACTION = "action";
+ public static final String KEY_ACCOUNT_ID = "accountId";
+ public static final String KEY_CONF_ID = "confId";
+ public static final String KEY_NUMBER = "number";
+ public static final String KEY_HAS_VIDEO = "hasVideo";
@Inject
- AccountService mAccountService;
-
- @Inject
- ConversationFacade mConversationFacade;
-
- @Inject
- NotificationService mNotificationService;
+ protected CallPresenter callPresenter;
@BindView(R.id.contact_bubble_layout)
- View contactBubbleLayout;
+ protected View contactBubbleLayout;
@BindView(R.id.contact_bubble)
- ImageView contactBubbleView;
+ protected ImageView contactBubbleView;
@BindView(R.id.contact_bubble_txt)
- TextView contactBubbleTxt;
+ protected TextView contactBubbleTxt;
@BindView(R.id.contact_bubble_num_txt)
- TextView contactBubbleNumTxt;
+ protected TextView contactBubbleNumTxt;
@BindView(R.id.call_accept_btn)
- View acceptButton;
+ protected View acceptButton;
@BindView(R.id.call_refuse_btn)
- View refuseButton;
+ protected View refuseButton;
@BindView(R.id.call_hangup_btn)
- View hangupButton;
+ protected View hangupButton;
@BindView(R.id.call_status_txt)
- TextView mCallStatusTxt;
-
- @BindView(R.id.security_indicator)
- View securityIndicator;
-
- @BindView(R.id.security_switcher)
- ViewSwitcher mSecuritySwitch;
+ protected TextView mCallStatusTxt;
@BindView(R.id.dialpad_edit_text)
- EditText mNumeralDialEditText;
+ protected EditText mNumeralDialEditText;
@BindView(R.id.ripple_animation)
- RippleBackground mPulseAnimation;
+ protected RippleBackground mPulseAnimation;
@BindView(R.id.video_preview_surface)
- SurfaceView mVideoSurface = null;
+ protected SurfaceView mVideoSurface = null;
+
+ @BindView(R.id.camera_preview_surface)
+ protected SurfaceView mVideoPreview = null;
private MenuItem speakerPhoneBtn = null;
private MenuItem addContactBtn = null;
@@ -169,65 +130,60 @@
private MenuItem dialPadBtn = null;
private MenuItem changeScreenOrientationBtn = null;
- @BindView(R.id.camera_preview_surface)
- SurfaceView videoPreview = null;
-
- public ConversationCallbacks mCallbacks = sDummyCallbacks;
-
- private AudioManager audioManager;
- private boolean haveVideo = false;
- private int videoWidth = -1, videoHeight = -1;
- private int previewWidth = -1, previewHeight = -1;
-
- private boolean lastVideoSource = true;
- private Conference mCachedConference = null;
-
- private boolean ongoingCall = false;
-
- private BlockchainInputHandler mBlockchainInputHandler;
-
+ // Screen wake lock for incoming call
+ private PowerManager.WakeLock mScreenWakeLock;
private DisplayManager.DisplayListener displayListener;
- @Override
- public void onAttach(Activity activity) {
- Log.i(TAG, "onAttach");
- super.onAttach(activity);
+ public static CallFragment newInstance(@NonNull String action, @Nullable String accountID, @Nullable Uri number, boolean hasVideo) {
+ Bundle bundle = new Bundle();
+ bundle.putString(KEY_ACTION, action);
+ bundle.putString(KEY_ACCOUNT_ID, accountID);
+ bundle.putSerializable(KEY_NUMBER, number);
+ bundle.putBoolean(KEY_HAS_VIDEO, hasVideo);
+ CallFragment countDownFragment = new CallFragment();
+ countDownFragment.setArguments(bundle);
+ return countDownFragment;
+ }
- if (!(activity instanceof ConversationCallbacks)) {
- throw new IllegalStateException("Activity must implement fragment's callbacks.");
+ public static CallFragment newInstance(@NonNull String action, @Nullable String confId) {
+ Bundle bundle = new Bundle();
+ bundle.putString(KEY_ACTION, action);
+ bundle.putString(KEY_CONF_ID, confId);
+ CallFragment countDownFragment = new CallFragment();
+ countDownFragment.setArguments(bundle);
+ return countDownFragment;
+ }
+
+ @Override
+ protected CallPresenter createPresenter() {
+ return callPresenter;
+ }
+
+ @Override
+ protected void initPresenter(CallPresenter presenter) {
+ super.initPresenter(presenter);
+
+ String action = getArguments().getString(KEY_ACTION);
+ if (action.equals(ACTION_PLACE_CALL)) {
+ callPresenter.initOutGoing(getArguments().getString(KEY_ACCOUNT_ID),
+ (Uri) getArguments().getSerializable(KEY_NUMBER),
+ getArguments().getBoolean(KEY_HAS_VIDEO));
+ } else if (action.equals(ACTION_GET_CALL)) {
+ callPresenter.initIncoming(getArguments().getString(KEY_CONF_ID));
}
-
- mCallbacks = (ConversationCallbacks) activity;
-
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(CallManagerCallBack.RECORD_STATE_CHANGED);
- intentFilter.addAction(CallManagerCallBack.RTCP_REPORT_RECEIVED);
- intentFilter.addAction(CallManagerCallBack.VCARD_COMPLETED);
-
- intentFilter.addAction(RingApplication.VIDEO_EVENT);
-
- getActivity().registerReceiver(mReceiver, intentFilter);
}
+ @Nullable
@Override
- public void onDetach() {
- Log.i(TAG, "onDetach");
- getActivity().unregisterReceiver(mReceiver);
- mCallbacks = sDummyCallbacks;
- super.onDetach();
- }
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
+ setHasOptionsMenu(true);
+ View inflatedView = inflater.inflate(R.layout.frag_call, container, false);
- @Override
- public void onCreate(Bundle savedBundle) {
- Log.i(TAG, "onCreate");
- super.onCreate(savedBundle);
+ ButterKnife.bind(this, inflatedView);
// dependency injection
((RingApplication) getActivity().getApplication()).getRingInjectionComponent().inject(this);
- audioManager = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
-
- setHasOptionsMenu(true);
PowerManager powerManager = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE);
mScreenWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, "cx.ring.onIncomingCall");
mScreenWakeLock.setReferenceCounted(false);
@@ -251,158 +207,84 @@
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
- try {
- mCallbacks.getRemoteService().switchInput(getConference().getId(), lastVideoSource);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
+ callPresenter.displayChanged();
}
});
}
};
}
+
+ mVideoSurface.getHolder().setFormat(PixelFormat.RGBA_8888);
+ mVideoSurface.getHolder().addCallback(new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ presenter.videoSurfaceCreated(holder);
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ presenter.videoSurfaceDestroyed();
+ }
+ });
+ inflatedView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View parent, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ callPresenter.layoutChanged();
+ }
+ });
+ inflatedView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
+ @Override
+ public void onSystemUiVisibilityChange(int visibility) {
+ boolean ui = (visibility & (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN)) == 0;
+ callPresenter.uiVisibilityChanged(ui);
+ }
+ });
+
+ mVideoPreview.getHolder().setFormat(PixelFormat.RGBA_8888);
+ mVideoPreview.getHolder().addCallback(new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ presenter.previewVideoSurfaceCreated(holder);
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ presenter.previewVideoSurfaceDestroyed();
+ }
+ });
+ mVideoPreview.setZOrderMediaOverlay(true);
+
+ return inflatedView;
}
@Override
- public void onDestroy() {
- super.onDestroy();
- Log.i(TAG, "onDestroy");
+ public void onStop() {
+ super.onStop();
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ DisplayManager displayManager = (DisplayManager) getActivity().getSystemService(Context.DISPLAY_SERVICE);
+ displayManager.unregisterDisplayListener(displayListener);
+ }
+
if (mScreenWakeLock != null && mScreenWakeLock.isHeld()) {
mScreenWakeLock.release();
}
}
- /**
- * The Activity calling this fragment has to implement this interface
- */
- public interface ConversationCallbacks extends LocalService.Callbacks {
- void startTimer();
-
- void terminateCall();
-
- Conference getDisplayedConference();
-
- void updateDisplayedConference(Conference c);
-
- ActionBar getSupportActionBar();
- }
-
- private static final ConversationCallbacks sDummyCallbacks = new ConversationCallbacks() {
- @Override
- public void startTimer() {
- //Dummy implementation
- }
-
- @Override
- public void terminateCall() {
- //Dummy implementation
- }
-
- @Override
- public Conference getDisplayedConference() {
- //Dummy implementation
- return null;
- }
-
- @Override
- public void updateDisplayedConference(Conference c) {
- //Dummy implementation
- }
-
- @Override
- public ActionBar getSupportActionBar() {
- //Dummy implementation
- return null;
- }
-
- @Override
- public IDRingService getRemoteService() {
- //Dummy implementation
- return null;
- }
-
- @Override
- public LocalService getService() {
- //Dummy implementation
- return null;
- }
- };
-
- public class CallReceiver extends BroadcastReceiver {
- private final String TAG = CallReceiver.class.getSimpleName();
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.contentEquals(LocalService.ACTION_CONF_UPDATE)) {
- confUpdate();
- } else if (action.contentEquals(RingApplication.VIDEO_EVENT)) {
- if (mVideoSurface == null)
- return;
- Conference conf = getConference();
- if (intent.hasExtra("start")) {
- mVideoSurface.setVisibility(View.VISIBLE);
- videoPreview.setVisibility(View.VISIBLE);
- } else if (intent.hasExtra("camera")) {
- previewWidth = intent.getIntExtra("width", 0);
- previewHeight = intent.getIntExtra("height", 0);
- } else if (conf != null && conf.getId().equals(intent.getStringExtra("call"))) {
- if (mVideoSurface != null) {
- haveVideo = intent.getBooleanExtra("started", false);
- if (haveVideo) {
- mVideoSurface.setVisibility(View.VISIBLE);
- videoPreview.setVisibility(View.VISIBLE);
-
- videoWidth = intent.getIntExtra("width", 0);
- videoHeight = intent.getIntExtra("height", 0);
- } else {
- mVideoSurface.setVisibility(View.GONE);
- videoPreview.setVisibility(View.GONE);
- }
- }
- refreshState();
- }
- resetVideoSizes();
- } else if (action.contentEquals(CallManagerCallBack.RECORD_STATE_CHANGED)) {
- recordingChanged((Conference) intent.getParcelableExtra("conference"), intent.getStringExtra("call"), intent.getStringExtra("file"));
- } else if (action.contentEquals(CallManagerCallBack.RTCP_REPORT_RECEIVED)) {
- rtcpReportReceived(null, null); // FIXME
- } else if (action.contentEquals(CallManagerCallBack.VCARD_COMPLETED)) {
- updateContactBubble();
- } else {
- Log.e(TAG, "Unknown action: " + intent.getAction());
- }
- }
- }
-
- private final CallReceiver mReceiver = new CallReceiver();
-
- public void refreshState() {
- Conference conf = getConference();
-
- if (conf == null) {
- contactBubbleView.setImageBitmap(null);
- contactBubbleTxt.setText("");
- contactBubbleNumTxt.setText("");
- acceptButton.setVisibility(View.GONE);
- refuseButton.setVisibility(View.GONE);
- hangupButton.setVisibility(View.GONE);
- } else if (conf.getParticipants().size() == 1) {
- SipCall call = conf.getParticipants().get(0);
- if (call.isIncoming() && call.isRinging()) {
- Log.w(TAG, "CallFragment refreshState INCOMING " + call.getCallId());
- initIncomingCallDisplay();
- } else if (conf.getParticipants().get(0).isRinging()) {
- Log.w(TAG, "CallFragment refreshState RINGING " + call.getCallId());
- initOutGoingCallDisplay();
- } else if (call.isOngoing()) {
- initNormalStateDisplay();
- }
- } else if (conf.getParticipants().size() > 1) {
- initNormalStateDisplay();
- }
-
- getActivity().invalidateOptionsMenu();
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ presenter.configurationChanged();
}
@Override
@@ -419,21 +301,197 @@
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
+ callPresenter.prepareOptionMenu();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ super.onOptionsItemSelected(item);
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ case R.id.menuitem_chat:
+ callPresenter.chatClick();
+ break;
+ case R.id.menuitem_addcontact:
+ callPresenter.acceptCall();
+ break;
+ case R.id.menuitem_speaker:
+ callPresenter.speakerClick();
+ getActivity().invalidateOptionsMenu();
+ break;
+ case R.id.menuitem_camera_flip:
+ callPresenter.switchVideoInputClick();
+ break;
+ case R.id.menuitem_dialpad:
+ callPresenter.dialpadClick();
+ break;
+ case R.id.menuitem_change_screen_orientation:
+ callPresenter.screenRotationClick();
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ public void blockScreenRotation() {
+ int currentOrientation = getResources().getConfiguration().orientation;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
+ return;
+ }
+ if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ } else {
+ getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
+ }
+
+ @Override
+ public void displayContactBubble(final boolean display) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ contactBubbleLayout.setVisibility(display ? View.VISIBLE : View.GONE);
+ }
+ });
+ }
+
+ @Override
+ public void displayVideoSurface(final boolean display) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mVideoSurface.setVisibility(display ? View.VISIBLE : View.GONE);
+ mVideoPreview.setVisibility(display ? View.VISIBLE : View.GONE);
+ }
+ });
+ }
+
+ @Override
+ public void displayHangupButton(boolean display) {
+ hangupButton.setVisibility(display ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void displayDialPadKeyboard() {
+ KeyboardVisibilityManager.showKeyboard(getActivity(),
+ mNumeralDialEditText,
+ InputMethodManager.SHOW_IMPLICIT);
+ }
+
+ @Override
+ public void switchCameraIcon(boolean isFront) {
+ flipCameraBtn.setIcon(isFront ? R.drawable.ic_camera_front_white : R.drawable.ic_camera_rear_white);
+ }
+
+ @Override
+ public void changeScreenRotation() {
+ int currentOrientation = getResources().getConfiguration().orientation;
+ if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ } else {
+ getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ }
+ }
+
+ @Override
+ public void updateTime(final long duration) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mCallStatusTxt.setText(String.format(Locale.getDefault(), "%d:%02d:%02d", duration / 3600, duration % 3600 / 60, duration % 60));
+ }
+ });
+ }
+
+ @Override
+ public void updateContactBubble(final String contactName) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (contactName.isEmpty()) {
+ return;
+ }
+ if (contactBubbleTxt.getText().toString().contains(CallContact.PREFIX_RING)) {
+ contactBubbleNumTxt.setVisibility(View.VISIBLE);
+ contactBubbleNumTxt.setText(contactBubbleTxt.getText());
+ contactBubbleTxt.setText(contactName);
+ } else {
+ contactBubbleNumTxt.setVisibility(View.VISIBLE);
+ contactBubbleNumTxt.setText(contactName);
+ }
+ }
+ });
+ }
+
+ /**
+ * Updates the bubble contact image with the vcard image, the contact image or by default the
+ * contact picture drawable.
+ */
+ @Override
+ public void updateContactBubbleWithVCard(final String contactName, final byte[] photo) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (photo != null && photo.length > 0) {
+ Glide.with(getActivity())
+ .load(photo)
+ .transform(new CircleTransform(getActivity()))
+ .error(R.drawable.ic_contact_picture)
+ .into(contactBubbleView);
+ } else {
+ Glide.with(getActivity())
+ .load(R.drawable.ic_contact_picture)
+ .into(contactBubbleView);
+ }
+
+ if (TextUtils.isEmpty(contactName) || contactName.contains(CallContact.PREFIX_RING)) {
+ return;
+ }
+ if (contactBubbleTxt.getText().toString().contains(CallContact.PREFIX_RING)) {
+ contactBubbleNumTxt.setVisibility(View.VISIBLE);
+ contactBubbleNumTxt.setText(contactBubbleTxt.getText());
+ }
+ contactBubbleTxt.setText(contactName);
+ }
+ });
+ }
+
+ @Override
+ public void updateCallStatus(final int callState) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ switch (callState) {
+ case SipCall.State.NONE:
+ mCallStatusTxt.setText("");
+ break;
+ default:
+ mCallStatusTxt.setText(callStateToHumanState(callState));
+ break;
+ }
+
+ }
+ });
+ }
+
+ @Override
+ public void initMenu(boolean isSpeakerOn, boolean hasContact, boolean displayFlip, boolean canDial, boolean onGoingCall) {
if (speakerPhoneBtn != null) {
- boolean speakerPhone = audioManager.isSpeakerphoneOn();
- if (speakerPhoneBtn.getIcon() != null)
- speakerPhoneBtn.getIcon().setAlpha(speakerPhone ? 255 : 128);
- speakerPhoneBtn.setChecked(speakerPhone);
+ speakerPhoneBtn.setVisible(onGoingCall);
+ if (speakerPhoneBtn.getIcon() != null) {
+ speakerPhoneBtn.getIcon().setAlpha(isSpeakerOn ? 255 : 128);
+ }
+ speakerPhoneBtn.setChecked(isSpeakerOn);
}
if (addContactBtn != null) {
- SipCall call = (getConference() != null && !getConference().getParticipants().isEmpty()) ? getFirstParticipant() : null;
- addContactBtn.setVisible(call != null && null != call.getContact() && call.getContact().isUnknown());
+ addContactBtn.setVisible(hasContact);
}
-
- flipCameraBtn.setVisible(haveVideo);
-
+ if (flipCameraBtn != null) {
+ flipCameraBtn.setVisible(displayFlip);
+ }
if (dialPadBtn != null) {
- dialPadBtn.setVisible(ongoingCall && getConference() != null && !getConference().isIncoming());
+ dialPadBtn.setVisible(canDial);
}
if (changeScreenOrientationBtn != null) {
changeScreenOrientationBtn.setVisible(mVideoSurface.getVisibility() == View.VISIBLE);
@@ -441,196 +499,133 @@
}
@Override
- public boolean onOptionsItemSelected(MenuItem item) {
- super.onOptionsItemSelected(item);
- SipCall firstParticipant = getFirstParticipant();
- switch (item.getItemId()) {
- case android.R.id.home:
- if (firstParticipant != null) {
- startConversationActivity(firstParticipant.getContact());
- }
- break;
- case R.id.menuitem_chat:
- if (firstParticipant == null
- || firstParticipant.getContact() == null
- || firstParticipant.getContact().getIds() == null
- || firstParticipant.getContact().getIds().isEmpty()) {
- break;
- }
- startConversationActivity(firstParticipant.getContact());
- break;
- case R.id.menuitem_addcontact:
- if (firstParticipant == null || firstParticipant.getContact() == null) {
- break;
- }
- startActivityForResult(ActionHelper.getAddNumberIntentForContact(firstParticipant.getContact()),
- ConversationFragment.REQ_ADD_CONTACT);
- break;
- case R.id.menuitem_speaker:
- audioManager.setSpeakerphoneOn(!audioManager.isSpeakerphoneOn());
+ public void initNormalStateDisplay(final boolean hasVideo) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ acceptButton.setVisibility(View.GONE);
+ refuseButton.setVisibility(View.GONE);
+ hangupButton.setVisibility(View.VISIBLE);
+
+ contactBubbleLayout.setVisibility(hasVideo ? View.INVISIBLE : View.VISIBLE);
+
getActivity().invalidateOptionsMenu();
- break;
- case R.id.menuitem_camera_flip:
- lastVideoSource = !lastVideoSource;
- try {
- mCallbacks.getRemoteService().switchInput(getConference().getId(), lastVideoSource);
- } catch (RemoteException e) {
- e.printStackTrace();
+ }
+ });
+ }
+
+ @Override
+ public void initIncomingCallDisplay() {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ acceptButton.setVisibility(View.VISIBLE);
+ refuseButton.setVisibility(View.VISIBLE);
+ hangupButton.setVisibility(View.GONE);
+ }
+ });
+ }
+
+ @Override
+ public void initOutGoingCallDisplay() {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ acceptButton.setVisibility(View.GONE);
+ refuseButton.setVisibility(View.VISIBLE);
+ hangupButton.setVisibility(View.GONE);
+ }
+ });
+ }
+
+ @Override
+ public void initContactDisplay(final SipCall call) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ CallContact contact = call.getContact();
+ if (contact == null) {
+ return;
}
- item.setIcon(lastVideoSource ? R.drawable.ic_camera_front_white : R.drawable.ic_camera_rear_white);
- break;
- case R.id.menuitem_dialpad:
- KeyboardVisibilityManager.showKeyboard(getActivity(),
- mNumeralDialEditText,
- InputMethodManager.SHOW_IMPLICIT);
- break;
- case R.id.menuitem_change_screen_orientation:
- changeScreenOrientation();
- break;
- }
- return true;
- }
-
- @Override
- public void onStop() {
- super.onStop();
-
- Conference c = getConference();
- Log.w(TAG, "onStop() haveVideo=" + haveVideo);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- DisplayManager displayManager = (DisplayManager) getActivity().getSystemService(Context.DISPLAY_SERVICE);
- displayManager.unregisterDisplayListener(displayListener);
- }
-
- RingApplication application = (RingApplication) getActivity().getApplication();
-
- application.videoSurfaces.remove(c.getId());
- application.mCameraPreviewSurface.clear();
- try {
- IDRingService service = mCallbacks.getRemoteService();
- if (service != null) {
- service.videoSurfaceRemoved(c.getId());
- service.videoPreviewSurfaceRemoved();
+ final String name = contact.getDisplayName();
+ contactBubbleTxt.setText(name);
+ if (!name.contains(CallContact.PREFIX_RING) && contactBubbleNumTxt.getText().toString().isEmpty()) {
+ contactBubbleNumTxt.setVisibility(View.VISIBLE);
+ contactBubbleNumTxt.setText(call.getNumber());
+ }
+ mPulseAnimation.startRippleAnimation();
}
- } catch (RemoteException e) {
- e.printStackTrace();
- }
+ });
}
@Override
- public void onStart() {
- super.onStart();
+ public void resetVideoSize(final int videoWidth, final int videoHeight, final int previewWidth, final int previewHeight) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ ViewGroup rootView = (ViewGroup) getView();
+ if (rootView == null)
+ return;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- DisplayManager displayManager = (DisplayManager) getActivity().getSystemService(Context.DISPLAY_SERVICE);
- displayManager.registerDisplayListener(displayListener, null);
- }
+ double videoRatio = videoWidth / (double) videoHeight;
+ double screenRatio = getView().getWidth() / (double) getView().getHeight();
- Conference c = getConference();
- if (c != null && mVideoSurface != null && c.shouldResumeVideo()) {
- Log.i(TAG, "Resuming video");
- haveVideo = true;
- mVideoSurface.setVisibility(View.VISIBLE);
- videoPreview.setVisibility(View.VISIBLE);
+ RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mVideoSurface.getLayoutParams();
+ int oldW = params.width;
+ int oldH = params.height;
+ if (videoRatio >= screenRatio) {
+ params.width = RelativeLayout.LayoutParams.MATCH_PARENT;
+ params.height = (int) (videoHeight * (double) rootView.getWidth() / (double) videoWidth);
+ } else {
+ params.height = RelativeLayout.LayoutParams.MATCH_PARENT;
+ params.width = (int) (videoWidth * (double) rootView.getHeight() / (double) videoHeight);
+ }
- c.setResumeVideo(false);
- }
- }
+ if (oldW != params.width || oldH != params.height) {
+ mVideoSurface.setLayoutParams(params);
+ }
- @Override
- public void onResume() {
- super.onResume();
- Log.i(TAG, "onResume()");
-
- mAccountService.addObserver(this);
- mConversationFacade.addObserver(this);
-
- Conference conference = getConference();
-
- confUpdate();
-
- if (getActivity() != null) {
- getActivity().invalidateOptionsMenu();
- }
-
- if (conference != null) {
- conference.setVisible(true);
- NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getActivity());
- notificationManager.cancel(conference.getUuid());
- if (conference.shouldResumeVideo()) {
- Log.w(TAG, "Resuming video");
- haveVideo = true;
- mVideoSurface.setVisibility(View.VISIBLE);
- videoPreview.setVisibility(View.VISIBLE);
-
- conference.setResumeVideo(false);
+ DisplayMetrics metrics = getResources().getDisplayMetrics();
+ RelativeLayout.LayoutParams paramsPreview = (RelativeLayout.LayoutParams) mVideoPreview.getLayoutParams();
+ oldW = paramsPreview.width;
+ oldH = paramsPreview.height;
+ double previewMaxDim = Math.max(previewWidth, previewHeight);
+ double previewRatio = metrics.density * 160. / previewMaxDim;
+ paramsPreview.width = (int) (previewWidth * previewRatio);
+ paramsPreview.height = (int) (previewHeight * previewRatio);
+ if (oldW != paramsPreview.width || oldH != paramsPreview.height) {
+ mVideoPreview.setLayoutParams(paramsPreview);
+ }
}
- }
-
- refreshState();
+ });
}
@Override
- public void onPause() {
- Log.w(TAG, "onPause() haveVideo=" + haveVideo);
- super.onPause();
-
- mAccountService.removeObserver(this);
- mConversationFacade.removeObserver(this);
-
- Conference conference = getConference();
- if (conference != null) {
- conference.setVisible(false);
- conference.setResumeVideo(haveVideo);
- mNotificationService.showCallNotification(conference);
- }
- }
-
- public void confUpdate() {
- LocalService service = mCallbacks.getService();
- if (service == null)
- return;
-
- Conference c = mConversationFacade.getConference(getConference().getId());
- mCallbacks.updateDisplayedConference(c);
- if (c == null || c.getParticipants().isEmpty()) {
- mCallbacks.terminateCall();
- return;
- }
-
- int newState = getHumanState(c);
-
- Log.w(TAG, "confUpdate() " + getString(newState));
-
- String newStateString = (newState == R.string.call_human_state_none ||
- newState == R.string.conference_human_state_default)
- ? "" :
- getString(newState);
- if (c.isOnGoing()) {
- ongoingCall = true;
- initNormalStateDisplay();
- } else if (c.isRinging()) {
- ongoingCall = false;
- mCallStatusTxt.setText(newStateString);
-
- if (c.isIncoming()) {
- initIncomingCallDisplay();
- } else
- initOutGoingCallDisplay();
+ public void goToConversation(String conversationId) {
+ Intent intent = new Intent();
+ if (ConversationFragment.isTabletMode(getActivity())) {
+ intent.setClass(getActivity(), HomeActivity.class)
+ .setAction(LocalService.ACTION_CONV_ACCEPT)
+ .putExtra(ConversationFragment.KEY_CONVERSATION_ID, conversationId);
+ startActivity(intent);
} else {
- NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getActivity());
- notificationManager.cancel(c.getUuid());
- mCallStatusTxt.setText(newStateString);
- mCallbacks.terminateCall();
+ intent.setClass(getActivity(), ConversationActivity.class)
+ .setAction(Intent.ACTION_VIEW)
+ .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ .setData(android.net.Uri.withAppendedPath(ContentUriHandler.CONVERSATION_CONTENT_URI, conversationId));
+ startActivityForResult(intent, HomeActivity.REQUEST_CODE_CONVERSATION);
}
}
- public int getHumanState(Conference conference) {
- if (conference.getParticipants().size() == 1) {
- return callStateToHumanState(conference.getParticipants().get(0).getCallState());
- }
- return getConferenceHumanState(conference.getState());
+ @Override
+ public void goToAddContact(CallContact callContact) {
+ startActivityForResult(ActionHelper.getAddNumberIntentForContact(callContact),
+ ConversationFragment.REQ_ADD_CONTACT);
+ }
+
+ @Override
+ public void finish() {
+ getActivity().finish();
}
public static int callStateToHumanState(final int state) {
@@ -661,604 +656,18 @@
}
}
- public int getConferenceHumanState(final int state) {
- switch (state) {
- case Conference.state.ACTIVE_ATTACHED:
- return R.string.conference_human_state_active_attached;
- case Conference.state.ACTIVE_DETACHED:
- return R.string.conference_human_state_active_detached;
- case Conference.state.ACTIVE_ATTACHED_REC:
- return R.string.conference_human_state_active_attached_rec;
- case Conference.state.ACTIVE_DETACHED_REC:
- return R.string.conference_human_state_active_detached_rec;
- case Conference.state.HOLD:
- return R.string.conference_human_state_hold;
- case Conference.state.HOLD_REC:
- return R.string.conference_human_state_hold_rec;
- default:
- return R.string.conference_human_state_default;
- }
+ @OnClick({R.id.call_hangup_btn})
+ public void hangUpClicked() {
+ callPresenter.hangupCall();
}
- @Override
- public void recordingChanged(Conference c, String callID, String filename) {
-
- }
-
- @Override
- public void rtcpReportReceived(Conference c, HashMap<String, Integer> stats) {
- // No implementation yet
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- SipCall transfer;
- if (requestCode == REQUEST_TRANSFER) {
- switch (resultCode) {
- case TransferDFragment.RESULT_TRANSFER_CONF:
- Conference c = data.getParcelableExtra("target");
- transfer = data.getParcelableExtra("transfer");
- try {
- mCallbacks.getRemoteService().attendedTransfer(transfer.getCallId(), c.getParticipants().get(0).getCallId());
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- break;
-
- case TransferDFragment.RESULT_TRANSFER_NUMBER:
- String to = data.getStringExtra("to_number");
- transfer = data.getParcelableExtra("transfer");
- try {
- mCallbacks.getRemoteService().transfer(transfer.getCallId(), to);
- mCallbacks.getRemoteService().hangUp(transfer.getCallId());
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- break;
- case Activity.RESULT_CANCELED:
- default:
- confUpdate();
- break;
- }
- }
- }
-
- void resetVideoSizes() {
- ViewGroup rootView = (ViewGroup) getView();
- if (rootView == null)
- return;
-
- double videoRatio = videoWidth / (double) videoHeight;
- double screenRatio = getView().getWidth() / (double) getView().getHeight();
-
- FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mVideoSurface.getLayoutParams();
- int oldW = params.width;
- int oldH = params.height;
- if (videoRatio >= screenRatio) {
- params.width = RelativeLayout.LayoutParams.MATCH_PARENT;
- params.height = (int) (videoHeight * (double) rootView.getWidth() / (double) videoWidth);
- } else {
- params.height = RelativeLayout.LayoutParams.MATCH_PARENT;
- params.width = (int) (videoWidth * (double) rootView.getHeight() / (double) videoHeight);
- }
-
- if (oldW != params.width || oldH != params.height) {
- Log.w(TAG, "onLayoutChange " + params.width + " x " + params.height);
- mVideoSurface.setLayoutParams(params);
- }
-
- DisplayMetrics metrics = getResources().getDisplayMetrics();
- FrameLayout.LayoutParams paramsPreview = (FrameLayout.LayoutParams) videoPreview.getLayoutParams();
- oldW = paramsPreview.width;
- oldH = paramsPreview.height;
- double previewMaxDim = Math.max(previewWidth, previewHeight);
- double previewRatio = metrics.density * 160. / previewMaxDim;
- paramsPreview.width = (int) (previewWidth * previewRatio);
- paramsPreview.height = (int) (previewHeight * previewRatio);
- if (oldW != paramsPreview.width || oldH != paramsPreview.height) {
- Log.i(TAG, "onLayoutChange " + paramsPreview.width + " x " + paramsPreview.height);
- videoPreview.setLayoutParams(paramsPreview);
- }
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- updatePreview();
- }
-
- private void updatePreview() {
- if (videoPreview.getVisibility() == View.VISIBLE) {
- try {
- mCallbacks.getRemoteService().setPreviewSettings();
- mCallbacks.getRemoteService().videoPreviewSurfaceAdded();
- } catch (RemoteException e) {
- Log.e(TAG, "service not found ", e);
- }
- }
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- Log.i(TAG, "onCreateView");
- View rootView = inflater.inflate(R.layout.frag_call, container, false);
-
- ButterKnife.bind(this, rootView);
-
- mNumeralDialEditText.requestFocus();
- mNumeralDialEditText.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- //~ Empty
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- String editTextString = s.toString();
- String lastChar = editTextString.substring(start, start + count);
- if (lastChar.matches(DTMF_SUPPORTED_CHARS_REGEX)) {
- try {
- Log.d(TAG, "Sending DTMF: " + lastChar.toUpperCase());
- mCallbacks.getRemoteService().playDtmf(lastChar.toUpperCase());
- } catch (RemoteException exc) {
- exc.printStackTrace();
- }
- }
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- //~ Empty
- }
- });
-
-
- mVideoSurface.getHolder().setFormat(PixelFormat.RGBA_8888);
- mVideoSurface.getHolder().addCallback(new SurfaceHolder.Callback() {
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- RingApplication application = (RingApplication) getActivity().getApplication();
- contactBubbleLayout.setVisibility(View.GONE);
- Conference c = getConference();
- application.videoSurfaces.put(c.getId(), new WeakReference<>(holder));
- blockSensorScreenRotation();
- try {
- mCallbacks.getRemoteService().videoSurfaceAdded(c.getId());
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- Log.i(TAG, "video surfaceChanged " + format + ", " + width + " x " + height);
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- Conference c = getConference();
- RingApplication application = (RingApplication) getActivity().getApplication();
- application.videoSurfaces.remove(c.getId());
- try {
- IDRingService service = mCallbacks.getRemoteService();
- if (service != null)
- service.videoSurfaceRemoved(c.getId());
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- });
-
- rootView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View parent, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
- resetVideoSizes();
- }
- });
- rootView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
- @Override
- public void onSystemUiVisibilityChange(int visibility) {
- boolean ui = (visibility & (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN)) == 0;
- if (ongoingCall) {
- hangupButton.setVisibility(ui ? View.VISIBLE : View.GONE);
- }
- }
- });
-
- videoPreview.getHolder().setFormat(PixelFormat.RGBA_8888);
- videoPreview.getHolder().addCallback(new SurfaceHolder.Callback() {
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- RingApplication application = (RingApplication) getActivity().getApplication();
- application.mCameraPreviewSurface = new WeakReference<>(holder);
- try {
- mCallbacks.getRemoteService().videoPreviewSurfaceAdded();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- Log.i(TAG, "videoPreview surfaceChanged " + format + ", " + width + " x " + height);
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- RingApplication application = (RingApplication) getActivity().getApplication();
- if (videoPreview != null && application.mCameraPreviewSurface.get() == holder) {
- application.mCameraPreviewSurface.clear();
- }
- try {
- IDRingService service = mCallbacks.getRemoteService();
- if (service != null)
- service.videoPreviewSurfaceRemoved();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- });
- videoPreview.setZOrderMediaOverlay(true);
-
- return rootView;
- }
-
- public Conference getConference() {
- Conference c = mCallbacks.getDisplayedConference();
- if (c != null) {
- if (mCachedConference != c)
- mCachedConference = c;
- return c;
- }
- return mCachedConference;
- }
-
- private void initContactDisplay(final SipCall call) {
-
- CallContact contact = call.getContact();
- final String name = contact.getDisplayName();
- contactBubbleTxt.setText(name);
- if (call.getNumber().contentEquals(name)) {
- contactBubbleNumTxt.setVisibility(View.GONE);
- } else {
- contactBubbleNumTxt.setVisibility(View.VISIBLE);
- contactBubbleNumTxt.setText(call.getNumber());
- getUsername(call);
- }
-
- mPulseAnimation.startRippleAnimation();
-
- updateContactBubble();
- }
-
- private void getUsername(SipCall call) {
- Log.d(TAG, "blockchain with " + call.getNumber());
-
- if (mBlockchainInputHandler == null || !mBlockchainInputHandler.isAlive()) {
- mBlockchainInputHandler = new BlockchainInputHandler(new WeakReference<>(mAccountService));
- }
-
- String[] split = call.getNumber().split(":");
- if (split.length > 0) {
- mBlockchainInputHandler.enqueueNextLookup(split[1]);
- }
- }
-
- private void initNormalStateDisplay() {
- Log.i(TAG, "Start normal display");
- mCallbacks.startTimer();
- acceptButton.setVisibility(View.GONE);
- refuseButton.setVisibility(View.GONE);
- hangupButton.setVisibility(View.VISIBLE);
-
- final SipCall call = getFirstParticipant();
- initContactDisplay(call);
-
- if (getActivity() != null) {
- getActivity().invalidateOptionsMenu();
- }
-
- contactBubbleLayout.setVisibility(haveVideo ? View.GONE : View.VISIBLE);
- updateSecurityDisplay();
-
- updatePreview();
- }
-
- private void updateSecurityDisplay() {
- //First we check if all participants use a security layer.
- boolean secureCall = !getConference().getParticipants().isEmpty();
- for (SipCall c : getConference().getParticipants())
- secureCall &= c instanceof SecureSipCall && ((SecureSipCall) c).isSecure();
-
- securityIndicator.setVisibility(secureCall ? View.VISIBLE : View.GONE);
- if (!secureCall)
- return;
-
- Log.i(TAG, "Enable security display");
- if (getConference().hasMultipleParticipants()) {
- //TODO What layout should we put?
- } else {
- final SecureSipCall secured = (SecureSipCall) getFirstParticipant();
- switch (secured.displayModule()) {
- case SecureSipCall.DISPLAY_GREEN_LOCK:
- Log.i(TAG, "DISPLAY_GREEN_LOCK");
- showLock(R.drawable.green_lock);
- break;
- case SecureSipCall.DISPLAY_RED_LOCK:
- Log.i(TAG, "DISPLAY_RED_LOCK");
- showLock(R.drawable.red_lock);
- break;
- case SecureSipCall.DISPLAY_NONE:
- break;
- }
- }
- }
-
- private void showLock(int resId) {
- ImageView lock = (ImageView) mSecuritySwitch.findViewById(R.id.lock_image);
- lock.setImageDrawable(ResourcesCompat.getDrawable(getResources(), resId, null));
- mSecuritySwitch.setDisplayedChild(1);
- mSecuritySwitch.setVisibility(View.VISIBLE);
- }
-
- private void initIncomingCallDisplay() {
- Log.i(TAG, "Start incoming display");
- final SipCall call = getFirstParticipant();
- if (mAccountService.getAccount(call.getAccount()).isAutoanswerEnabled()) {
- try {
- mCallbacks.getRemoteService().accept(call.getCallId());
- } catch (Exception e) {
- e.printStackTrace();
- }
- } else {
- initContactDisplay(call);
- acceptButton.setVisibility(View.VISIBLE);
- refuseButton.setVisibility(View.VISIBLE);
- hangupButton.setVisibility(View.GONE);
- }
- }
-
- private void initOutGoingCallDisplay() {
- Log.i(TAG, "Start outgoing display");
-
- final SipCall call = getFirstParticipant();
- initContactDisplay(call);
-
- acceptButton.setVisibility(View.GONE);
- refuseButton.setVisibility(View.VISIBLE);
- hangupButton.setVisibility(View.GONE);
- }
-
- public void updateTime() {
- if (getConference() != null && !getConference().getParticipants().isEmpty()) {
- long duration = System.currentTimeMillis() - getFirstParticipant().getTimestampStart();
- duration = duration / 1000;
- if (getConference().isOnGoing())
- mCallStatusTxt.setText(String.format(Locale.getDefault(), "%d:%02d:%02d", duration / 3600, duration % 3600 / 60, duration % 60));
- }
- }
-
- /**
- * Updates the bubble contact image with the vcard image, the contact image or by default the
- * contact picture drawable.
- */
- private void updateContactBubble() {
- Conference conference = this.getConference();
- Context context = getActivity();
- if (conference == null || context == null) {
- return;
- }
-
- SipCall participant = getFirstParticipant();
- if (participant == null) {
- return;
- }
-
- VCard vcard = null;
- String username = participant.getNumberUri().getUsername();
- if (username != null) {
- Log.d(TAG, "username " + username);
- vcard = VCardUtils.loadPeerProfileFromDisk(context.getFilesDir(), username + ".vcf");
- }
- if (vcard == null) {
- Log.d(TAG, "No vcard.");
- setDefaultPhoto();
- return;
- } else {
- Log.d(TAG, "VCard found: " + vcard);
- }
-
- if (!vcard.getPhotos().isEmpty()) {
- Photo tmp = vcard.getPhotos().get(0);
- if (tmp.getData() != null) {
- contactBubbleView.setImageBitmap(BitmapUtils.cropImageToCircle(tmp.getData()));
- } else {
- setDefaultPhoto();
- }
- } else {
- setDefaultPhoto();
- }
-
- if (vcard.getFormattedName() == null || TextUtils.isEmpty(vcard.getFormattedName().getValue())) {
- return;
- }
- contactBubbleTxt.setText(vcard.getFormattedName().getValue());
- ActionBar ab = mCallbacks.getSupportActionBar();
- ab.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE);
- ab.setTitle(vcard.getFormattedName().getValue());
-
- if (participant.getNumber().contentEquals(vcard.getFormattedName().getValue())) {
- contactBubbleNumTxt.setVisibility(View.GONE);
- } else {
- contactBubbleNumTxt.setVisibility(View.VISIBLE);
- contactBubbleNumTxt.setText(participant.getNumber());
- getUsername(participant);
- }
- }
-
-
- @OnClick({R.id.call_hangup_btn, R.id.call_refuse_btn})
- public void hangUpClicked(View view) {
- try {
- final SipCall call = getFirstParticipant();
- if (call == null) {
- return;
- }
- final String callId = call.getCallId();
- startConversationActivity(call.getContact());
- if (view.getId() == R.id.call_hangup_btn) {
- mCallbacks.getRemoteService().hangUp(callId);
- } else {
- mCallbacks.getRemoteService().refuse(callId);
- }
- mCallbacks.terminateCall();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
+ @OnClick(R.id.call_refuse_btn)
+ public void refuseClicked() {
+ callPresenter.refuseCall();
}
@OnClick(R.id.call_accept_btn)
public void acceptClicked() {
- final SipCall call = getFirstParticipant();
- if (call == null) {
- return;
- }
- try {
- mCallbacks.getRemoteService().accept(call.getCallId());
- } catch (RemoteException e) {
- e.printStackTrace();
- }
+ callPresenter.acceptCall();
}
-
- public void changeScreenOrientation() {
- int currentOrientation = getResources().getConfiguration().orientation;
- if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
- getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
- } else {
- getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
- }
- }
-
- private void blockSensorScreenRotation() {
- changeScreenOrientationBtn.setVisible(true);
- int currentOrientation = getResources().getConfiguration().orientation;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
- getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
- return;
- }
- if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
- getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
- } else {
- getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
- }
- }
-
- /**
- * Helper accessor that check nullity or emptiness of components to access first call participant
- *
- * @return the first participant or null
- */
- @Nullable
- private SipCall getFirstParticipant() {
- if (getConference() == null || getConference().getParticipants() == null || getConference().getParticipants().isEmpty()) {
- return null;
- }
- return getConference().getParticipants().get(0);
- }
-
- public void onBackPressed() {
- SipCall call = getFirstParticipant();
- if (call != null) {
- startConversationActivity(call.getContact());
- }
- }
-
- private void startConversationActivity(CallContact contact) {
- if (contact == null || contact.getIds().isEmpty()) {
- return;
- }
- Intent intent = new Intent();
- if (ConversationFragment.isTabletMode(getActivity())) {
- intent.setClass(getActivity(), HomeActivity.class)
- .setAction(LocalService.ACTION_CONV_ACCEPT)
- .putExtra("conversationID", contact.getIds().get(0));
- startActivity(intent);
- } else {
- intent.setClass(getActivity(), ConversationActivity.class)
- .setAction(Intent.ACTION_VIEW)
- .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP)
- .setData(Uri.withAppendedPath(ContentUriHandler.CONVERSATION_CONTENT_URI, contact.getIds().get(0)));
- startActivityForResult(intent, HomeActivity.REQUEST_CODE_CONVERSATION);
- }
- }
-
- private void setDefaultPhoto() {
- if (getConference() != null
- && getConference().getParticipants() != null
- && !getConference().getParticipants().isEmpty()) {
- final SipCall call = getConference().getParticipants().get(0);
- final CallContact contact = call.getContact();
- if (contact != null) {
- new ContactDetailsTask(getActivity(), contact, this).run();
- }
- } else {
- contactBubbleView.setImageDrawable(
- ResourcesCompat.getDrawable(getResources(), R.drawable.ic_contact_picture, null));
- }
- }
-
- @Override
- public void onDetailsLoaded(Bitmap bmp, String formattedName) {
- if (bmp != null) {
- contactBubbleView.setImageBitmap(bmp);
- }
-
- if (formattedName != null) {
- contactBubbleTxt.setText(formattedName);
- ActionBar ab = mCallbacks.getSupportActionBar();
- ab.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE);
- ab.setTitle(formattedName);
- }
- }
-
- @Override
- public void update(Observable observable, ServiceEvent event) {
- if (event == null) {
- return;
- }
-
- if (observable instanceof ConversationFacade) {
- switch (event.getEventType()) {
- case HISTORY_LOADED:
- case CONVERSATIONS_CHANGED:
- case CALL_STATE_CHANGED:
- RingApplication.uiHandler.post(new Runnable() {
- @Override
- public void run() {
- confUpdate();
- }
- });
- break;
- default:
- Log.d(TAG, "This event type is not handled here " + event.getEventType());
- break;
- }
- } else {
- switch (event.getEventType()) {
- case REGISTERED_NAME_FOUND:
- final String name = event.getEventInput(ServiceEvent.EventInput.NAME, String.class);
- RingApplication.uiHandler.post(new Runnable() {
- @Override
- public void run() {
- contactBubbleNumTxt.setText(name);
- }
- });
- break;
- default:
- Log.d(TAG, "This event type is not handled here " + event.getEventType());
- break;
- }
- }
-
- }
-}
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
index 2d299f8..48b2878 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
@@ -47,6 +47,7 @@
import cx.ring.model.Phone;
import cx.ring.model.Uri;
import cx.ring.mvp.BaseFragment;
+import cx.ring.services.NotificationService;
import cx.ring.utils.ActionHelper;
import cx.ring.utils.BitmapUtils;
import cx.ring.utils.ClipboardHelper;
@@ -64,12 +65,13 @@
ContactDetailsTask.DetailsLoadedCallback,
ConversationView {
+ public static final int REQ_ADD_CONTACT = 42;
+ public static final String KEY_CONVERSATION_ID = "CONVERSATION_ID";
+
private static final String TAG = ConversationFragment.class.getSimpleName();
private static final String CONVERSATION_DELETE = "CONVERSATION_DELETE";
private static final int MIN_SIZE_TABLET = 960;
- public static final int REQ_ADD_CONTACT = 42;
-
@Inject
protected ConversationPresenter conversationPresenter;
@@ -77,7 +79,7 @@
protected EditText mMsgEditTxt;
@BindView(R.id.ongoingcall_pane)
- protected ViewGroup mBottomPane;
+ protected ViewGroup mTopPane;
@BindView(R.id.hist_list)
protected RecyclerView mHistList;
@@ -140,8 +142,8 @@
// Dependency injection
((RingApplication) getActivity().getApplication()).getRingInjectionComponent().inject(this);
- if (mBottomPane != null) {
- mBottomPane.setVisibility(View.GONE);
+ if (mTopPane != null) {
+ mTopPane.setVisibility(View.GONE);
}
LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
@@ -341,8 +343,8 @@
@Override
protected void initPresenter(ConversationPresenter presenter) {
super.initPresenter(presenter);
- String conversationId = getArguments().getString("conversationID");
- Uri number = new Uri(getArguments().getString("number"));
+ String conversationId = getArguments().getString(KEY_CONVERSATION_ID);
+ Uri number = new Uri(getArguments().getString(CallFragment.KEY_NUMBER));
conversationPresenter.init(conversationId, number);
}
@@ -386,7 +388,7 @@
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
- mBottomPane.setVisibility(display ? View.GONE : View.VISIBLE);
+ mTopPane.setVisibility(display ? View.GONE : View.VISIBLE);
}
});
}
@@ -478,7 +480,7 @@
public void goToCallActivity(String conferenceId) {
startActivity(new Intent(Intent.ACTION_VIEW)
.setClass(getActivity().getApplicationContext(), CallActivity.class)
- .setData(android.net.Uri.withAppendedPath(ContentUriHandler.CONFERENCE_CONTENT_URI, conferenceId)));
+ .putExtra(NotificationService.KEY_CALL_ID, conferenceId));
}
@Override
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.java
index ba88736..3612a6e 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.java
@@ -531,7 +531,7 @@
startActivityForResult(intent, HomeActivity.REQUEST_CODE_CONVERSATION);
} else {
Bundle bundle = new Bundle();
- bundle.putString("conversationID", callContact.getIds().get(0));
+ bundle.putString(ConversationFragment.KEY_CONVERSATION_ID, callContact.getIds().get(0));
((HomeActivity) getActivity()).startConversationTablet(bundle);
}
}
diff --git a/ring-android/app/src/main/java/cx/ring/mvp/BaseFragment.java b/ring-android/app/src/main/java/cx/ring/mvp/BaseFragment.java
index 5c6df74..3767c76 100644
--- a/ring-android/app/src/main/java/cx/ring/mvp/BaseFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/mvp/BaseFragment.java
@@ -23,8 +23,12 @@
import android.os.Bundle;
import android.view.View;
+import cx.ring.utils.Log;
+
public abstract class BaseFragment<T extends RootPresenter> extends Fragment {
+ protected static final String TAG = BaseFragment.class.getSimpleName();
+
protected T presenter;
@Override
@@ -39,6 +43,7 @@
@Override
public void onDestroyView() {
+ Log.d(TAG, "onDestroyView");
super.onDestroyView();
presenter.unbindView();
}
diff --git a/ring-android/app/src/main/java/cx/ring/service/DRingService.java b/ring-android/app/src/main/java/cx/ring/service/DRingService.java
index f42a03c..60fd660 100644
--- a/ring-android/app/src/main/java/cx/ring/service/DRingService.java
+++ b/ring-android/app/src/main/java/cx/ring/service/DRingService.java
@@ -35,7 +35,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
-import android.view.SurfaceHolder;
import java.io.File;
import java.util.ArrayList;
@@ -49,7 +48,7 @@
import cx.ring.BuildConfig;
import cx.ring.application.RingApplication;
-import cx.ring.daemon.StringMap;
+import cx.ring.client.CallActivity;
import cx.ring.model.Codec;
import cx.ring.services.AccountService;
import cx.ring.services.CallService;
@@ -69,6 +68,11 @@
public static final String ACTION_TRUST_REQUEST_REFUSE = BuildConfig.APPLICATION_ID + ".action.TRUST_REQUEST_REFUSE";
public static final String ACTION_TRUST_REQUEST_BLOCK = BuildConfig.APPLICATION_ID + ".action.TRUST_REQUEST_BLOCK";
+ static public final String ACTION_CALL_ACCEPT = BuildConfig.APPLICATION_ID + ".action.CALL_ACCEPT";
+ static public final String ACTION_CALL_REFUSE = BuildConfig.APPLICATION_ID + ".action.CALL_REFUSE";
+ static public final String ACTION_CALL_END = BuildConfig.APPLICATION_ID + ".action.CALL_END";
+ static public final String ACTION_CALL_VIEW = BuildConfig.APPLICATION_ID + ".action.CALL_VIEW";
+
private static final String TAG = DRingService.class.getName();
@Inject
@@ -441,7 +445,7 @@
@Override
public void playDtmf(final String key) throws RemoteException {
- mCallService.playDtmf(key);
+
}
@Override
@@ -480,51 +484,38 @@
}
@Override
+ @Deprecated
public void videoSurfaceAdded(String id) {
- Log.d(TAG, "DRingService.videoSurfaceAdded() " + id);
- RingApplication application = (RingApplication) getApplication();
- RingApplication.Shm shm = ((RingApplication) getApplication()).videoInputs.get(id);
- SurfaceHolder holder = application.videoSurfaces.get(id).get();
- if (shm != null && holder != null && shm.window == 0) {
- application.startVideo(shm, holder);
- }
+
}
@Override
+ @Deprecated
public void videoSurfaceRemoved(String id) {
- Log.d(TAG, "DRingService.videoSurfaceRemoved() " + id);
- RingApplication application = (RingApplication) getApplication();
- RingApplication.Shm shm = application.videoInputs.get(id);
- if (shm != null) {
- application.stopVideo(shm);
- }
+
}
@Override
+ @Deprecated
public void videoPreviewSurfaceAdded() {
- Log.i(TAG, "DRingService.videoPreviewSurfaceChanged()");
- ((RingApplication) getApplication()).startCapture(((RingApplication) getApplication()).previewParams);
+
}
@Override
+ @Deprecated
public void videoPreviewSurfaceRemoved() {
- Log.i(TAG, "DRingService.videoPreviewSurfaceChanged()");
- ((RingApplication) getApplication()).stopCapture();
+
}
@Override
+ @Deprecated
public void switchInput(final String id, final boolean front) {
- RingApplication application = (RingApplication) getApplication();
- final int camId = (front ? application.mVideoManagerCallback.cameraFront : application.mVideoManagerCallback.cameraBack);
- final String uri = "camera://" + camId;
- final cx.ring.daemon.StringMap map = application.mVideoManagerCallback.getNativeParams(camId).toMap(getResources().getConfiguration().orientation);
- mHardwareService.switchInput(id, uri, map);
}
@Override
+ @Deprecated
public void setPreviewSettings() {
- Map<String, StringMap> camSettings = mDeviceRuntimeService.retrieveAvailablePreviewSettings();
- mHardwareService.setPreviewSettings(camSettings);
+
}
@Override
@@ -559,16 +550,25 @@
};
private void parseIntent(Intent intent) {
+ Bundle extras;
switch (intent.getAction()) {
case ACTION_TRUST_REQUEST_ACCEPT:
case ACTION_TRUST_REQUEST_REFUSE:
- case ACTION_TRUST_REQUEST_BLOCK: {
- Bundle extras = intent.getExtras();
+ case ACTION_TRUST_REQUEST_BLOCK:
+ extras = intent.getExtras();
if (extras != null) {
handleTrustRequestAction(intent.getAction(), extras);
}
break;
- }
+ case ACTION_CALL_ACCEPT:
+ case ACTION_CALL_REFUSE:
+ case ACTION_CALL_END:
+ case ACTION_CALL_VIEW:
+ extras = intent.getExtras();
+ if (extras != null) {
+ handleCallAction(intent.getAction(), extras);
+ }
+ break;
default:
break;
}
@@ -580,20 +580,52 @@
if (account != null && from != null) {
mNotificationService.cancelTrustRequestNotification(account);
switch (action) {
- case ACTION_TRUST_REQUEST_ACCEPT: {
+ case ACTION_TRUST_REQUEST_ACCEPT:
mAccountService.acceptTrustRequest(account, from);
break;
- }
- case ACTION_TRUST_REQUEST_REFUSE: {
+ case ACTION_TRUST_REQUEST_REFUSE:
mAccountService.discardTrustRequest(account, from);
break;
- }
- case ACTION_TRUST_REQUEST_BLOCK: {
+ case ACTION_TRUST_REQUEST_BLOCK:
mAccountService.discardTrustRequest(account, from);
mContactService.removeContact(account, from);
break;
- }
}
}
}
+
+ private void handleCallAction(String action, Bundle extras) {
+ String callId = extras.getString(NotificationServiceImpl.KEY_CALL_ID);
+
+ if (callId == null || callId.isEmpty()) {
+ return;
+ }
+
+ switch (action) {
+ case ACTION_CALL_ACCEPT:
+ mCallService.accept(callId);
+ mNotificationService.cancelCallNotification(callId.hashCode());
+ startActivity(new Intent(Intent.ACTION_VIEW)
+ .putExtras(extras)
+ .setClass(getApplicationContext(), CallActivity.class)
+ .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK));
+ break;
+ case ACTION_CALL_REFUSE:
+ mCallService.refuse(callId);
+ mDeviceRuntimeService.closeAudioState();
+ mNotificationService.cancelCallNotification(callId.hashCode());
+ break;
+ case ACTION_CALL_END:
+ mCallService.hangUp(callId);
+ mDeviceRuntimeService.closeAudioState();
+ mNotificationService.cancelCallNotification(callId.hashCode());
+ break;
+ case ACTION_CALL_VIEW:
+ startActivity(new Intent(Intent.ACTION_VIEW)
+ .putExtras(extras)
+ .setClass(getApplicationContext(), CallActivity.class)
+ .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK));
+ break;
+ }
+ }
}
diff --git a/ring-android/app/src/main/java/cx/ring/service/LocalService.java b/ring-android/app/src/main/java/cx/ring/service/LocalService.java
index 90f465a..5fdb8f5 100644
--- a/ring-android/app/src/main/java/cx/ring/service/LocalService.java
+++ b/ring-android/app/src/main/java/cx/ring/service/LocalService.java
@@ -67,9 +67,6 @@
static public final String ACTION_CONV_READ = BuildConfig.APPLICATION_ID + ".action.CONV_READ";
// Receiving commands
- static public final String ACTION_CALL_ACCEPT = BuildConfig.APPLICATION_ID + ".action.CALL_ACCEPT";
- static public final String ACTION_CALL_REFUSE = BuildConfig.APPLICATION_ID + ".action.CALL_REFUSE";
- static public final String ACTION_CALL_END = BuildConfig.APPLICATION_ID + ".action.CALL_END";
static public final String ACTION_CONV_ACCEPT = BuildConfig.APPLICATION_ID + ".action.CONV_ACCEPT";
@Inject
@@ -277,41 +274,6 @@
sendBroadcast(new Intent(ACTION_CONF_UPDATE).setData(android.net.Uri.withAppendedPath(ContentUriHandler.CONVERSATION_CONTENT_URI, convId)));
break;
}
- case ACTION_CALL_ACCEPT: {
- String callId = intent.getData().getLastPathSegment();
- try {
- mService.accept(callId);
- } catch (RemoteException e) {
- Log.e(TAG, "ACTION_CALL_ACCEPT", e);
- }
- mDeviceRuntimeService.updateAudioState(mConversationFacade.getCurrentCallingConf());
- Conference conf = mConversationFacade.getConference(callId);
- if (conf != null && !conf.isVisible()) {
- startActivity(ActionHelper.getViewIntent(LocalService.this, conf).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- }
- break;
- }
- case ACTION_CALL_REFUSE: {
- String call_id = intent.getData().getLastPathSegment();
- try {
- mService.refuse(call_id);
- } catch (RemoteException e) {
- Log.e(TAG, "ACTION_CALL_REFUSE", e);
- }
- mDeviceRuntimeService.updateAudioState(mConversationFacade.getCurrentCallingConf());
- break;
- }
- case ACTION_CALL_END: {
- String call_id = intent.getData().getLastPathSegment();
- try {
- mService.hangUp(call_id);
- mService.hangUpConference(call_id);
- } catch (RemoteException e) {
- Log.e(TAG, "ACTION_CALL_END", e);
- }
- mDeviceRuntimeService.updateAudioState(mConversationFacade.getCurrentCallingConf());
- break;
- }
default:
break;
}
diff --git a/ring-android/app/src/main/java/cx/ring/service/VideoManagerCallback.java b/ring-android/app/src/main/java/cx/ring/service/VideoManagerCallback.java
deleted file mode 100644
index 6ef3e92..0000000
--- a/ring-android/app/src/main/java/cx/ring/service/VideoManagerCallback.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
- *
- * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- * Damien Riegel <damien.riegel@savoirfairelinux.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package cx.ring.service;
-
-import android.content.res.Configuration;
-import android.graphics.Point;
-import android.hardware.Camera;
-import android.util.Log;
-import android.util.LongSparseArray;
-
-import java.util.HashMap;
-
-import javax.inject.Inject;
-
-import cx.ring.application.RingApplication;
-import cx.ring.daemon.IntVect;
-import cx.ring.daemon.StringMap;
-import cx.ring.daemon.UintVect;
-import cx.ring.model.ServiceEvent;
-import cx.ring.services.HardwareService;
-import cx.ring.utils.Observable;
-import cx.ring.utils.Observer;
-
-
-public class VideoManagerCallback implements Observer<ServiceEvent> {
- private static final String TAG = VideoManagerCallback.class.getSimpleName();
-
- @Inject
- HardwareService mHardwareService;
-
- private final RingApplication mRingApplication;
- private final LongSparseArray<DeviceParams> mNativeParams = new LongSparseArray<>();
- private final HashMap<String, RingApplication.VideoParams> mParams = new HashMap<>();
-
- @Override
- public void update(Observable o, ServiceEvent event) {
- if (event == null) {
- return;
- }
-
- switch (event.getEventType()) {
- case DECODING_STARTED:
- decodingStarted(
- event.getEventInput(ServiceEvent.EventInput.ID, String.class),
- event.getEventInput(ServiceEvent.EventInput.PATHS, String.class),
- event.getEventInput(ServiceEvent.EventInput.WIDTH, Integer.class),
- event.getEventInput(ServiceEvent.EventInput.HEIGHT, Integer.class),
- event.getEventInput(ServiceEvent.EventInput.IS_MIXER, Boolean.class)
- );
- break;
- case DECODING_STOPPED:
- decodingStopped(
- event.getEventInput(ServiceEvent.EventInput.ID, String.class),
- event.getEventInput(ServiceEvent.EventInput.PATHS, String.class),
- event.getEventInput(ServiceEvent.EventInput.IS_MIXER, Boolean.class)
- );
- break;
- case GET_CAMERA_INFO:
- getCameraInfo(
- event.getEventInput(ServiceEvent.EventInput.CAMERA_ID, String.class),
- event.getEventInput(ServiceEvent.EventInput.FORMATS, IntVect.class),
- event.getEventInput(ServiceEvent.EventInput.SIZES, UintVect.class),
- event.getEventInput(ServiceEvent.EventInput.RATES, UintVect.class)
- );
- break;
- case SET_PARAMETERS:
- setParameters(
- event.getEventInput(ServiceEvent.EventInput.CAMERA_ID, String.class),
- event.getEventInput(ServiceEvent.EventInput.FORMATS, Integer.class),
- event.getEventInput(ServiceEvent.EventInput.WIDTH, Integer.class),
- event.getEventInput(ServiceEvent.EventInput.HEIGHT, Integer.class),
- event.getEventInput(ServiceEvent.EventInput.RATES, Integer.class)
- );
- break;
- case START_CAPTURE:
- startCapture(
- event.getEventInput(ServiceEvent.EventInput.CAMERA_ID, String.class)
- );
- break;
- case STOP_CAPTURE:
- stopCapture();
- break;
- default:
- Log.i(TAG, "Unknown daemon event");
- break;
- }
- }
-
- static public class DeviceParams {
- Point size;
- long rate;
- Camera.CameraInfo infos;
-
- public StringMap toMap(int orientation) {
- StringMap map = new StringMap();
- boolean rotated = (size.x > size.y) == (orientation == Configuration.ORIENTATION_PORTRAIT);
- map.set("size", Integer.toString(rotated ? size.y : size.x) + "x" + Integer.toString(rotated ? size.x : size.y));
- map.set("rate", Long.toString(rate));
- return map;
- }
- }
-
- public int cameraFront = 0;
- public int cameraBack = 0;
-
- public VideoManagerCallback(RingApplication app) {
- mRingApplication = app;
- mRingApplication.getRingInjectionComponent().inject(this);
- }
-
- public void init() {
- mNativeParams.clear();
- int number_cameras = getNumberOfCameras();
- Camera.CameraInfo camInfo = new Camera.CameraInfo();
- for (int i = 0; i < number_cameras; i++) {
- mHardwareService.addVideoDevice(Integer.toString(i));
- Camera.getCameraInfo(i, camInfo);
- if (camInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
- cameraFront = i;
- } else {
- cameraBack = i;
- }
- Log.d(TAG, "Camera number " + i);
- }
- mHardwareService.setDefaultVideoDevice(Integer.toString(cameraFront));
- }
-
- public DeviceParams getNativeParams(int i) {
- return mNativeParams.get(i);
- }
-
- private void decodingStarted(String id, String shmPath, int width, int height, boolean isMixer) {
- mRingApplication.decodingStarted(id, shmPath, width, height, isMixer);
- }
-
- private void decodingStopped(String id, String shmPath, boolean isMixer) {
- mRingApplication.decodingStopped(id);
- }
-
- private void setParameters(String camId, int format, int width, int height, int rate) {
- int id = Integer.valueOf(camId);
- DeviceParams p = mNativeParams.get(id);
- RingApplication.VideoParams newParams = new RingApplication.VideoParams(id, format, p.size.x, p.size.y, rate);
- newParams.rotWidth = width;
- newParams.rotHeight = height;
- mRingApplication.setVideoRotation(newParams, p.infos);
- mParams.put(camId, newParams);
- }
-
- private void startCapture(String camId) {
- RingApplication.VideoParams params = mParams.get(camId);
- if (params == null) {
- return;
- }
-
- mRingApplication.startCapture(params);
- }
-
- private void stopCapture() {
- mRingApplication.stopCapture();
- }
-
- private void getCameraInfo(String camId, IntVect formats, UintVect sizes, UintVect rates) {
-
- int id = Integer.valueOf(camId);
-
- if (id < 0 || id >= getNumberOfCameras()) {
- return;
- }
-
- Camera cam;
- try {
- cam = Camera.open(id);
- } catch (Exception e) {
- Log.d(TAG, e.getMessage());
- return;
- }
-
- Camera.Parameters param = cam.getParameters();
- cam.release();
-
- getFormats(param, formats);
-
- DeviceParams p = new DeviceParams();
- p.size = getSizeToUse(param);
- sizes.add(p.size.x);
- sizes.add(p.size.y);
- sizes.add(p.size.y);
- sizes.add(p.size.x);
-
- getRates(param, rates);
- p.rate = rates.get(0);
-
- p.infos = new Camera.CameraInfo();
- Camera.getCameraInfo(id, p.infos);
-
- mNativeParams.put(id, p);
- }
-
- private int getNumberOfCameras() {
- return Camera.getNumberOfCameras();
- }
-
- private void getFormats(Camera.Parameters param, IntVect formats) {
- for (int fmt : param.getSupportedPreviewFormats()) {
- formats.add(fmt);
- }
- }
-
- private Point getSizeToUse(Camera.Parameters param) {
- final int MIN_WIDTH = 320;
- final Point size = new Point(0, 0);
- /** {@link Camera.Parameters#getSupportedPreviewSizes} :
- * "This method will always return a list with at least one element."
- * Attempt to find the size with width closest (but above) MIN_WIDTH. */
- for (Camera.Size s : param.getSupportedPreviewSizes()) {
- if (s.width < s.height)
- continue;
- if (size.x < MIN_WIDTH ? s.width > size.x : (s.width >= MIN_WIDTH && s.width < size.x)) {
- size.x = s.width;
- size.y = s.height;
- }
- }
- Log.d(TAG, "Size to use: " + size.x + " x " + size.y);
- return size;
- }
-
- private void getRates(Camera.Parameters param, UintVect rates) {
- for (int fps[] : param.getSupportedPreviewFpsRange()) {
- int rate = (fps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + fps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]) / 2;
- rates.add(rate);
- }
- }
-}
diff --git a/ring-android/app/src/main/java/cx/ring/services/DeviceRuntimeServiceImpl.java b/ring-android/app/src/main/java/cx/ring/services/DeviceRuntimeServiceImpl.java
index ea2cd77..960a860 100644
--- a/ring-android/app/src/main/java/cx/ring/services/DeviceRuntimeServiceImpl.java
+++ b/ring-android/app/src/main/java/cx/ring/services/DeviceRuntimeServiceImpl.java
@@ -22,7 +22,6 @@
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.hardware.Camera;
import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
@@ -30,8 +29,6 @@
import android.support.v4.content.ContextCompat;
import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
@@ -39,9 +36,6 @@
import javax.inject.Inject;
import javax.inject.Named;
-import cx.ring.application.RingApplication;
-import cx.ring.daemon.StringMap;
-import cx.ring.model.Conference;
import cx.ring.utils.Log;
import cx.ring.utils.MediaManager;
import cx.ring.utils.NetworkUtils;
@@ -87,31 +81,31 @@
}
@Override
- public void updateAudioState(final Conference conf) {
+ public void updateAudioState(final boolean isRinging) {
Handler mainHandler = new Handler(mContext.getMainLooper());
mainHandler.post(new Runnable() {
@Override
public void run() {
- if (conf != null) {
- boolean incomingAndRinging = conf.isIncoming() && conf.isRinging();
- mediaManager.obtainAudioFocus(incomingAndRinging);
- if (incomingAndRinging) {
- mediaManager.audioManager.setMode(AudioManager.MODE_RINGTONE);
- mediaManager.startRing(null);
- } else {
- mediaManager.stopRing();
- mediaManager.audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
- }
+ mediaManager.obtainAudioFocus(isRinging);
+ if (isRinging) {
+ mediaManager.audioManager.setMode(AudioManager.MODE_RINGTONE);
+ mediaManager.startRing(null);
} else {
mediaManager.stopRing();
- mediaManager.abandonAudioFocus();
+ mediaManager.audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
}
}
});
}
@Override
+ public void closeAudioState() {
+ mediaManager.stopRing();
+ mediaManager.abandonAudioFocus();
+ }
+
+ @Override
public File provideFilesDir() {
return mContext.getFilesDir();
}
@@ -163,18 +157,6 @@
return checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
}
- @Override
- public Map<String, StringMap> retrieveAvailablePreviewSettings() {
- RingApplication application = (RingApplication) mContext.getApplicationContext();
- Map<String, StringMap> camSettings = new HashMap<>();
- for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
- if (application.mVideoManagerCallback.getNativeParams(i) != null) {
- camSettings.put(Integer.toString(i), application.mVideoManagerCallback.getNativeParams(i).toMap(mContext.getResources().getConfiguration().orientation));
- }
- }
- return camSettings;
- }
-
private boolean checkPermission(String permission) {
return ContextCompat.checkSelfPermission(mContext, permission) == PackageManager.PERMISSION_GRANTED;
}
diff --git a/ring-android/app/src/main/java/cx/ring/services/HardwareServiceImpl.java b/ring-android/app/src/main/java/cx/ring/services/HardwareServiceImpl.java
new file mode 100644
index 0000000..9f533e2
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/services/HardwareServiceImpl.java
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2017 Savoir-faire Linux Inc.
+ *
+ * Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.services;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.ImageFormat;
+import android.graphics.Point;
+import android.hardware.Camera;
+import android.media.AudioManager;
+import android.support.annotation.Nullable;
+import android.util.LongSparseArray;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.WindowManager;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import cx.ring.daemon.IntVect;
+import cx.ring.daemon.StringMap;
+import cx.ring.daemon.UintVect;
+import cx.ring.model.ServiceEvent;
+import cx.ring.utils.Log;
+
+public class HardwareServiceImpl extends HardwareService {
+
+ public static final String TAG = HardwareServiceImpl.class.getName();
+
+ private Context mContext;
+
+ private int cameraFront = 0;
+ private int cameraBack = 0;
+
+ private int currentCamera = -1;
+
+ private final Map<String, Shm> videoInputs = new HashMap<>();
+ private static WeakReference<SurfaceHolder> mCameraPreviewSurface = new WeakReference<>(null);
+ private static Map<String, WeakReference<SurfaceHolder>> videoSurfaces = Collections.synchronizedMap(new HashMap<String, WeakReference<SurfaceHolder>>());
+ private VideoParams previewParams = null;
+ private Camera previewCamera = null;
+ private final HashMap<String, VideoParams> mParams = new HashMap<>();
+ private final LongSparseArray<DeviceParams> mNativeParams = new LongSparseArray<>();
+
+ public HardwareServiceImpl(Context mContext) {
+ this.mContext = mContext;
+ }
+
+ public void initVideo() {
+ Log.i(TAG, "initVideo()");
+ mNativeParams.clear();
+ int numberCameras = Camera.getNumberOfCameras();
+ Camera.CameraInfo camInfo = new Camera.CameraInfo();
+ for (int i = 0; i < numberCameras; i++) {
+ addVideoDevice(Integer.toString(i));
+ Camera.getCameraInfo(i, camInfo);
+ if (camInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+ cameraFront = i;
+ } else {
+ cameraBack = i;
+ }
+ }
+ currentCamera = cameraFront;
+ setDefaultVideoDevice(Integer.toString(cameraFront));
+ }
+
+ @Override
+ public boolean isSpeakerPhoneOn() {
+ AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ return audioManager.isSpeakerphoneOn();
+ }
+
+ @Override
+ public void switchSpeakerPhone() {
+ AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ audioManager.setSpeakerphoneOn(!audioManager.isSpeakerphoneOn());
+ }
+
+ public void decodingStarted(String id, String shmPath, int width, int height, boolean isMixer) {
+ Log.i(TAG, "decodingStarted() " + id + " " + width + "x" + height);
+ Shm shm = new Shm();
+ shm.id = id;
+ shm.path = shmPath;
+ shm.w = width;
+ shm.h = height;
+ shm.mixer = isMixer;
+ videoInputs.put(id, shm);
+ WeakReference<SurfaceHolder> weakSurfaceHolder = videoSurfaces.get(id);
+ if (weakSurfaceHolder != null) {
+ SurfaceHolder holder = weakSurfaceHolder.get();
+ if (holder != null) {
+ shm.window = startVideo(id, holder.getSurface(), width, height);
+
+ if (shm.window == 0) {
+ Log.i(TAG, "DRingService.decodingStarted() no window !");
+
+ ServiceEvent event = new ServiceEvent(ServiceEvent.EventType.VIDEO_EVENT);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_START, true);
+ setChanged();
+ notifyObservers(event);
+ return;
+ }
+
+ ServiceEvent event = new ServiceEvent(ServiceEvent.EventType.VIDEO_EVENT);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_CALL, shm.id);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_STARTED, true);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_WIDTH, shm.w);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_HEIGHT, shm.h);
+ setChanged();
+ notifyObservers(event);
+ }
+ }
+ }
+
+ @Override
+ public void decodingStopped(String id, String shmPath, boolean isMixer) {
+ Log.i(TAG, "decodingStopped() " + id);
+ Shm shm = videoInputs.remove(id);
+ if (shm != null) {
+ stopVideo(shm.id, shm.window);
+ }
+ }
+
+ @Override
+ public void getCameraInfo(String camId, IntVect formats, UintVect sizes, UintVect rates) {
+ Log.d(TAG, "getCameraInfo: " + camId + ", " + formats + ", " + sizes + ", " + rates);
+ int id = Integer.valueOf(camId);
+
+ if (id < 0 || id >= Camera.getNumberOfCameras()) {
+ return;
+ }
+
+ Camera cam;
+ try {
+ cam = Camera.open(id);
+ } catch (Exception e) {
+ Log.d(TAG, e.getMessage());
+ return;
+ }
+
+ Camera.Parameters param = cam.getParameters();
+ cam.release();
+
+ for (int fmt : param.getSupportedPreviewFormats()) {
+ formats.add(fmt);
+ }
+
+ DeviceParams p = new DeviceParams();
+
+ int MIN_WIDTH = 320;
+ Point size = new Point(0, 0);
+ /** {@link Camera.Parameters#getSupportedPreviewSizes} :
+ * "This method will always return a list with at least one element."
+ * Attempt to find the size with width closest (but above) MIN_WIDTH. */
+ for (Camera.Size s : param.getSupportedPreviewSizes()) {
+ if (s.width < s.height) {
+ continue;
+ }
+ if (size.x < MIN_WIDTH ? s.width > size.x : (s.width >= MIN_WIDTH && s.width < size.x)) {
+ size.x = s.width;
+ size.y = s.height;
+ }
+ }
+
+ p.size = size;
+
+ sizes.add(p.size.x);
+ sizes.add(p.size.y);
+ sizes.add(p.size.y);
+ sizes.add(p.size.x);
+
+ for (int fps[] : param.getSupportedPreviewFpsRange()) {
+ int rate = (fps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + fps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]) / 2;
+ rates.add(rate);
+ }
+ p.rate = rates.get(0);
+
+ p.infos = new Camera.CameraInfo();
+ Camera.getCameraInfo(id, p.infos);
+
+ mNativeParams.put(id, p);
+ }
+
+ @Override
+ public void setParameters(String camId, int format, int width, int height, int rate) {
+ Log.d(TAG, "setParameters: " + camId + ", " + format + ", " + width + ", " + height + ", " + rate);
+ int id = Integer.valueOf(camId);
+ DeviceParams deviceParams = mNativeParams.get(id);
+ VideoParams newParams = new VideoParams(id, format, deviceParams.size.x, deviceParams.size.y, rate);
+ newParams.rotWidth = width;
+ newParams.rotHeight = height;
+ WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ int rotation = rotationToDegrees(windowManager.getDefaultDisplay().getRotation());
+ if (deviceParams.infos.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+ newParams.rotation = (deviceParams.infos.orientation + rotation + 360) % 360;
+ } else {
+ newParams.rotation = (deviceParams.infos.orientation - rotation + 360) % 360;
+ }
+ mParams.put(camId, newParams);
+ }
+
+ @Override
+ public void startCapture(@Nullable String camId) {
+ VideoParams videoParams;
+
+ if (camId == null && previewParams != null) {
+ videoParams = previewParams;
+ } else if (camId != null) {
+ videoParams = mParams.get(camId);
+ } else if (mParams.size() == 2) {
+ currentCamera = cameraFront;
+ videoParams = mParams.get(cameraFront);
+ } else {
+ currentCamera = cameraBack;
+ videoParams = mParams.get(cameraBack);
+ }
+
+ SurfaceHolder surface = mCameraPreviewSurface.get();
+ if (surface == null) {
+ Log.w(TAG, "Can't start capture: no surface registered.");
+ previewParams = videoParams;
+
+ ServiceEvent event = new ServiceEvent(ServiceEvent.EventType.VIDEO_EVENT);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_START, true);
+ setChanged();
+ notifyObservers(event);
+ return;
+ }
+
+ if (videoParams == null) {
+ Log.w(TAG, "startCapture: no video parameters ");
+ return;
+ }
+ Log.d(TAG, "startCapture " + videoParams.id + " " + videoParams.width + "x" + videoParams.height + " rot" + videoParams.rotation);
+
+ final Camera preview;
+ try {
+ if (previewCamera != null) {
+ previewCamera.release();
+ previewCamera = null;
+ }
+ preview = Camera.open(videoParams.id);
+ setCameraDisplayOrientation(videoParams.id, preview);
+ } catch (Exception e) {
+ Log.e(TAG, "Camera.open: " + e.getMessage());
+ return;
+ }
+
+ try {
+ surface.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+ preview.setPreviewDisplay(surface);
+ } catch (IOException e) {
+ Log.e(TAG, "setPreviewDisplay: " + e.getMessage());
+ return;
+ }
+
+ Camera.Parameters parameters = preview.getParameters();
+ parameters.setPreviewFormat(videoParams.format);
+ parameters.setPreviewSize(videoParams.width, videoParams.height);
+ parameters.setRotation(0);
+
+ for (int[] fps : parameters.getSupportedPreviewFpsRange()) {
+ if (videoParams.rate >= fps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] &&
+ videoParams.rate <= fps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]) {
+ parameters.setPreviewFpsRange(fps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+ fps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+ }
+ }
+
+ try {
+ preview.setParameters(parameters);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Error while settings preview parameters", e);
+ }
+
+ final int videoWidth = videoParams.width;
+ final int heigth = videoParams.height;
+ final int rotation = videoParams.rotation;
+
+ preview.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
+ @Override
+ public void onPreviewFrame(byte[] data, Camera camera) {
+ setVideoFrame(data, videoWidth, heigth, rotation);
+ preview.addCallbackBuffer(data);
+ }
+ });
+
+ // enqueue first buffer
+ int bufferSize = parameters.getPreviewSize().width * parameters.getPreviewSize().height * ImageFormat.getBitsPerPixel(parameters.getPreviewFormat()) / 8;
+ preview.addCallbackBuffer(new byte[bufferSize]);
+
+ preview.setErrorCallback(new Camera.ErrorCallback() {
+ @Override
+ public void onError(int error, Camera cam) {
+ Log.w(TAG, "Camera onError " + error);
+ if (preview == cam) {
+ stopCapture();
+ }
+ }
+ });
+ try {
+ preview.startPreview();
+ } catch (RuntimeException e) {
+ Log.e(TAG, "startPreview: " + e.getMessage());
+ return;
+ }
+
+ previewCamera = preview;
+ previewParams = videoParams;
+
+ ServiceEvent event = new ServiceEvent(ServiceEvent.EventType.VIDEO_EVENT);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_CAMERA, videoParams.id == 1);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_STARTED, true);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_WIDTH, videoParams.rotWidth);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_HEIGHT, videoParams.rotHeight);
+ setChanged();
+ notifyObservers(event);
+ }
+
+ @Override
+ public void stopCapture() {
+ Log.d(TAG, "stopCapture " + previewCamera);
+ if (previewCamera != null) {
+ final Camera preview = previewCamera;
+ final VideoParams params = previewParams;
+ previewCamera = null;
+ try {
+ preview.setPreviewCallback(null);
+ preview.setErrorCallback(null);
+ preview.stopPreview();
+ preview.release();
+ } catch (Exception e) {
+ Log.e(TAG, "stopCapture error" + e);
+ }
+
+ ServiceEvent event = new ServiceEvent(ServiceEvent.EventType.VIDEO_EVENT);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_CAMERA, params.id == 1);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_STARTED, false);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_WIDTH, params.width);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_HEIGHT, params.height);
+ setChanged();
+ notifyObservers(event);
+ }
+ }
+
+ @Override
+ public void addVideoSurface(String id, Object holder) {
+ if (!(holder instanceof SurfaceHolder)) {
+ return;
+ }
+
+ Log.w(TAG, "addVideoSurface " + id + holder.hashCode());
+
+ Shm shm = videoInputs.get(id);
+ WeakReference<SurfaceHolder> surfaceHolder = new WeakReference<>((SurfaceHolder) holder);
+ videoSurfaces.put(id, surfaceHolder);
+ if (shm != null && shm.window == 0) {
+ shm.window = startVideo(shm.id, surfaceHolder.get().getSurface(), shm.w, shm.h);
+ }
+
+ if (shm == null || shm.window == 0) {
+ Log.i(TAG, "DRingService.addVideoSurface() no window !");
+
+ ServiceEvent event = new ServiceEvent(ServiceEvent.EventType.VIDEO_EVENT);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_START, true);
+ setChanged();
+ notifyObservers(event);
+ return;
+ }
+
+ ServiceEvent event = new ServiceEvent(ServiceEvent.EventType.VIDEO_EVENT);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_CALL, shm.id);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_STARTED, true);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_WIDTH, shm.w);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_HEIGHT, shm.h);
+ setChanged();
+ notifyObservers(event);
+
+ }
+
+ @Override
+ public void addPreviewVideoSurface(Object holder) {
+ if (!(holder instanceof SurfaceHolder)) {
+ return;
+ }
+
+ Log.w(TAG, "addPreviewVideoSurface " + holder.hashCode());
+
+ mCameraPreviewSurface = new WeakReference<>((SurfaceHolder) holder);
+ }
+
+ @Override
+ public void removeVideoSurface(String id) {
+ Log.i(TAG, "removeVideoSurface " + id);
+ Shm shm = videoInputs.get(id);
+ if (shm == null) {
+ return;
+ }
+ if (shm.window != 0) {
+ try {
+ stopVideo(shm.id, shm.window);
+ } catch (Exception e) {
+ Log.e(TAG, "removeVideoSurface error" + e);
+ }
+
+ shm.window = 0;
+ }
+
+ ServiceEvent event = new ServiceEvent(ServiceEvent.EventType.VIDEO_EVENT);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_CALL, shm.id);
+ event.addEventInput(ServiceEvent.EventInput.VIDEO_STARTED, false);
+ setChanged();
+ notifyObservers(event);
+ }
+
+ @Override
+ public void removePreviewVideoSurface() {
+ Log.w(TAG, "removePreviewVideoSurface");
+ mCameraPreviewSurface.clear();
+ }
+
+ @Override
+ public void switchInput(String id) {
+ Log.w(TAG, "switchInput " + id);
+
+ final int camId;
+ if (currentCamera == cameraBack) {
+ camId = cameraFront;
+ } else {
+ camId = cameraBack;
+ }
+ currentCamera = camId;
+
+ final String uri = "camera://" + camId;
+ final StringMap map = mNativeParams.get(camId).toMap(mContext.getResources().getConfiguration().orientation);
+ this.switchInput(id, uri, map);
+ }
+
+ @Override
+ public void restartCamera(String id) {
+ stopCapture();
+ setPreviewSettings();
+ final String uri = "camera://" + currentCamera;
+ final StringMap map = mNativeParams.get(currentCamera).toMap(mContext.getResources().getConfiguration().orientation);
+ this.switchInput(id, uri, map);
+ }
+
+ @Override
+ public void setPreviewSettings() {
+ Map<String, StringMap> camSettings = new HashMap<>();
+ for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
+ if (mNativeParams.get(i) != null) {
+ camSettings.put(Integer.toString(i), mNativeParams.get(i).toMap(mContext.getResources().getConfiguration().orientation));
+ Log.w(TAG, "setPreviewSettings camera:" + Integer.toString(i));
+ }
+ }
+ this.setPreviewSettings(camSettings);
+ }
+
+ @Override
+ public int getCameraCount() {
+ return Camera.getNumberOfCameras();
+ }
+
+ @Override
+ public boolean isPreviewFromFrontCamera() {
+ return Camera.getNumberOfCameras() == 1 || currentCamera == cameraFront;
+ }
+
+ private void setCameraDisplayOrientation(int camId, Camera camera) {
+ Camera.CameraInfo info = new Camera.CameraInfo();
+ Camera.getCameraInfo(camId, info);
+ WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ int rotation = rotationToDegrees(windowManager.getDefaultDisplay().getRotation());
+ int result;
+ if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+ result = (info.orientation + rotation) % 360;
+ result = (360 - result) % 360; // compensate the mirror
+ } else { // back-facing
+ result = (info.orientation - rotation + 360) % 360;
+ }
+ camera.setDisplayOrientation(result);
+ Log.w(TAG, "setCameraDisplayOrientation " + Integer.toString(rotation) + " " + Integer.toString(result));
+ }
+
+ private int rotationToDegrees(int rotation) {
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ return 0;
+ case Surface.ROTATION_90:
+ return 90;
+ case Surface.ROTATION_180:
+ return 180;
+ case Surface.ROTATION_270:
+ return 270;
+ }
+ return 0;
+ }
+
+ private static class Shm {
+ String id;
+ String path;
+ int w, h;
+ boolean mixer;
+ long window = 0;
+ }
+
+ private static class VideoParams {
+ public VideoParams(int id, int format, int width, int height, int rate) {
+ this.id = id;
+ this.format = format;
+ this.width = width;
+ this.height = height;
+ this.rate = rate;
+ }
+
+ public int id;
+ public int format;
+
+ // size as captured by Android
+ public int width;
+ public int height;
+
+ //size, rotated, as seen by the daemon
+ public int rotWidth;
+ public int rotHeight;
+
+ public int rate;
+ public int rotation;
+ }
+
+ private static class DeviceParams {
+ Point size;
+ long rate;
+ Camera.CameraInfo infos;
+
+ public StringMap toMap(int orientation) {
+ StringMap map = new StringMap();
+ boolean rotated = (size.x > size.y) == (orientation == Configuration.ORIENTATION_PORTRAIT);
+ map.set("size", Integer.toString(rotated ? size.y : size.x) + "x" + Integer.toString(rotated ? size.x : size.y));
+ map.set("rate", Long.toString(rate));
+ return map;
+ }
+ }
+}
diff --git a/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.java b/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.java
index 3b5489f..dd925f4 100644
--- a/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.java
+++ b/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.java
@@ -42,6 +42,7 @@
import cx.ring.BuildConfig;
import cx.ring.R;
+import cx.ring.client.CallActivity;
import cx.ring.client.ConversationActivity;
import cx.ring.client.HomeActivity;
import cx.ring.contactrequests.PendingContactRequestsFragment;
@@ -66,9 +67,6 @@
private static final String TAG = NotificationServiceImpl.class.getName();
- public static final String TRUST_REQUEST_NOTIFICATION_ACCOUNT_ID = "trustRequestNotificationAccountId";
- public static final String TRUST_REQUEST_NOTIFICATION_FROM = "trustRequestNotificationFrom";
-
private static final String NOTIF_CALL = "CALL";
private static final String NOTIF_MSG = "MESSAGE";
private static final String NOTIF_TRUST_REQUEST = "TRUST REQUEST";
@@ -109,12 +107,14 @@
SipCall call = conference.getParticipants().get(0);
CallContact contact = call.getContact();
- final int notificationId = getCallNotificationId(call);
+ final int notificationId = call.getCallId().hashCode();
notificationManager.cancel(notificationId);
- final Uri callUri = Uri.withAppendedPath(ContentUriHandler.CALL_CONTENT_URI, call.getCallId());
- PendingIntent gotoIntent = PendingIntent.getActivity(mContext, new Random().nextInt(),
- ActionHelper.getViewIntent(mContext, conference), PendingIntent.FLAG_ONE_SHOT);
+ PendingIntent gotoIntent = PendingIntent.getService(mContext,
+ new Random().nextInt(),
+ new Intent(DRingService.ACTION_CALL_VIEW)
+ .setClass(mContext, DRingService.class)
+ .putExtra(KEY_CALL_ID, call.getCallId()), 0);
NotificationCompat.Builder messageNotificationBuilder = new NotificationCompat.Builder(mContext);
if (conference.isOnGoing()) {
@@ -123,9 +123,9 @@
.setContentIntent(gotoIntent)
.addAction(R.drawable.ic_call_end_white, mContext.getText(R.string.action_call_hangup),
PendingIntent.getService(mContext, new Random().nextInt(),
- new Intent(LocalService.ACTION_CALL_END)
- .setClass(mContext, LocalService.class)
- .setData(callUri),
+ new Intent(DRingService.ACTION_CALL_END)
+ .setClass(mContext, DRingService.class)
+ .putExtra(KEY_CALL_ID, call.getCallId()),
PendingIntent.FLAG_ONE_SHOT));
} else if (conference.isRinging()) {
if (conference.isIncoming()) {
@@ -138,15 +138,15 @@
.setFullScreenIntent(gotoIntent, true)
.addAction(R.drawable.ic_call_end_white, mContext.getText(R.string.action_call_decline),
PendingIntent.getService(mContext, new Random().nextInt(),
- new Intent(LocalService.ACTION_CALL_REFUSE)
- .setClass(mContext, LocalService.class)
- .setData(callUri),
+ new Intent(DRingService.ACTION_CALL_REFUSE)
+ .setClass(mContext, DRingService.class)
+ .putExtra(KEY_CALL_ID, call.getCallId()),
PendingIntent.FLAG_ONE_SHOT))
.addAction(R.drawable.ic_action_accept, mContext.getText(R.string.action_call_accept),
PendingIntent.getService(mContext, new Random().nextInt(),
- new Intent(LocalService.ACTION_CALL_ACCEPT)
- .setClass(mContext, LocalService.class)
- .setData(callUri),
+ new Intent(DRingService.ACTION_CALL_ACCEPT)
+ .setClass(mContext, DRingService.class)
+ .putExtra(KEY_CALL_ID, call.getCallId()),
PendingIntent.FLAG_ONE_SHOT))
.addExtras(extras);
} else {
@@ -155,12 +155,11 @@
.setContentIntent(gotoIntent)
.addAction(R.drawable.ic_call_end_white, mContext.getText(R.string.action_call_hangup),
PendingIntent.getService(mContext, new Random().nextInt(),
- new Intent(LocalService.ACTION_CALL_END)
- .setClass(mContext, LocalService.class)
- .setData(callUri),
+ new Intent(DRingService.ACTION_CALL_END)
+ .setClass(mContext, DRingService.class)
+ .putExtra(KEY_CALL_ID, call.getCallId()),
PendingIntent.FLAG_ONE_SHOT));
}
-
} else {
return;
}
@@ -202,7 +201,7 @@
if (ConversationFragment.isTabletMode(mContext)) {
intentConversation = new Intent(LocalService.ACTION_CONV_ACCEPT)
.setClass(mContext, HomeActivity.class)
- .putExtra("conversationID", contact.getIds().get(0));
+ .putExtra(ConversationFragment.KEY_CONVERSATION_ID, contact.getIds().get(0));
} else {
intentConversation = new Intent(Intent.ACTION_VIEW)
.setClass(mContext, ConversationActivity.class)
@@ -353,11 +352,7 @@
}
@Override
- public void cancelCallNotification(SipCall call) {
- if (call == null) {
- return;
- }
- int notificationId = getCallNotificationId(call);
+ public void cancelCallNotification(int notificationId) {
notificationManager.cancel(notificationId);
mNotificationBuilders.remove(notificationId);
}
diff --git a/ring-android/app/src/main/java/cx/ring/utils/ActionHelper.java b/ring-android/app/src/main/java/cx/ring/utils/ActionHelper.java
index 0e663e1..08842f0 100644
--- a/ring-android/app/src/main/java/cx/ring/utils/ActionHelper.java
+++ b/ring-android/app/src/main/java/cx/ring/utils/ActionHelper.java
@@ -195,11 +195,6 @@
}
}
- public static Intent getViewIntent(Context context, Conference conference) {
- final android.net.Uri confUri = android.net.Uri.withAppendedPath(ContentUriHandler.CONFERENCE_CONTENT_URI, conference.getId());
- return new Intent(Intent.ACTION_VIEW).setData(confUri).setClass(context, CallActivity.class);
- }
-
public static String getShortenedNumber(String number) {
if (number != null && !number.isEmpty() && number.length() > 18) {
int size = number.length();
diff --git a/ring-android/app/src/main/res/layout-land/frag_call.xml b/ring-android/app/src/main/res/layout-land/frag_call.xml
index 277abde..8bbabda 100644
--- a/ring-android/app/src/main/res/layout-land/frag_call.xml
+++ b/ring-android/app/src/main/res/layout-land/frag_call.xml
@@ -16,7 +16,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@@ -27,184 +27,144 @@
android:id="@+id/video_preview_surface"
android:layout_width="match_parent"
android:layout_height="32dp"
+ android:layout_centerInParent="true"
android:layout_gravity="center"
- android:visibility="gone" />
+ android:visibility="gone"
+ tools:visibility="visible" />
- <FrameLayout
- android:id="@+id/inner_layout"
+ <SurfaceView
+ android:id="@+id/camera_preview_surface"
+ android:layout_width="160dp"
+ android:layout_height="120dp"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_margin="8dp"
+ android:visibility="gone"
+ tools:visibility="visible" />
+
+ <LinearLayout
+ android:id="@+id/contact_bubble_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="true">
+ android:layout_marginBottom="16dp"
+ android:orientation="horizontal"
+ android:weightSum="100">
- <SurfaceView
- android:id="@+id/camera_preview_surface"
- android:layout_width="160dp"
- android:layout_height="120dp"
- android:layout_gravity="bottom|end"
- android:layout_margin="8dp"
- android:visibility="gone" />
+ <com.skyfishjy.library.RippleBackground
+ android:id="@+id/ripple_animation"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="50"
+ app:rb_color="@color/white"
+ app:rb_duration="5000"
+ app:rb_radius="20dp"
+ app:rb_rippleAmount="3"
+ app:rb_scale="6">
+
+ <ImageView
+ android:id="@+id/contact_bubble"
+ android:layout_width="160dp"
+ android:layout_height="160dp"
+ android:layout_centerInParent="true"
+ tools:src="@drawable/ic_contact_picture" />
+ </com.skyfishjy.library.RippleBackground>
<LinearLayout
- android:id="@+id/contact_bubble_layout"
- android:layout_width="match_parent"
+ android:layout_width="0dp"
android:layout_height="match_parent"
- android:weightSum="100"
- android:orientation="horizontal"
- android:layout_marginBottom="16dp">
+ android:layout_margin="10dp"
+ android:layout_weight="50"
+ android:gravity="center"
+ android:orientation="vertical">
- <com.skyfishjy.library.RippleBackground
- android:id="@+id/ripple_animation"
- android:layout_width="0dp"
- android:layout_weight="50"
- android:layout_height="match_parent"
- app:rb_color="@color/white"
- app:rb_duration="5000"
- app:rb_radius="20dp"
- app:rb_rippleAmount="3"
- app:rb_scale="6">
+ <TextView
+ android:id="@+id/contact_bubble_txt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:ellipsize="middle"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="@color/text_color_primary_dark"
+ tools:text="Contact Name" />
- <ImageView
- android:id="@+id/contact_bubble"
- android:layout_width="160dp"
- android:layout_height="160dp"
- android:layout_centerInParent="true"
- tools:src="@drawable/ic_contact_picture" />
- </com.skyfishjy.library.RippleBackground>
+ <TextView
+ android:id="@+id/contact_bubble_num_txt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:ellipsize="middle"
+ android:paddingEnd="32dp"
+ android:paddingStart="32dp"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/text_color_secondary_dark"
+ tools:text="ring:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
+
+ <TextView
+ android:id="@+id/call_status_txt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:textColor="@color/text_color_primary_dark"
+ android:textSize="16sp"
+ tools:text="Connecting" />
<LinearLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:gravity="center"
- android:layout_weight="50"
- android:layout_margin="10dp"
- android:orientation="vertical">
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="12dp"
+ android:orientation="horizontal">
- <TextView
- android:id="@+id/contact_bubble_txt"
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/call_refuse_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:textColor="@color/text_color_primary_dark"
- tools:text="Contact Name" />
+ android:layout_margin="16dp"
+ android:contentDescription="@string/action_call_decline"
+ android:src="@drawable/ic_call_end_white"
+ app:backgroundTint="@color/error_red"
+ app:elevation="6dp"
+ app:pressedTranslationZ="12dp"
+ app:rippleColor="@android:color/white" />
- <TextView
- android:id="@+id/contact_bubble_num_txt"
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/call_accept_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:ellipsize="end"
- android:paddingEnd="32dp"
- android:paddingStart="32dp"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="@color/text_color_secondary_dark"
- tools:text="ring:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
-
- <TextView
- android:id="@+id/call_status_txt"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:textColor="@color/text_color_primary_dark"
- android:textSize="16sp"
- tools:text="Connecting" />
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="12dp"
- android:orientation="horizontal">
-
- <android.support.design.widget.FloatingActionButton
- android:id="@+id/call_refuse_btn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:contentDescription="@string/action_call_decline"
- android:src="@drawable/ic_call_end_white"
- app:backgroundTint="@color/error_red"
- app:elevation="6dp"
- app:pressedTranslationZ="12dp"
- app:rippleColor="@android:color/white" />
-
- <android.support.design.widget.FloatingActionButton
- android:id="@+id/call_accept_btn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:contentDescription="@string/action_call_accept"
- android:src="@drawable/ic_call_white"
- app:backgroundTint="#4caf50"
- app:elevation="6dp"
- app:pressedTranslationZ="12dp"
- app:rippleColor="@android:color/white" />
- </LinearLayout>
-
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/imageView"
- android:layout_gravity="center_horizontal" />
+ android:layout_margin="16dp"
+ android:contentDescription="@string/action_call_accept"
+ android:src="@drawable/ic_call_white"
+ app:backgroundTint="#4caf50"
+ app:elevation="6dp"
+ app:pressedTranslationZ="12dp"
+ app:rippleColor="@android:color/white" />
</LinearLayout>
</LinearLayout>
+ </LinearLayout>
- <RelativeLayout
- android:id="@+id/call_status_bar"
- android:layout_width="match_parent"
- android:layout_height="?android:attr/actionBarSize"
- android:visibility="visible">
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/call_hangup_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="48dp"
+ android:src="@drawable/ic_call_end_white"
+ app:backgroundTint="@color/error_red"
+ app:elevation="6dp"
+ app:pressedTranslationZ="12dp"
+ app:rippleColor="@android:color/white" />
- <ViewSwitcher
- android:id="@+id/security_switcher"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:visibility="gone">
+ <EditText
+ android:id="@+id/dialpad_edit_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:inputType="phone"
+ android:visibility="visible" />
- <ImageView
- android:id="@+id/lock_image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end|center_vertical" />
- </ViewSwitcher>
-
- <ImageView
- android:id="@+id/security_indicator"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
- android:layout_centerVertical="true"
- android:layout_margin="16dp"
- android:src="@drawable/ic_lock_white"
- android:tint="#4caf50"
- android:visibility="gone" />
-
- </RelativeLayout>
-
- <android.support.design.widget.FloatingActionButton
- android:id="@+id/call_hangup_btn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|center_horizontal"
- android:layout_margin="28dp"
- android:src="@drawable/ic_call_end_white"
- app:backgroundTint="@color/error_red"
- app:elevation="6dp"
- app:pressedTranslationZ="12dp"
- app:rippleColor="@android:color/white" />
-
- <EditText
- android:id="@+id/dialpad_edit_text"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:ems="10"
- android:inputType="phone"
- android:visibility="visible" />
-
- </FrameLayout>
-</FrameLayout>
+</RelativeLayout>
diff --git a/ring-android/app/src/main/res/layout/frag_call.xml b/ring-android/app/src/main/res/layout/frag_call.xml
index 18f4f13..b671ba3 100644
--- a/ring-android/app/src/main/res/layout/frag_call.xml
+++ b/ring-android/app/src/main/res/layout/frag_call.xml
@@ -16,7 +16,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@@ -27,180 +27,142 @@
android:id="@+id/video_preview_surface"
android:layout_width="match_parent"
android:layout_height="32dp"
+ android:layout_centerInParent="true"
android:layout_gravity="center"
- android:visibility="gone" />
+ android:visibility="gone"
+ tools:visibility="visible" />
- <FrameLayout
- android:id="@+id/inner_layout"
+ <SurfaceView
+ android:id="@+id/camera_preview_surface"
+ android:layout_width="160dp"
+ android:layout_height="120dp"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_margin="12dp"
+ android:visibility="gone"
+ tools:visibility="visible" />
+
+ <LinearLayout
+ android:id="@+id/contact_bubble_layout"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fitsSystemWindows="true">
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:gravity="center"
+ android:orientation="vertical">
- <SurfaceView
- android:id="@+id/camera_preview_surface"
- android:layout_width="160dp"
- android:layout_height="120dp"
- android:layout_gravity="bottom|end"
- android:layout_margin="8dp"
- android:visibility="gone" />
+ <com.skyfishjy.library.RippleBackground
+ android:id="@+id/ripple_animation"
+ android:layout_width="230dp"
+ android:layout_height="230dp"
+ app:rb_color="@color/white"
+ app:rb_duration="5000"
+ app:rb_radius="20dp"
+ app:rb_rippleAmount="3"
+ app:rb_scale="6">
- <RelativeLayout
- android:id="@+id/contact_bubble_layout"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:layout_marginBottom="16dp">
-
- <com.skyfishjy.library.RippleBackground
- android:id="@+id/ripple_animation"
- android:layout_width="230dp"
- android:layout_height="230dp"
+ <ImageView
+ android:id="@+id/contact_bubble"
+ android:layout_width="160dp"
+ android:layout_height="160dp"
android:layout_centerInParent="true"
- app:rb_color="@color/white"
- app:rb_duration="5000"
- app:rb_radius="20dp"
- app:rb_rippleAmount="3"
- app:rb_scale="6">
+ tools:src="@drawable/ic_contact_picture" />
+ </com.skyfishjy.library.RippleBackground>
- <ImageView
- android:id="@+id/contact_bubble"
- android:layout_width="160dp"
- android:layout_height="160dp"
- android:layout_centerInParent="true"
- tools:src="@drawable/ic_contact_picture" />
- </com.skyfishjy.library.RippleBackground>
-
- <LinearLayout
- android:layout_width="300dp"
- android:layout_height="wrap_content"
- android:layout_below="@id/ripple_animation"
- android:layout_centerHorizontal="true"
- android:layout_margin="10dp"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/contact_bubble_txt"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:textColor="@color/text_color_primary_dark"
- tools:text="Contact Name" />
-
- <TextView
- android:id="@+id/contact_bubble_num_txt"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:ellipsize="end"
- android:paddingEnd="32dp"
- android:paddingStart="32dp"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="@color/text_color_secondary_dark"
- tools:text="ring:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
-
- <TextView
- android:id="@+id/call_status_txt"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:textColor="@color/text_color_primary_dark"
- android:textSize="16sp"
- tools:text="Connecting" />
-
- </LinearLayout>
-
-
- </RelativeLayout>
-
- <LinearLayout
- android:layout_width="wrap_content"
+ <TextView
+ android:id="@+id/contact_bubble_txt"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="bottom|center_horizontal"
- android:layout_margin="12dp"
- android:orientation="horizontal">
+ android:gravity="center_horizontal"
+ android:ellipsize="middle"
+ android:paddingEnd="32dp"
+ android:paddingStart="32dp"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="@color/text_color_primary_dark"
+ tools:text="Contact Name" />
- <android.support.design.widget.FloatingActionButton
- android:id="@+id/call_refuse_btn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:contentDescription="@string/action_call_decline"
- android:src="@drawable/ic_call_end_white"
- app:backgroundTint="@color/error_red"
- app:elevation="6dp"
- app:pressedTranslationZ="12dp"
- app:rippleColor="@android:color/white" />
+ <TextView
+ android:id="@+id/contact_bubble_num_txt"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="middle"
+ android:gravity="center_horizontal"
+ android:paddingEnd="32dp"
+ android:paddingStart="32dp"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/text_color_secondary_dark"
+ android:visibility="gone"
+ tools:text="ring:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ tools:visibility="visible" />
- <android.support.design.widget.FloatingActionButton
- android:id="@+id/call_accept_btn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:contentDescription="@string/action_call_accept"
- android:src="@drawable/ic_call_white"
- app:backgroundTint="#4caf50"
- app:elevation="6dp"
- app:pressedTranslationZ="12dp"
- app:rippleColor="@android:color/white" />
- </LinearLayout>
+ <TextView
+ android:id="@+id/call_status_txt"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:paddingEnd="32dp"
+ android:paddingStart="32dp"
+ android:textColor="@color/text_color_primary_dark"
+ android:textSize="16sp"
+ tools:text="Connecting" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/contact_bubble_layout"
+ android:layout_centerHorizontal="true"
+ android:orientation="horizontal">
<android.support.design.widget.FloatingActionButton
- android:id="@+id/call_hangup_btn"
+ android:id="@+id/call_refuse_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="bottom|center_horizontal"
- android:layout_margin="28dp"
+ android:layout_margin="16dp"
+ android:contentDescription="@string/action_call_decline"
android:src="@drawable/ic_call_end_white"
- android:visibility="gone"
app:backgroundTint="@color/error_red"
app:elevation="6dp"
app:pressedTranslationZ="12dp"
app:rippleColor="@android:color/white" />
- <RelativeLayout
- android:id="@+id/call_status_bar"
- android:layout_width="match_parent"
- android:layout_height="?android:attr/actionBarSize"
- android:visibility="visible">
-
- <ViewSwitcher
- android:id="@+id/security_switcher"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:visibility="gone">
-
- <ImageView
- android:id="@+id/lock_image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end|center_vertical" />
- </ViewSwitcher>
-
- <ImageView
- android:id="@+id/security_indicator"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
- android:layout_centerVertical="true"
- android:layout_margin="16dp"
- android:src="@drawable/ic_lock_white"
- android:tint="#4caf50"
- android:visibility="gone" />
-
- </RelativeLayout>
-
- <EditText
- android:id="@+id/dialpad_edit_text"
- android:layout_width="0dp"
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/call_accept_btn"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:ems="10"
- android:inputType="phone"
- android:visibility="visible" />
+ android:layout_margin="16dp"
+ android:contentDescription="@string/action_call_accept"
+ android:src="@drawable/ic_call_white"
+ app:backgroundTint="#4caf50"
+ app:elevation="6dp"
+ app:pressedTranslationZ="12dp"
+ app:rippleColor="@android:color/white" />
- </FrameLayout>
-</FrameLayout>
+ </LinearLayout>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/call_hangup_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_centerInParent="true"
+ android:layout_marginBottom="64dp"
+ android:src="@drawable/ic_call_end_white"
+ android:visibility="gone"
+ app:backgroundTint="@color/error_red"
+ app:elevation="6dp"
+ app:pressedTranslationZ="12dp"
+ app:rippleColor="@android:color/white"
+ tools:visibility="visible" />
+
+ <EditText
+ android:id="@+id/dialpad_edit_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:inputType="phone"
+ android:visibility="visible" />
+
+</RelativeLayout>
diff --git a/ring-android/app/src/main/res/values/colors.xml b/ring-android/app/src/main/res/values/colors.xml
index d87852c..f9fae11 100644
--- a/ring-android/app/src/main/res/values/colors.xml
+++ b/ring-android/app/src/main/res/values/colors.xml
@@ -44,6 +44,7 @@
<color name="holo_blue_bright">#ff00ddff</color>
<color name="transparent_grey">#AACCCCCC</color>
+ <color name="transparent_dark_grey">#88424242</color>
<color name="text_color_primary">@color/abc_primary_text_material_light</color>
<color name="text_color_secondary">@color/abc_secondary_text_material_light</color>
diff --git a/ring-android/app/src/main/res/values/strings_call.xml b/ring-android/app/src/main/res/values/strings_call.xml
index b591eb0..e16a75a 100644
--- a/ring-android/app/src/main/res/values/strings_call.xml
+++ b/ring-android/app/src/main/res/values/strings_call.xml
@@ -22,9 +22,9 @@
-->
<resources>
<!-- SipCalls -->
- <string name="call_human_state_incoming">Ringing</string>
+ <string name="call_human_state_incoming">Incoming</string>
<string name="call_human_state_connecting">Connecting</string>
- <string name="call_human_state_ringing">Calling</string>
+ <string name="call_human_state_ringing">Ringing</string>
<string name="call_human_state_current">Talking</string>
<string name="call_human_state_hungup">Over</string>
<string name="call_human_state_busy">Busy</string>
diff --git a/ring-android/app/src/main/res/values/styles.xml b/ring-android/app/src/main/res/values/styles.xml
index 183a86e..56a56c5 100644
--- a/ring-android/app/src/main/res/values/styles.xml
+++ b/ring-android/app/src/main/res/values/styles.xml
@@ -15,9 +15,9 @@
</style>
<style name="ActionBar.Transparent" parent="@android:style/Widget.DeviceDefault.Light.ActionBar.Solid.Inverse">
- <item name="android:background">@android:color/transparent</item>
+ <item name="android:background">@color/transparent_dark_grey</item>
<item name="android:height">@dimen/abc_action_bar_default_height_material</item>
- <item name="background">@android:color/transparent</item>
+ <item name="background">@color/transparent_dark_grey</item>
</style>
<style name="ToolbarTitle" parent="@style/TextAppearance.Widget.AppCompat.Toolbar.Title">