Skip to content

Commit 94f8a77

Browse files
committed
FloatingVolume: Extend volume control interface
* Volume control interface can now be moved around with (optionally) a bounce effect or be fixed in the centre Signed-off-by: Adam Myczkowski <mycaxd511@gmail.com>
1 parent f8b0b35 commit 94f8a77

File tree

14 files changed

+268
-25
lines changed

14 files changed

+268
-25
lines changed

.idea/assetWizardSettings.xml

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
0 Bytes
Binary file not shown.

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ android {
66
applicationId "com.android.mycax.floatingvolume"
77
minSdkVersion 23
88
targetSdkVersion 27
9-
versionCode 3
10-
versionName "1.0.2 beta"
9+
versionCode 4
10+
versionName "1.0.3 beta"
1111
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
1212
}
1313
buildTypes {

app/src/main/java/com/android/mycax/floatingvolume/MainActivity.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,18 @@
1919
import java.util.Objects;
2020

2121
@SuppressWarnings("deprecation")
22-
public class MainActivity extends AppCompatPreferenceActivity implements SwitchPreference.OnPreferenceChangeListener, DualButton.OnDualClickListener{
22+
public class MainActivity extends AppCompatPreferenceActivity implements SwitchPreference.OnPreferenceChangeListener, DualButton.OnDualClickListener {
2323
private DualButton FloatingService;
24+
private SwitchPreference bounceEffect;
2425
private static final String PREF_ENABLE_DARK_MODE = "enable_dark_mode_switch";
26+
private static final String PREF_ENABLE_BOUNCE = "enable_bounce_effect";
27+
private static final String PREF_DISABLE_FIXED_UI = "disable_fixed_ui";
2528
private AppUtils utils;
2629

2730
@Override
2831
protected void onCreate(Bundle savedInstanceState) {
2932
setTheme(PreferenceManager.getDefaultSharedPreferences(this)
30-
.getBoolean(PREF_ENABLE_DARK_MODE,false) ? R.style.AppTheme_Dark : R.style.AppTheme);
33+
.getBoolean(PREF_ENABLE_DARK_MODE, false) ? R.style.AppTheme_Dark : R.style.AppTheme);
3134
super.onCreate(savedInstanceState);
3235
setContentView(R.layout.activity_main);
3336
utils = new AppUtils(this);
@@ -48,8 +51,7 @@ protected void onCreate(Bundle savedInstanceState) {
4851
}
4952
if (Settings.canDrawOverlays(this) && Objects.requireNonNull(notificationManager).isNotificationPolicyAccessGranted()) {
5053
initializeView();
51-
}
52-
else Toast.makeText(this,R.string.app_permission_denied, Toast.LENGTH_LONG).show();
54+
} else Toast.makeText(this, R.string.app_permission_denied, Toast.LENGTH_LONG).show();
5355
}
5456

5557
@Override
@@ -58,6 +60,13 @@ protected void onPostCreate(Bundle savedInstanceState) {
5860
addPreferencesFromResource(R.xml.pref_main);
5961
SwitchPreference darkMode = (SwitchPreference) findPreference(PREF_ENABLE_DARK_MODE);
6062
darkMode.setOnPreferenceChangeListener(this);
63+
bounceEffect = (SwitchPreference) findPreference(PREF_ENABLE_BOUNCE);
64+
if (!PreferenceManager.getDefaultSharedPreferences(this)
65+
.getBoolean(PREF_DISABLE_FIXED_UI, false)) {
66+
bounceEffect.setEnabled(false);
67+
}
68+
SwitchPreference disableFixedUI = (SwitchPreference) findPreference(PREF_DISABLE_FIXED_UI);
69+
disableFixedUI.setOnPreferenceChangeListener(this);
6170
}
6271

6372
private void initializeView() {
@@ -66,7 +75,7 @@ private void initializeView() {
6675

6776
@Override
6877
public void onClickFirst(Button btn) {
69-
utils.manageService(true);
78+
utils.manageService(true);
7079
}
7180

7281
@Override
@@ -81,6 +90,9 @@ public boolean onPreferenceChange(Preference preference, Object object) {
8190
final Intent intent = getIntent();
8291
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
8392
startActivity(intent);
93+
} else if (preference == findPreference(PREF_DISABLE_FIXED_UI)) {
94+
bounceEffect.setEnabled(!PreferenceManager.getDefaultSharedPreferences(this)
95+
.getBoolean(PREF_DISABLE_FIXED_UI, false));
8496
}
8597
return true;
8698
}

app/src/main/java/com/android/mycax/floatingvolume/services/FloatingVolumeService.java

Lines changed: 188 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
import android.content.Intent;
88
import android.content.IntentFilter;
99
import android.content.SharedPreferences;
10+
import android.content.res.Configuration;
1011
import android.graphics.PixelFormat;
12+
import android.graphics.Point;
1113
import android.os.Build;
14+
import android.os.CountDownTimer;
1215
import android.os.IBinder;
1316
import android.preference.PreferenceManager;
1417
import android.support.annotation.Nullable;
@@ -17,6 +20,7 @@
1720
import android.util.DisplayMetrics;
1821
import android.view.Gravity;
1922
import android.view.LayoutInflater;
23+
import android.view.MotionEvent;
2024
import android.view.View;
2125
import android.view.WindowManager;
2226
import android.view.animation.Animation;
@@ -46,8 +50,14 @@ public class FloatingVolumeService extends Service implements FloatingViewListen
4650
private SeekBar mediaControl, ringerControl, alarmControl, voiceCallControl;
4751
private static final String PREF_KEY_LAST_POSITION_X = "last_position_x";
4852
private static final String PREF_KEY_LAST_POSITION_Y = "last_position_y";
53+
private static final String PREF_KEY_LAST_POSITION_X_EXPANDED = "last_position_x_expanded";
54+
private static final String PREF_KEY_LAST_POSITION_Y_EXPANDED = "last_position_y_expanded";
4955
private BroadcastReceiver RingerModeReceiver;
50-
private boolean isDarkThemeEnabled;
56+
private boolean isDarkThemeEnabled, isDisableStaticUiEnabled, isUseLastPosition, isBounceEnabled;
57+
private int x_init_cord, y_init_cord, x_init_margin, y_init_margin;
58+
private final Point szWindow = new Point();
59+
private SharedPreferences.Editor editor;
60+
private SharedPreferences sharedPref;
5161

5262
@Nullable
5363
@Override
@@ -62,6 +72,7 @@ public int onStartCommand(Intent intent, int flags, int startId) {
6272
return START_STICKY;
6373
}
6474

75+
sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
6576
final DisplayMetrics metrics = new DisplayMetrics();
6677
final WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
6778
Objects.requireNonNull(windowManager).getDefaultDisplay().getMetrics(metrics);
@@ -70,7 +81,7 @@ public int onStartCommand(Intent intent, int flags, int startId) {
7081
iconView.setOnClickListener(new View.OnClickListener() {
7182
@Override
7283
public void onClick(View v) {
73-
expandView(inflater);
84+
expandView(inflater, metrics);
7485
}
7586
});
7687

@@ -83,14 +94,17 @@ public void onClick(View v) {
8394
return START_REDELIVER_INTENT;
8495
}
8596

86-
private void expandView(LayoutInflater inflater) {
97+
private void expandView(LayoutInflater inflater, DisplayMetrics displayMetrics) {
8798
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
8899
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
89100

90-
isDarkThemeEnabled = PreferenceManager.getDefaultSharedPreferences(this)
91-
.getBoolean("enable_dark_mode_switch",false);
101+
isDarkThemeEnabled = sharedPref.getBoolean("enable_dark_mode_switch", false);
102+
isDisableStaticUiEnabled = sharedPref.getBoolean("disable_fixed_ui", false);
103+
isUseLastPosition = sharedPref.getBoolean("settings_save_last_position", false);
104+
isBounceEnabled = sharedPref.getBoolean("enable_bounce_effect", false);
92105

93-
addFloatingWidgetView(inflater);
106+
addFloatingWidgetView(inflater, displayMetrics);
107+
if (isDisableStaticUiEnabled) implementTouchListenerToFloatingWidgetView(this);
94108

95109
CardView expanded_card_view = mFloatingWidgetView.findViewById(R.id.expanded_card_view);
96110
expanded_card_view.setCardBackgroundColor(
@@ -142,7 +156,8 @@ private void expandView(LayoutInflater inflater) {
142156
}
143157

144158
@SuppressLint("InflateParams")
145-
private void addFloatingWidgetView(LayoutInflater inflater) {
159+
private void addFloatingWidgetView(LayoutInflater inflater, DisplayMetrics displayMetrics) {
160+
getWindowManagerDefaultDisplay();
146161
mFloatingWidgetView = inflater.inflate(R.layout.floating_layout, null, false);
147162

148163
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
@@ -153,9 +168,21 @@ private void addFloatingWidgetView(LayoutInflater inflater) {
153168
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
154169
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
155170
PixelFormat.TRANSLUCENT);
156-
params.gravity = Gravity.CENTER;
157-
params.x = 0;
158-
params.y = 0;
171+
if (isDisableStaticUiEnabled) {
172+
params.gravity = Gravity.TOP | Gravity.START;
173+
if (isUseLastPosition) {
174+
params.x = sharedPref.getInt(PREF_KEY_LAST_POSITION_X_EXPANDED, 0);
175+
params.y = sharedPref.getInt(PREF_KEY_LAST_POSITION_Y_EXPANDED, 0);
176+
} else {
177+
int height = displayMetrics.heightPixels;
178+
params.x = displayMetrics.widthPixels - mFloatingWidgetView.getWidth();
179+
params.y = (height / 4);
180+
}
181+
} else {
182+
params.gravity = Gravity.CENTER;
183+
params.x = 0;
184+
params.y = 0;
185+
}
159186
params.windowAnimations = android.R.style.Animation_Dialog;
160187

161188
mWindowManager.addView(mFloatingWidgetView, params);
@@ -193,16 +220,16 @@ private void implementVolumeFeatures() {
193220

194221
change_ringer_mode = mFloatingWidgetView.findViewById(R.id.imageViewModeSwitch);
195222
final Animation fab_open = AnimationUtils.loadAnimation(this, R.anim.fab_open_0_to_1);
196-
RingerModeReceiver =new BroadcastReceiver(){
223+
RingerModeReceiver = new BroadcastReceiver() {
197224
@Override
198225
public void onReceive(Context context, Intent intent) {
199226
change_ringer_mode.setImageResource(getCurrentRingerModeDrawable());
200227
change_ringer_mode.startAnimation(fab_open);
201228
}
202229
};
203-
IntentFilter filter=new IntentFilter(
230+
IntentFilter filter = new IntentFilter(
204231
AudioManager.RINGER_MODE_CHANGED_ACTION);
205-
registerReceiver(RingerModeReceiver,filter);
232+
registerReceiver(RingerModeReceiver, filter);
206233
change_ringer_mode.setOnClickListener(this);
207234
}
208235

@@ -324,7 +351,7 @@ public void onFinishFloatingView() {
324351
@Override
325352
public void onTouchFinished(boolean isFinishing, int x, int y) {
326353
if (!isFinishing) {
327-
final SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
354+
editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
328355
editor.putInt(PREF_KEY_LAST_POSITION_X, x);
329356
editor.putInt(PREF_KEY_LAST_POSITION_Y, y);
330357
editor.apply();
@@ -364,7 +391,6 @@ private FloatingViewManager.Options loadOptions(DisplayMetrics metrics) {
364391
final FloatingViewManager.Options options = new FloatingViewManager.Options();
365392
final SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
366393

367-
final boolean isUseLastPosition = sharedPref.getBoolean("settings_save_last_position", false);
368394
if (isUseLastPosition) {
369395
final int defaultX = options.floatingViewX;
370396
final int defaultY = options.floatingViewY;
@@ -378,4 +404,151 @@ private FloatingViewManager.Options loadOptions(DisplayMetrics metrics) {
378404

379405
return options;
380406
}
407+
408+
private void implementTouchListenerToFloatingWidgetView(final Context context) {
409+
mFloatingWidgetView.findViewById(R.id.root_container).setOnTouchListener(new View.OnTouchListener() {
410+
@SuppressLint("ClickableViewAccessibility")
411+
@Override
412+
public boolean onTouch(View v, MotionEvent event) {
413+
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) mFloatingWidgetView.getLayoutParams();
414+
415+
int x_cord = (int) event.getRawX();
416+
int y_cord = (int) event.getRawY();
417+
418+
int x_cord_Destination, y_cord_Destination;
419+
420+
switch (event.getAction()) {
421+
case MotionEvent.ACTION_DOWN:
422+
423+
x_init_cord = x_cord;
424+
y_init_cord = y_cord;
425+
426+
x_init_margin = layoutParams.x;
427+
y_init_margin = layoutParams.y;
428+
429+
return true;
430+
case MotionEvent.ACTION_UP:
431+
boolean isClicked = false;
432+
int x_diff = x_cord - x_init_cord;
433+
int y_diff = y_cord - y_init_cord;
434+
435+
if (Math.abs(x_diff) < 5 && Math.abs(y_diff) < 5) isClicked = true;
436+
437+
y_cord_Destination = y_init_margin + y_diff;
438+
439+
int barHeight = getStatusBarHeight();
440+
if (y_cord_Destination < 0) y_cord_Destination = 0;
441+
else if (y_cord_Destination + (mFloatingWidgetView.getHeight() + barHeight) > szWindow.y) {
442+
y_cord_Destination = szWindow.y - (mFloatingWidgetView.getHeight() + barHeight);
443+
}
444+
445+
layoutParams.y = y_cord_Destination;
446+
447+
if (!isClicked) resetPosition(x_cord);
448+
449+
return true;
450+
case MotionEvent.ACTION_MOVE:
451+
int x_diff_move = x_cord - x_init_cord;
452+
int y_diff_move = y_cord - y_init_cord;
453+
454+
x_cord_Destination = x_init_margin + x_diff_move;
455+
y_cord_Destination = y_init_margin + y_diff_move;
456+
457+
layoutParams.x = x_cord_Destination;
458+
layoutParams.y = y_cord_Destination;
459+
460+
mWindowManager.updateViewLayout(mFloatingWidgetView, layoutParams);
461+
editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
462+
editor.putInt(PREF_KEY_LAST_POSITION_X_EXPANDED, layoutParams.x);
463+
editor.putInt(PREF_KEY_LAST_POSITION_Y_EXPANDED, layoutParams.y);
464+
editor.apply();
465+
return true;
466+
}
467+
return false;
468+
}
469+
});
470+
}
471+
472+
private void resetPosition(int x_cord_now) {
473+
if (x_cord_now <= szWindow.x / 2) moveToLeft(x_cord_now);
474+
else moveToRight(x_cord_now);
475+
}
476+
477+
private void moveToLeft(final int current_x_cord) {
478+
new CountDownTimer(500, 5) {
479+
final WindowManager.LayoutParams mParams = (WindowManager.LayoutParams) mFloatingWidgetView.getLayoutParams();
480+
481+
public void onTick(long t) {
482+
long step = (500 - t) / 5;
483+
484+
mParams.x = 0 - (int) (current_x_cord * current_x_cord * step);
485+
486+
if (isBounceEnabled)
487+
mParams.x = 0 - (int) (double) bounceValue(step, current_x_cord);
488+
489+
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
490+
}
491+
492+
public void onFinish() {
493+
mParams.x = 0;
494+
495+
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
496+
}
497+
}.start();
498+
}
499+
500+
private void moveToRight(final int current_x_cord) {
501+
502+
new CountDownTimer(500, 5) {
503+
final WindowManager.LayoutParams mParams = (WindowManager.LayoutParams) mFloatingWidgetView.getLayoutParams();
504+
505+
public void onTick(long t) {
506+
long step = (500 - t) / 5;
507+
508+
mParams.x = (int) (szWindow.x + (current_x_cord * current_x_cord * step) - mFloatingWidgetView.getWidth());
509+
510+
if (isBounceEnabled)
511+
mParams.x = szWindow.x + (int) (double) bounceValue(step, current_x_cord) - mFloatingWidgetView.getWidth();
512+
513+
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
514+
}
515+
516+
public void onFinish() {
517+
mParams.x = szWindow.x - mFloatingWidgetView.getWidth();
518+
519+
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
520+
}
521+
}.start();
522+
}
523+
524+
private int getStatusBarHeight() {
525+
return (int) Math.ceil(25 * getApplicationContext().getResources().getDisplayMetrics().density);
526+
}
527+
528+
private double bounceValue(long step, long scale) {
529+
return scale * Math.exp(-0.055 * step) * Math.cos(0.08 * step);
530+
}
531+
532+
@Override
533+
public void onConfigurationChanged(Configuration newConfig) {
534+
super.onConfigurationChanged(newConfig);
535+
536+
getWindowManagerDefaultDisplay();
537+
538+
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) mFloatingWidgetView.getLayoutParams();
539+
540+
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
541+
if (layoutParams.y + (mFloatingWidgetView.getHeight() + getStatusBarHeight()) > szWindow.y) {
542+
layoutParams.y = szWindow.y - (mFloatingWidgetView.getHeight() + getStatusBarHeight());
543+
mWindowManager.updateViewLayout(mFloatingWidgetView, layoutParams);
544+
}
545+
if (layoutParams.x != 0 && layoutParams.x < szWindow.x) resetPosition(szWindow.x);
546+
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
547+
if (layoutParams.x > szWindow.x) resetPosition(szWindow.x);
548+
}
549+
}
550+
551+
private void getWindowManagerDefaultDisplay() {
552+
mWindowManager.getDefaultDisplay().getSize(szWindow);
553+
}
381554
}

0 commit comments

Comments
 (0)