diff --git a/.gitignore b/.gitignore index 6a273b4..c267cae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.iml +output.json .gradle /local.properties /.idea/caches diff --git a/app/build.gradle b/app/build.gradle index b35d961..d5b6fd0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "com.chif.headsetcontrolplus" minSdkVersion 23 targetSdkVersion 29 - versionCode 12 - versionName "0.2.7" + versionCode 15 + versionName "0.2.9" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/app/release/output.json b/app/release/output.json new file mode 100644 index 0000000..acae66f --- /dev/null +++ b/app/release/output.json @@ -0,0 +1 @@ +[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":15,"versionName":"0.2.9","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file diff --git a/app/src/main/java/com/chif/headsetcontrolplus/ForegroundService.java b/app/src/main/java/com/chif/headsetcontrolplus/ForegroundService.java index 477f38d..169d40f 100644 --- a/app/src/main/java/com/chif/headsetcontrolplus/ForegroundService.java +++ b/app/src/main/java/com/chif/headsetcontrolplus/ForegroundService.java @@ -52,14 +52,14 @@ public class ForegroundService extends Service { private static final Handler S_HANDLER = new Handler(); private static AudioManager sAudioManager; - private static int sKeyDownCount = 0; + private static int sKeyUpCount = 0; private final Handler mHandler = new Handler(); private MediaSessionCompat mMediaSessionCompat; private MediaPlayer mMediaPlayer; private ScreenOnOffReceiver mScreenOnOffReceiver; private String mGestureMode = "unknown"; - private Runnable mGestureLongPressed; private Runnable mGestureSinglePressed; + private Runnable mGestureDoublePressed; private Context mContext; private PlaybackStateCompat.Builder mStateBuilder; @@ -83,9 +83,6 @@ public void onCreate() { mMediaSessionCompat.setCallback(new MediaSessionCompat.Callback() { @Override public boolean onMediaButtonEvent(Intent mediaButtonEvent) { - if (mScreenOnOffReceiver.isScreenOn()) { - return super.onMediaButtonEvent(mediaButtonEvent); - } return handleMediaButton(mediaButtonEvent); } }); @@ -175,57 +172,58 @@ private boolean handleMediaButton(final Intent mediaButtonEvent) { return false; } - if (keycode != KeyEvent.KEYCODE_HEADSETHOOK && keycode != KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { - // Not interested in any other key + // Not interested in any other key + if (ServiceBase.isSupportedKey(keycode)) { Log.i(APP_TAG, "Ignored " + keycode); return false; } SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); - // Long Press. - if (action == KeyEvent.ACTION_DOWN) { - mGestureLongPressed = new Runnable() { - public void run() { - mGestureMode = "long_press"; - HeadsetControlPlusService.handleGesture("long"); - Log.w(APP_TAG, "Executed long press Action"); - } - }; - // Start tracking long press. If no action up is detected after 950ms, - // consider ut as long press. - mHandler.postDelayed(mGestureLongPressed, 900); - } - - - // Single and Double Click. + // Single, Double, Triple Click. if (action == KeyEvent.ACTION_UP) { - sKeyDownCount++; - mHandler.removeCallbacks(mGestureLongPressed); - mGestureSinglePressed = new Runnable() { - public void run() { - // Single press. - if (sKeyDownCount == 1) { - // Check if this keyup event is not following a long press event. - if (mGestureMode != "long_press") { + sKeyUpCount++; + + // Single press. + if (sKeyUpCount == 1) { + mGestureSinglePressed = new Runnable() { + public void run() { + if (sKeyUpCount == 1) { + sKeyUpCount = 0; HeadsetControlPlusService.handleGesture("single"); Log.w(APP_TAG, "Executed single press Action"); } - mGestureMode = "unknown"; } - // Double press. - if (sKeyDownCount == 2) { - if (mGestureMode != "long_press") { + }; + S_HANDLER.postDelayed(mGestureSinglePressed, 550); + } + + // Double press. + if (sKeyUpCount == 2) { + S_HANDLER.removeCallbacks(mGestureSinglePressed); + mGestureDoublePressed = new Runnable() { + public void run() { + if (sKeyUpCount == 2) { + sKeyUpCount = 0; HeadsetControlPlusService.handleGesture("double"); Log.w(APP_TAG, "Executed double press Action"); + mGestureMode = "unknown"; } - mGestureMode = "unknown"; + } - sKeyDownCount = 0; + }; + S_HANDLER.postDelayed(mGestureDoublePressed, 500); + } + + // Triple press. + if (sKeyUpCount == 3) { + S_HANDLER.removeCallbacks(mGestureDoublePressed); + if (sKeyUpCount == 3) { + sKeyUpCount = 0; + HeadsetControlPlusService.handleGesture("triple"); + Log.w(APP_TAG, "Executed triple press Action"); + mGestureMode = "unknown"; } - }; - if (sKeyDownCount == 1) { - mHandler.postDelayed(mGestureSinglePressed, 400); } } } @@ -280,9 +278,6 @@ public void onReceive(final Context context, final Intent intent) { mMediaSessionCompat.setCallback(new MediaSessionCompat.Callback() { @Override public boolean onMediaButtonEvent(final Intent mediaButtonEvent) { - if (mScreenOnOffReceiver.isScreenOn()) { - return super.onMediaButtonEvent(mediaButtonEvent); - } return handleMediaButton(mediaButtonEvent); } }); diff --git a/app/src/main/java/com/chif/headsetcontrolplus/HeadsetControlPlusService.java b/app/src/main/java/com/chif/headsetcontrolplus/HeadsetControlPlusService.java index 473377b..973ebcd 100644 --- a/app/src/main/java/com/chif/headsetcontrolplus/HeadsetControlPlusService.java +++ b/app/src/main/java/com/chif/headsetcontrolplus/HeadsetControlPlusService.java @@ -1,19 +1,18 @@ /** * HeadsetControlPlusService.java * -
Copyright 2020 github.com/nadchif + *
Copyright 2020 github.com/nadchif * -
Licensed under the Apache License, Version 2.0 (the "License"); + *
Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * -
Unless required by applicable law or agreed to in writing, software + *
Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- * @todo replace long press functionality with triple press
*/
@@ -32,11 +31,12 @@
import androidx.preference.PreferenceManager;
import com.chif.headsetcontrolplus.providers.FlashlightProvider;
import com.chif.headsetcontrolplus.providers.StravaProvider;
+import com.chif.headsetcontrolplus.shared.ServiceBase;
public class HeadsetControlPlusService extends AccessibilityService {
private static final String APP_TAG = HeadsetControlPlusService.class.getSimpleName();
private static final Handler S_HANDLER = new Handler();
- private static int sKeyDownCount = 0;
+ private static int sKeyUpCount = 0;
private static String sActionsDefault;
private static String sActionsPlayPause;
private static String sActionsNext;
@@ -51,8 +51,8 @@ public class HeadsetControlPlusService extends AccessibilityService {
private static SharedPreferences pref;
private static String mGestureMode = "unknown";
private static boolean sIsSimulation = false;
- private static Runnable sGestureLongPressed;
private static Runnable sGestureSinglePressed;
+ private static Runnable sGestureDoublePressed;
private static FlashlightProvider sFlashlightProvider;
private static StravaProvider sStravaProvider;
private static Context sContext;
@@ -95,7 +95,7 @@ private static void execAction(final String action) {
/**
* Handles gestures that were polled during screen off.
- * @param gesture - Accepts "single", "double", and "long"
+ * @param gesture - Accepts "single", "double", and "triple"
*/
public static void handleGesture(final String gesture) {
@@ -103,14 +103,14 @@ public static void handleGesture(final String gesture) {
sActionsPlayPause);
final String doublePressAction = pref.getString("hcp_gestures_double_press",
sActionsNext);
- final String longPressAction = pref.getString("hcp_gestures_long_press",
+ final String triplePressAction = pref.getString("hcp_gestures_triple_press",
sActionsPrevious);
if (gesture == "single") {
execAction(singlePressAction);
} else if (gesture == "double") {
execAction(doublePressAction);
- } else if (gesture == "long") {
- execAction(doublePressAction);
+ } else if (gesture == "triple") {
+ execAction(triplePressAction);
}
}
@@ -170,28 +170,14 @@ private static void simulateDoublePress() {
}
/**
- * Simulates a long press of the headset button. This is necessary for cases where after
- * catching the initial long press event and it is assigned to do default, you have to
+ * Simulates triple press of the headset button. This is necessary for cases where after
+ * catching the initial triple press event and it is assigned to do default, you have to
* re-stage it with the sIsSimulation set to true, to allow the event to go through this service
* uninterrupted.
+ * @todo write Triple Press Simulation function
*/
- private static void simulateLongPress() {
- sIsSimulation = true; // Set to true each time, to allow it go through and be handled by system.
-
- sAudioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_HEADSETHOOK));
- sGestureLongPressed = new Runnable() {
- public void run() {
- sAudioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_HEADSETHOOK));
- }
- };
- // Schedule keyup event after longpress timeout.
-
- //ideally use ViewConfiguration.get(this).getLongPressTimeout(), but for now will set 1000
- S_HANDLER.postDelayed(sGestureLongPressed, 1000);
+ private static void simulateTriplePress() {
- Log.i(APP_TAG, "hcp simulated long press");
}
/* Broadcast a togglepause intent */
@@ -242,7 +228,7 @@ private static void muteVolume() {
@Override
protected void onServiceConnected() {
sAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
- sActionsDefault = getString(R.string.pref_button_actions_default);
+ // sActionsDefault = getString(R.string.pref_button_actions_default);
sActionsPlayPause = getString(R.string.pref_button_actions_playpause);
sActionsNext = getString(R.string.pref_button_actions_next);
sActionsPrevious = getString(R.string.pref_button_actions_previous);
@@ -272,9 +258,7 @@ public boolean onKeyEvent(final KeyEvent event) {
return false;
}
- if (keycode != KeyEvent.KEYCODE_HEADSETHOOK
- && keycode != KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
- && keycode != KeyEvent.KEYCODE_MEDIA_PLAY) {
+ if (ServiceBase.isSupportedKey(keycode)) {
// Not interested in any other keys
Log.i(APP_TAG, "Ignored " + keycode);
return false;
@@ -290,6 +274,7 @@ public boolean onKeyEvent(final KeyEvent event) {
Log.d(APP_TAG, ("Broadcast Key " + keycode));
Intent intent = new Intent(getPackageName());
+ intent.putExtra("pressed", keycode);
sendBroadcast(intent);
@@ -297,65 +282,69 @@ public boolean onKeyEvent(final KeyEvent event) {
sActionsPlayPause);
final String doublePressAction = pref.getString("hcp_gestures_double_press",
sActionsNext);
- final String longPressAction = pref.getString("hcp_gestures_long_press",
+ final String triplePressAction = pref.getString("hcp_gestures_triple_press",
sActionsPrevious);
- // Long Press.
- if (action == KeyEvent.ACTION_DOWN) {
- sGestureLongPressed = new Runnable() {
- public void run() {
- mGestureMode = "long_press";
- Log.i(APP_TAG, "Exec Long Press Action");
- if (longPressAction.equals(sActionsDefault)) {
- simulateLongPress();
- } else {
- execAction(longPressAction);
- }
- }
- };
- // Start tracking long press. If no action up is detected after 1100ms,
- // consider ut as long press.
- S_HANDLER.postDelayed(sGestureLongPressed, 1100);
- }
-
- // Single and Double Click.
+ // Determin Single, Double or Click.
if (action == KeyEvent.ACTION_UP) {
- sKeyDownCount++;
- S_HANDLER.removeCallbacks(sGestureLongPressed);
- sGestureSinglePressed = new Runnable() {
- public void run() {
- // Single press.
- if (sKeyDownCount == 1) {
- // Check if this keyup event is not following a long press event.
- if (mGestureMode != "long_press") {
- Log.i(APP_TAG, "Exec Single Press Action");
+ sKeyUpCount++;
+
+ // Single press.
+ if (sKeyUpCount == 1) {
+ sGestureSinglePressed = new Runnable() {
+ public void run() {
+ if (sKeyUpCount == 1) {
+ sKeyUpCount = 0;
+ Log.d(APP_TAG, "Exec Single Press Action");
if (singlePressAction.equals(sActionsDefault)) {
// Simulate the original event.
simulateSinglePress();
} else {
execAction(singlePressAction);
}
+ mGestureMode = "unknown";
}
- mGestureMode = "unknown";
}
- // Double press.
- if (sKeyDownCount == 2) {
- if (mGestureMode != "long_press") {
- Log.i(APP_TAG, "Exec Double Press Action");
+ };
+ S_HANDLER.postDelayed(sGestureSinglePressed, 500);
+ }
+
+ // Double press.
+ if (sKeyUpCount == 2) {
+ S_HANDLER.removeCallbacks(sGestureSinglePressed);
+ sGestureDoublePressed = new Runnable() {
+ public void run() {
+ if (sKeyUpCount == 2) {
+ sKeyUpCount = 0;
+ Log.d(APP_TAG, "Exec Double Press Action");
if (doublePressAction.equals(sActionsDefault)) {
// Simulate the original event.
simulateDoublePress();
} else {
execAction(doublePressAction);
}
+ mGestureMode = "unknown";
}
- mGestureMode = "unknown";
+
+ }
+ };
+ S_HANDLER.postDelayed(sGestureDoublePressed, 500);
+ }
+
+ // Triple press.
+ if (sKeyUpCount == 3) {
+ S_HANDLER.removeCallbacks(sGestureDoublePressed);
+ if (sKeyUpCount == 3) {
+ sKeyUpCount = 0;
+ Log.d(APP_TAG, "Exec Triple Press Action");
+ if (doublePressAction.equals(sActionsDefault)) {
+ // Simulate the original event.
+ simulateTriplePress();
+ } else {
+ execAction(triplePressAction);
}
- sKeyDownCount = 0;
+ mGestureMode = "unknown";
}
- };
- if (sKeyDownCount == 1) {
- S_HANDLER.postDelayed(sGestureSinglePressed, 400);
}
}
return true;
diff --git a/app/src/main/java/com/chif/headsetcontrolplus/SettingsFragment.java b/app/src/main/java/com/chif/headsetcontrolplus/SettingsFragment.java
index 263cbd5..0033bb0 100644
--- a/app/src/main/java/com/chif/headsetcontrolplus/SettingsFragment.java
+++ b/app/src/main/java/com/chif/headsetcontrolplus/SettingsFragment.java
@@ -77,7 +77,7 @@ public void onResume() {
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
setPreferencesFromResource(R.xml.main_preferences, rootKey);
bindPreferenceSummaryToValue(findPreference("hcp_mode"));
- bindPreferenceSummaryToValue(findPreference("hcp_gestures_long_press"));
+ bindPreferenceSummaryToValue(findPreference("hcp_gestures_triple_press"));
bindPreferenceSummaryToValue(findPreference("hcp_gestures_single_press"));
bindPreferenceSummaryToValue(findPreference("hcp_gestures_double_press"));
mPrefForegroundSwitch = findPreference("enable_hcp_foreground_service");
diff --git a/app/src/main/java/com/chif/headsetcontrolplus/providers/StravaProvider.java b/app/src/main/java/com/chif/headsetcontrolplus/providers/StravaProvider.java
index 1cad90e..33ee0a3 100644
--- a/app/src/main/java/com/chif/headsetcontrolplus/providers/StravaProvider.java
+++ b/app/src/main/java/com/chif/headsetcontrolplus/providers/StravaProvider.java
@@ -11,29 +11,38 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.util.Log;
public class StravaProvider {
+ private static final String APP_TAG = StravaProvider.class.getSimpleName();
private Context mContext;
- /** Strava Provider.
+ /**
+ * Strava Provider.
* Handles interactions between Strava and the app
+ *
* @param context - the Context
*/
public StravaProvider(final Context context) {
this.mContext = context;
+
}
- /** Toggle Record.
+ /**
+ * Toggle Record.
* Starts an activity if its not recording.
* Stops an activity if its already recording.
- * @todo Catch activity not found exception
*/
public void toggleRecord() {
Intent intent = new Intent(Intent.ACTION_RUN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse("http://strava.com/nfc/record/toggle"));
- mContext.startActivity(intent);
+ try {
+ mContext.startActivity(intent);
+ } catch (Exception ex) {
+ Log.e(APP_TAG, ex.getMessage());
+ }
}
}
diff --git a/app/src/main/java/com/chif/headsetcontrolplus/shared/ServiceBase.java b/app/src/main/java/com/chif/headsetcontrolplus/shared/ServiceBase.java
index d6cfc23..15c507f 100644
--- a/app/src/main/java/com/chif/headsetcontrolplus/shared/ServiceBase.java
+++ b/app/src/main/java/com/chif/headsetcontrolplus/shared/ServiceBase.java
@@ -8,9 +8,9 @@
import android.content.Context;
import android.provider.Settings;
import android.text.TextUtils;
+import android.view.KeyEvent;
public class ServiceBase {
-
/**
* Checks if the Accessibility Service is enabled. Returns true if it is
* @param context - The Context
@@ -41,4 +41,15 @@ public static boolean isAccessibilityServiceEnabled(final Context context,
}
return false;
}
+
+ /**
+ * Checks if the provided keyCode is to be acknowledged as a headset key.
+ * @param keyCode - The keyCode
+ * @return true if the keyCode is headset
+ */
+ public static boolean isSupportedKey(final int keyCode) {
+ return (keyCode != KeyEvent.KEYCODE_HEADSETHOOK
+ && keyCode != KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
+ && keyCode != KeyEvent.KEYCODE_MEDIA_PLAY);
+ }
}
diff --git a/app/src/main/java/com/chif/headsetcontrolplus/slides/IntroSlideSetup.java b/app/src/main/java/com/chif/headsetcontrolplus/slides/IntroSlideSetup.java
index ab32f78..61a21f7 100644
--- a/app/src/main/java/com/chif/headsetcontrolplus/slides/IntroSlideSetup.java
+++ b/app/src/main/java/com/chif/headsetcontrolplus/slides/IntroSlideSetup.java
@@ -12,6 +12,8 @@
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -25,7 +27,9 @@
public class IntroSlideSetup extends Fragment implements ISlidePolicy {
private static final String APP_TAG = "HeadsetControlPlus";
+ private static final Handler S_HANDLER = new Handler();
private static final String DOCS_TROUBLESHOOT_MD = "https://github.com/nadchif/headset-control-plus/blob/master/docs/TROUBLESHOOT.md";
+ private static Runnable sShowHavingTrouble;
private Button mStatusMessageBtn;
private boolean mSignalReceived = false;
private BroadcastReceiver mBroadcastReceiver;
@@ -54,19 +58,41 @@ public void onReceive(Context context, Intent intent) {
editor.apply();
// Unregister the broadcast receiver after first successful reception
getActivity().unregisterReceiver(mBroadcastReceiver);
+ // Cancel the Having Trouble runnable
+ S_HANDLER.removeCallbacks(sShowHavingTrouble);
}
};
+
// This receiver will wait to hear from the accessibility service that,
// the headset button has worked
getActivity().registerReceiver(mBroadcastReceiver,
new IntentFilter(getActivity().getPackageName()));
+
+ // Show a having trouble? message after 30 seconds
+ sShowHavingTrouble = new Runnable() {
+ public void run() {
+ Log.e(APP_TAG, "Ran Having tTrouble");
+ if (!mSignalReceived) {
+ mStatusMessageBtn.setText(R.string.status_headset_setup_trouble);
+ }
+ }
+ };
+ S_HANDLER.postDelayed(sShowHavingTrouble, 25000);
+
return view;
}
@Override
public void onDestroyView() {
super.onDestroyView();
+ try {
+ // in case user is closing app
+ getActivity().unregisterReceiver(mBroadcastReceiver);
+ S_HANDLER.removeCallbacks(sShowHavingTrouble);
+ } catch (Exception ex) {
+ Log.e(APP_TAG, ex.getMessage());
+ }
}
@Override
diff --git a/app/src/main/res/layout/fragment_intro_slide_permissions.xml b/app/src/main/res/layout/fragment_intro_slide_permissions.xml
index fcc930c..133f001 100644
--- a/app/src/main/res/layout/fragment_intro_slide_permissions.xml
+++ b/app/src/main/res/layout/fragment_intro_slide_permissions.xml
@@ -6,7 +6,7 @@
android:background="@color/introPermissions"
android:gravity="center"
android:orientation="vertical"
- android:padding="60dp">
+ android:padding="48dp">