From e7d67fdfeb7fb626e0aed06de89e9136710d66bf Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Mon, 19 Nov 2018 13:39:57 +0100 Subject: [PATCH] Gymnastics to allow accessibility tools to answer or decline call when screen is locked (https://developer.android.com/guide/topics/ui/accessibility/custom-views#custom-touch-events) --- .../linphone/call/CallIncomingActivity.java | 108 ++++----------- .../linphone/ui/CallIncomingAnswerButton.java | 125 ++++++++++++++++++ .../ui/CallIncomingButtonListener.java | 24 ++++ .../ui/CallIncomingDeclineButton.java | 119 +++++++++++++++++ app/src/main/res/layout/call_incoming.xml | 112 +++------------- .../layout/call_incoming_answer_button.xml | 47 +++++++ .../layout/call_incoming_decline_button.xml | 47 +++++++ 7 files changed, 403 insertions(+), 179 deletions(-) create mode 100644 app/src/main/java/org/linphone/ui/CallIncomingAnswerButton.java create mode 100644 app/src/main/java/org/linphone/ui/CallIncomingButtonListener.java create mode 100644 app/src/main/java/org/linphone/ui/CallIncomingDeclineButton.java create mode 100644 app/src/main/res/layout/call_incoming_answer_button.xml create mode 100644 app/src/main/res/layout/call_incoming_decline_button.xml diff --git a/app/src/main/java/org/linphone/call/CallIncomingActivity.java b/app/src/main/java/org/linphone/call/CallIncomingActivity.java index b185bdaea..49401bdd4 100644 --- a/app/src/main/java/org/linphone/call/CallIncomingActivity.java +++ b/app/src/main/java/org/linphone/call/CallIncomingActivity.java @@ -52,6 +52,9 @@ import org.linphone.core.CallParams; import org.linphone.core.Core; import org.linphone.core.CoreListenerStub; import org.linphone.mediastream.Log; +import org.linphone.ui.CallIncomingAnswerButton; +import org.linphone.ui.CallIncomingButtonListener; +import org.linphone.ui.CallIncomingDeclineButton; import java.util.ArrayList; import java.util.List; @@ -61,14 +64,13 @@ public class CallIncomingActivity extends LinphoneGenericActivity { private TextView name, number; private ImageView contactPicture, acceptIcon; - private LinearLayout accept, decline; + private CallIncomingAnswerButton accept; + private CallIncomingDeclineButton decline; private Call mCall; private CoreListenerStub mListener; private LinearLayout acceptUnlock; private LinearLayout declineUnlock; - private boolean alreadyAcceptedOrDeniedCall, begin; - private float answerX, oldMove; - private float declineX; + private boolean alreadyAcceptedOrDeniedCall; private KeyguardManager mKeyguardManager; public static CallIncomingActivity instance() { @@ -98,13 +100,12 @@ public class CallIncomingActivity extends LinphoneGenericActivity { int flags = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; getWindow().addFlags(flags); - final int screenWidth = getResources().getDisplayMetrics().widthPixels; - acceptUnlock = findViewById(R.id.acceptUnlock); declineUnlock = findViewById(R.id.declineUnlock); + accept = findViewById(R.id.answer_button); + decline = findViewById(R.id.decline_button); acceptIcon = findViewById(R.id.acceptIcon); - accept = findViewById(R.id.accept); lookupCurrentCall(); if (LinphonePreferences.instance() != null && mCall != null && mCall.getRemoteParams() != null && @@ -113,86 +114,29 @@ public class CallIncomingActivity extends LinphoneGenericActivity { acceptIcon.setImageResource(R.drawable.call_video_start); } - decline = findViewById(R.id.decline); - mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); boolean doNotUseSliders = getResources().getBoolean(R.bool.do_not_use_sliders_to_answer_hangup_call_if_phone_unlocked); if (doNotUseSliders && !mKeyguardManager.inKeyguardRestrictedInputMode()) { - accept.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - answer(); - } - }); - - decline.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - decline(); - } - }); + accept.setSliderMode(false); + decline.setSliderMode(false); } else { - acceptUnlock.setVisibility(View.VISIBLE); - accept.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View view, MotionEvent motionEvent) { - float curX; - switch (motionEvent.getAction()) { - case MotionEvent.ACTION_DOWN: - decline.setVisibility(View.GONE); - answerX = motionEvent.getX() - accept.getWidth(); - begin = true; - oldMove = 0; - break; - case MotionEvent.ACTION_MOVE: - curX = motionEvent.getX() - accept.getWidth(); - view.scrollBy((int) (answerX - curX), view.getScrollY()); - oldMove -= answerX - curX; - answerX = curX; - if (oldMove < -25) - begin = false; - if (curX < (screenWidth / 4) - accept.getWidth() && !begin) { - answer(); - return true; - } - break; - case MotionEvent.ACTION_UP: - decline.setVisibility(View.VISIBLE); - view.scrollTo(0, view.getScrollY()); - break; - } - return true; - } - }); - - declineUnlock.setVisibility(View.VISIBLE); - decline.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View view, MotionEvent motionEvent) { - float curX; - switch (motionEvent.getAction()) { - case MotionEvent.ACTION_DOWN: - accept.setVisibility(View.GONE); - declineX = motionEvent.getX(); - break; - case MotionEvent.ACTION_MOVE: - curX = motionEvent.getX(); - view.scrollBy((int) (declineX - curX), view.getScrollY()); - declineX = curX; - if (curX > (3 * screenWidth / 4)) { - decline(); - return true; - } - break; - case MotionEvent.ACTION_UP: - accept.setVisibility(View.VISIBLE); - view.scrollTo(0, view.getScrollY()); - break; - } - return true; - } - }); + accept.setSliderMode(true); + decline.setSliderMode(true); + accept.setDeclineButton(decline); + decline.setAnswerButton(accept); } + accept.setListener(new CallIncomingButtonListener() { + @Override + public void onAction() { + answer(); + } + }); + decline.setListener(new CallIncomingButtonListener() { + @Override + public void onAction() { + decline(); + } + }); mListener = new CoreListenerStub() { @Override diff --git a/app/src/main/java/org/linphone/ui/CallIncomingAnswerButton.java b/app/src/main/java/org/linphone/ui/CallIncomingAnswerButton.java new file mode 100644 index 000000000..baf8fc994 --- /dev/null +++ b/app/src/main/java/org/linphone/ui/CallIncomingAnswerButton.java @@ -0,0 +1,125 @@ +package org.linphone.ui; + +/* +CallIncomingAnswerButton.java +Copyright (C) 2018 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import android.content.Context; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.LinearLayout; + +import org.linphone.R; + +public class CallIncomingAnswerButton extends LinearLayout implements View.OnClickListener, View.OnTouchListener { + private LinearLayout mRoot; + private boolean mUseSliderMode = false; + private CallIncomingButtonListener mListener; + private View mDeclineButton; + + private int mScreenWidth; + private boolean mBegin; + private float mAnswerX, mOldSize; + + public CallIncomingAnswerButton(Context context) { + super(context); + init(); + } + + public CallIncomingAnswerButton(Context context,@Nullable AttributeSet attrs) { + super(context, attrs); + init(); + } + + public CallIncomingAnswerButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + public void setSliderMode(boolean enabled) { + mUseSliderMode = enabled; + findViewById(R.id.acceptUnlock).setVisibility(enabled ? VISIBLE : GONE); + } + + public void setListener(CallIncomingButtonListener listener) { + mListener = listener; + } + + public void setDeclineButton(View decline) { + mDeclineButton = decline; + } + + private void init() { + inflate(getContext(), R.layout.call_incoming_answer_button, this); + mRoot = findViewById(R.id.root); + mRoot.setOnClickListener(this); + mRoot.setOnTouchListener(this); + mScreenWidth = getResources().getDisplayMetrics().widthPixels; + } + + @Override + public void onClick(View v) { + if (!mUseSliderMode) { + performClick(); + } + } + + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + if (mUseSliderMode) { + float curX; + switch (motionEvent.getAction()) { + case MotionEvent.ACTION_DOWN: + mDeclineButton.setVisibility(View.GONE); + mAnswerX = motionEvent.getX() - mRoot.getWidth(); + mBegin = true; + mOldSize = 0; + break; + case MotionEvent.ACTION_MOVE: + curX = motionEvent.getX() - mRoot.getWidth(); + view.scrollBy((int) (mAnswerX - curX), view.getScrollY()); + mOldSize -= mAnswerX - curX; + mAnswerX = curX; + if (mOldSize < -25) + mBegin = false; + if (curX < (mScreenWidth / 4) - mRoot.getWidth() && !mBegin) { + performClick(); + return true; + } + break; + case MotionEvent.ACTION_UP: + mDeclineButton.setVisibility(View.VISIBLE); + view.scrollTo(0, view.getScrollY()); + break; + } + return true; + } + return false; + } + + @Override + public boolean performClick() { + super.performClick(); + if (mListener != null) { + mListener.onAction(); + } + return true; + } +} diff --git a/app/src/main/java/org/linphone/ui/CallIncomingButtonListener.java b/app/src/main/java/org/linphone/ui/CallIncomingButtonListener.java new file mode 100644 index 000000000..738baba16 --- /dev/null +++ b/app/src/main/java/org/linphone/ui/CallIncomingButtonListener.java @@ -0,0 +1,24 @@ +package org.linphone.ui; + +/* +CallIncomingButtonListener.java +Copyright (C) 2018 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +public interface CallIncomingButtonListener { + void onAction(); +} diff --git a/app/src/main/java/org/linphone/ui/CallIncomingDeclineButton.java b/app/src/main/java/org/linphone/ui/CallIncomingDeclineButton.java new file mode 100644 index 000000000..f775ccc01 --- /dev/null +++ b/app/src/main/java/org/linphone/ui/CallIncomingDeclineButton.java @@ -0,0 +1,119 @@ +package org.linphone.ui; + +/* +CallIncomingDeclineButton.java +Copyright (C) 2018 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import android.content.Context; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.LinearLayout; + +import org.linphone.R; + +public class CallIncomingDeclineButton extends LinearLayout implements View.OnClickListener, View.OnTouchListener { + private LinearLayout mRoot; + private boolean mUseSliderMode = false; + private CallIncomingButtonListener mListener; + private View mAnswerButton; + + private int mScreenWidth; + private float mDeclineX; + + public CallIncomingDeclineButton(Context context) { + super(context); + init(); + } + + public CallIncomingDeclineButton(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(); + } + + public CallIncomingDeclineButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + public void setSliderMode(boolean enabled) { + mUseSliderMode = enabled; + findViewById(R.id.declineUnlock).setVisibility(enabled ? VISIBLE : GONE); + } + + public void setListener(CallIncomingButtonListener listener) { + mListener = listener; + } + + public void setAnswerButton(View answer) { + mAnswerButton = answer; + } + + private void init() { + inflate(getContext(), R.layout.call_incoming_decline_button, this); + mRoot = findViewById(R.id.root); + mRoot.setOnClickListener(this); + mRoot.setOnTouchListener(this); + mScreenWidth = getResources().getDisplayMetrics().widthPixels; + } + + @Override + public void onClick(View v) { + if (!mUseSliderMode) { + performClick(); + } + } + + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + if (mUseSliderMode) { + float curX; + switch (motionEvent.getAction()) { + case MotionEvent.ACTION_DOWN: + mAnswerButton.setVisibility(View.GONE); + mDeclineX = motionEvent.getX(); + break; + case MotionEvent.ACTION_MOVE: + curX = motionEvent.getX(); + view.scrollBy((int) (mDeclineX - curX), view.getScrollY()); + mDeclineX = curX; + if (curX > (3 * mScreenWidth / 4)) { + performClick(); + return true; + } + break; + case MotionEvent.ACTION_UP: + mAnswerButton.setVisibility(View.VISIBLE); + view.scrollTo(0, view.getScrollY()); + break; + } + return true; + } + return false; + } + + @Override + public boolean performClick() { + super.performClick(); + if (mListener != null) { + mListener.onAction(); + } + return true; + } +} diff --git a/app/src/main/res/layout/call_incoming.xml b/app/src/main/res/layout/call_incoming.xml index c7200e7c7..492318e64 100644 --- a/app/src/main/res/layout/call_incoming.xml +++ b/app/src/main/res/layout/call_incoming.xml @@ -86,107 +86,25 @@ - + - + android:layout_height="match_parent" + android:layout_weight="1" /> - - - - - - - - - - - - - - - + android:layout_height="match_parent" + android:layout_weight="1" /> - - - - - - - - - - - - - - - + diff --git a/app/src/main/res/layout/call_incoming_answer_button.xml b/app/src/main/res/layout/call_incoming_answer_button.xml new file mode 100644 index 000000000..0ad77356c --- /dev/null +++ b/app/src/main/res/layout/call_incoming_answer_button.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/call_incoming_decline_button.xml b/app/src/main/res/layout/call_incoming_decline_button.xml new file mode 100644 index 000000000..a13d02808 --- /dev/null +++ b/app/src/main/res/layout/call_incoming_decline_button.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + \ No newline at end of file