Skip to content
This repository was archived by the owner on Feb 3, 2020. It is now read-only.

Commit b95b5d5

Browse files
Merge pull request #92 from recruit-lifestyle/feature/2.4.0-beta01
Ver 2.4.0 beta01
2 parents 3fcb929 + 857ef6c commit b95b5d5

File tree

7 files changed

+116
-60
lines changed

7 files changed

+116
-60
lines changed

README.md

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -123,25 +123,17 @@ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
123123

124124
9) Add DisplayCutout process(API Level >= 28)
125125

126-
Note: `DisplayCutout` is obtained from `Fragment` or `Activity` (You can not get `DisplayCutout` from `Service`)
127-
Note: You must set the `DisplayCutout` obtained on portrait orientation.
128-
Note: You must not set `windowLayoutInDisplayCutoutMode` to `never` when getting a `DisplayCutout`.
126+
Call `FloatingViewManager.findCutoutSafeArea(activity)`.
127+
Note: Activity must be portrait oriented.
128+
Note: You must not set `windowLayoutInDisplayCutoutMode` to `never`.
129129

130130
example)
131131

132132
- FloatingViewControlFragment.java
133133

134134
```java
135-
final Rect safeInsetRect = new Rect();
136-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
137-
final DisplayCutout displayCutout = activity.getWindow().getDecorView().getRootWindowInsets().getDisplayCutout();
138-
if (displayCutout != null) {
139-
safeInsetRect.set(displayCutout.getSafeInsetLeft(), displayCutout.getSafeInsetTop(), displayCutout.getSafeInsetRight(), displayCutout.getSafeInsetBottom());
140-
}
141-
}
142-
143135
final Intent intent = new Intent(activity, ChatHeadService.class);
144-
intent.putExtra(ChatHeadService.EXTRA_CUTOUT_SAFE_AREA, safeInsetRect);
136+
intent.putExtra(ChatHeadService.EXTRA_CUTOUT_SAFE_AREA, FloatingViewManager.findCutoutSafeArea(activity));
145137
ContextCompat.startForegroundService(activity, intent);
146138
```
147139

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ buildscript {
66
google()
77
}
88
dependencies {
9-
classpath 'com.android.tools.build:gradle:3.3.0-alpha11'
9+
classpath 'com.android.tools.build:gradle:3.2.0'
1010
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
1111
// NOTE: Do not place your application dependencies here; they belong
1212
// in the individual module build.gradle files

library/build.gradle

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ apply plugin: 'com.android.library'
22

33
android {
44
compileSdkVersion 28
5-
buildToolsVersion '28.0.2'
6-
75
defaultConfig {
86
minSdkVersion 14
97
targetSdkVersion 28
@@ -13,9 +11,9 @@ android {
1311
}
1412

1513
dependencies {
16-
implementation 'com.android.support:support-annotations:28.0.0-rc02'
17-
implementation 'com.android.support:support-compat:28.0.0-rc02'
18-
implementation 'com.android.support:support-dynamic-animation:28.0.0-rc02'
14+
implementation 'com.android.support:support-annotations:28.0.0'
15+
implementation 'com.android.support:support-compat:28.0.0'
16+
implementation 'com.android.support:support-dynamic-animation:28.0.0'
1917
}
2018

2119
// build a jar with source files
@@ -25,7 +23,7 @@ task sourcesJar(type: Jar) {
2523
}
2624

2725
task javadoc(type: Javadoc) {
28-
failOnError false
26+
failOnError false
2927
source = android.sourceSets.main.java.sourceFiles
3028
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
3129
}

library/src/main/java/jp/co/recruit_lifestyle/android/floatingview/FloatingView.java

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -284,15 +284,15 @@ class FloatingView extends FrameLayout implements ViewTreeObserver.OnPreDrawList
284284
/**
285285
* status bar's height(landscape)
286286
*/
287-
private int mBaseStatusBarRotatedHeight;
287+
private final int mBaseStatusBarRotatedHeight;
288288

289289
/**
290290
* Current status bar's height
291291
*/
292292
private int mStatusBarHeight;
293293

294294
/**
295-
* Navigation bar's height(portlait)
295+
* Navigation bar's height(portrait)
296296
*/
297297
private final int mBaseNavigationBarHeight;
298298

@@ -568,17 +568,17 @@ public boolean onPreDraw() {
568568
* @param isHideStatusBar If true, the status bar is hidden
569569
* @param isHideNavigationBar If true, the navigation bar is hidden
570570
* @param isPortrait If true, the device orientation is portrait
571-
* @param windowLeftOffset Left side offset of device display
571+
* @param windowRect {@link Rect} of system window
572572
*/
573-
void onUpdateSystemLayout(boolean isHideStatusBar, boolean isHideNavigationBar, boolean isPortrait, int windowLeftOffset) {
573+
void onUpdateSystemLayout(boolean isHideStatusBar, boolean isHideNavigationBar, boolean isPortrait, Rect windowRect) {
574574
// status bar
575575
updateStatusBarHeight(isHideStatusBar, isPortrait);
576576
// touch X offset(support Cutout)
577-
updateTouchXOffset(isHideNavigationBar, windowLeftOffset);
577+
updateTouchXOffset(isHideNavigationBar, windowRect.left);
578578
// touch Y offset(support Cutout)
579579
mTouchYOffset = isPortrait ? mSafeInsetRect.top : 0;
580580
// navigation bar
581-
updateNavigationBarOffset(isHideNavigationBar, isPortrait);
581+
updateNavigationBarOffset(isHideNavigationBar, isPortrait, windowRect);
582582
refreshLimitRect();
583583
}
584584

@@ -636,17 +636,54 @@ private void updateTouchXOffset(boolean isHideNavigationBar, int windowLeftOffse
636636
*
637637
* @param isHideNavigationBar If true, the navigation bar is hidden
638638
* @param isPortrait If true, the device orientation is portrait
639-
*/
640-
private void updateNavigationBarOffset(boolean isHideNavigationBar, boolean isPortrait) {
639+
* @param windowRect {@link Rect} of system window
640+
*/
641+
private void updateNavigationBarOffset(boolean isHideNavigationBar, boolean isPortrait, Rect windowRect) {
642+
int currentNavigationBarHeight = 0;
643+
int currentNavigationBarWidth = 0;
644+
int navigationBarVerticalDiff = 0;
645+
final boolean hasSoftNavigationBar = hasSoftNavigationBar();
646+
// auto hide navigation bar(Galaxy S8, S9 and so on.)
647+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
648+
final DisplayMetrics realDisplayMetrics = new DisplayMetrics();
649+
mWindowManager.getDefaultDisplay().getRealMetrics(realDisplayMetrics);
650+
currentNavigationBarHeight = realDisplayMetrics.heightPixels - windowRect.bottom;
651+
currentNavigationBarWidth = realDisplayMetrics.widthPixels - mMetrics.widthPixels;
652+
navigationBarVerticalDiff = mBaseNavigationBarHeight - currentNavigationBarHeight;
653+
}
654+
641655
if (!isHideNavigationBar) {
642-
mNavigationBarVerticalOffset = 0;
656+
// auto hide navigation bar
657+
// 他デバイスとの矛盾をもとに推測する
658+
// 1.デバイスに組み込まれたナビゲーションバー(mBaseNavigationBarHeight == 0)はシステムの状態によって高さに差が発生しない
659+
// 2.デバイスに組み込まれたナビゲーションバー(!hasSoftNavigationBar)は意図的にBaseを0にしているので、矛盾している
660+
if (navigationBarVerticalDiff != 0 && mBaseNavigationBarHeight == 0 ||
661+
!hasSoftNavigationBar && mBaseNavigationBarHeight != 0) {
662+
if (hasSoftNavigationBar) {
663+
// 1.auto hide mode -> show mode
664+
// 2.show mode -> auto hide mode -> home
665+
mNavigationBarVerticalOffset = 0;
666+
} else {
667+
// show mode -> home
668+
mNavigationBarVerticalOffset = -currentNavigationBarHeight;
669+
}
670+
} else {
671+
// normal device
672+
mNavigationBarVerticalOffset = 0;
673+
}
674+
643675
mNavigationBarHorizontalOffset = 0;
644676
return;
645677
}
646678

647679
// If the portrait, is displayed at the bottom of the screen
648680
if (isPortrait) {
649-
mNavigationBarVerticalOffset = mBaseNavigationBarHeight;
681+
// auto hide navigation bar
682+
if (!hasSoftNavigationBar && mBaseNavigationBarHeight != 0) {
683+
mNavigationBarVerticalOffset = 0;
684+
} else {
685+
mNavigationBarVerticalOffset = mBaseNavigationBarHeight;
686+
}
650687
mNavigationBarHorizontalOffset = 0;
651688
return;
652689
}
@@ -658,7 +695,17 @@ private void updateNavigationBarOffset(boolean isHideNavigationBar, boolean isPo
658695
mNavigationBarHorizontalOffset = 0;
659696
} else {
660697
mNavigationBarVerticalOffset = 0;
661-
mNavigationBarHorizontalOffset = mBaseNavigationBarRotatedHeight;
698+
// auto hide navigation bar
699+
// 他デバイスとの矛盾をもとに推測する
700+
// 1.デバイスに組み込まれたナビゲーションバー(!hasSoftNavigationBar)は、意図的にBaseを0にしているので、矛盾している
701+
if (!hasSoftNavigationBar && mBaseNavigationBarRotatedHeight != 0) {
702+
mNavigationBarHorizontalOffset = 0;
703+
} else if (hasSoftNavigationBar && mBaseNavigationBarRotatedHeight == 0) {
704+
// 2.ソフトナビゲーションバーの場合、Baseが設定されるため矛盾している
705+
mNavigationBarHorizontalOffset = currentNavigationBarWidth;
706+
} else {
707+
mNavigationBarHorizontalOffset = mBaseNavigationBarRotatedHeight;
708+
}
662709
}
663710
}
664711

library/src/main/java/jp/co/recruit_lifestyle/android/floatingview/FloatingViewManager.java

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package jp.co.recruit_lifestyle.android.floatingview;
1818

19+
import android.app.Activity;
1920
import android.content.Context;
2021
import android.content.res.Configuration;
2122
import android.content.res.Resources;
@@ -24,7 +25,9 @@
2425
import android.os.Build;
2526
import android.support.annotation.DrawableRes;
2627
import android.support.annotation.IntDef;
28+
import android.support.annotation.NonNull;
2729
import android.util.DisplayMetrics;
30+
import android.view.DisplayCutout;
2831
import android.view.HapticFeedbackConstants;
2932
import android.view.MotionEvent;
3033
import android.view.View;
@@ -244,15 +247,20 @@ public void onScreenChanged(Rect windowRect, int visibility) {
244247
final boolean isHideNavigationBar;
245248
if (visibility == FullscreenObserverView.NO_LAST_VISIBILITY) {
246249
// At the first it can not get the correct value, so do special processing
247-
mWindowManager.getDefaultDisplay().getMetrics(mDisplayMetrics);
248-
isHideNavigationBar = windowRect.width() - mDisplayMetrics.widthPixels > 0 || windowRect.height() - mDisplayMetrics.heightPixels > 0;
250+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
251+
mWindowManager.getDefaultDisplay().getRealMetrics(mDisplayMetrics);
252+
isHideNavigationBar = windowRect.width() - mDisplayMetrics.widthPixels == 0 && windowRect.bottom - mDisplayMetrics.heightPixels == 0;
253+
} else {
254+
mWindowManager.getDefaultDisplay().getMetrics(mDisplayMetrics);
255+
isHideNavigationBar = windowRect.width() - mDisplayMetrics.widthPixels > 0 || windowRect.height() - mDisplayMetrics.heightPixels > 0;
256+
}
249257
} else {
250258
isHideNavigationBar = (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
251259
}
252-
final boolean isPortrait = mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
253260

261+
final boolean isPortrait = mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
254262
// update FloatingView layout
255-
mTargetFloatingView.onUpdateSystemLayout(isHideStatusBar, isHideNavigationBar, isPortrait, windowRect.left);
263+
mTargetFloatingView.onUpdateSystemLayout(isHideStatusBar, isHideNavigationBar, isPortrait, windowRect);
256264

257265
// フルスクリーンでの非表示モードでない場合は何もしない
258266
if (mDisplayMode != DISPLAY_MODE_HIDE_FULLSCREEN) {
@@ -439,13 +447,13 @@ public void setDisplayMode(@DisplayMode int displayMode) {
439447
mDisplayMode = displayMode;
440448
// 常に表示/フルスクリーン時に非表示にするモードの場合
441449
if (mDisplayMode == DISPLAY_MODE_SHOW_ALWAYS || mDisplayMode == DISPLAY_MODE_HIDE_FULLSCREEN) {
442-
for (FloatingView floatingView: mFloatingViewList) {
450+
for (FloatingView floatingView : mFloatingViewList) {
443451
floatingView.setVisibility(View.VISIBLE);
444452
}
445453
}
446454
// 常に非表示にするモードの場合
447455
else if (mDisplayMode == DISPLAY_MODE_HIDE_ALWAYS) {
448-
for (FloatingView floatingView: mFloatingViewList) {
456+
for (FloatingView floatingView : mFloatingViewList) {
449457
floatingView.setVisibility(View.GONE);
450458
}
451459
mTrashView.dismiss();
@@ -579,6 +587,29 @@ public void removeAllViewToWindow() {
579587
mFloatingViewList.clear();
580588
}
581589

590+
/**
591+
* Find the safe area of DisplayCutout.
592+
*
593+
* @param activity {@link Activity} (Portrait and `windowLayoutInDisplayCutoutMode` != never)
594+
* @return Safe cutout insets.
595+
*/
596+
public static Rect findCutoutSafeArea(@NonNull Activity activity) {
597+
final Rect safeInsetRect = new Rect();
598+
// TODO:Rewrite with android-x
599+
// TODO:Consider alternatives
600+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
601+
return safeInsetRect;
602+
}
603+
604+
// set safeInsetRect
605+
final DisplayCutout displayCutout = activity.getWindow().getDecorView().getRootWindowInsets().getDisplayCutout();
606+
if (displayCutout != null) {
607+
safeInsetRect.set(displayCutout.getSafeInsetLeft(), displayCutout.getSafeInsetTop(), displayCutout.getSafeInsetRight(), displayCutout.getSafeInsetBottom());
608+
}
609+
610+
return safeInsetRect;
611+
}
612+
582613
/**
583614
* FloatingViewを貼り付ける際のオプションを表すクラスです。
584615
*/

sample/build.gradle

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ apply plugin: 'com.android.application'
22

33
android {
44
compileSdkVersion 28
5-
buildToolsVersion '28.0.2'
6-
75
defaultConfig {
86
applicationId "jp.co.recruit.floatingview"
97
minSdkVersion 14
@@ -21,6 +19,6 @@ android {
2119

2220
dependencies {
2321
implementation fileTree(dir: 'libs', include: ['*.jar'])
24-
implementation 'com.android.support:preference-v14:28.0.0-rc02'
22+
implementation 'com.android.support:preference-v14:28.0.0'
2523
implementation project(':library')
2624
}

sample/src/main/java/jp/co/recruit_lifestyle/sample/fragment/FloatingViewControlFragment.java

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import android.content.Context;
77
import android.content.Intent;
88
import android.content.res.Configuration;
9-
import android.graphics.Rect;
109
import android.net.Uri;
1110
import android.os.Build;
1211
import android.os.Bundle;
@@ -15,13 +14,13 @@
1514
import android.support.v4.app.Fragment;
1615
import android.support.v4.app.FragmentTransaction;
1716
import android.support.v4.content.ContextCompat;
18-
import android.view.DisplayCutout;
1917
import android.view.LayoutInflater;
2018
import android.view.View;
2119
import android.view.ViewGroup;
2220
import android.view.WindowManager;
2321

2422
import jp.co.recruit.floatingview.R;
23+
import jp.co.recruit_lifestyle.android.floatingview.FloatingViewManager;
2524
import jp.co.recruit_lifestyle.sample.service.ChatHeadService;
2625
import jp.co.recruit_lifestyle.sample.service.CustomFloatingViewService;
2726

@@ -143,35 +142,26 @@ private void showFloatingView(Context context, boolean isShowOverlayPermission,
143142
* @param isCustomFloatingView If true, it launches CustomFloatingViewService.
144143
*/
145144
private static void startFloatingViewService(Activity activity, boolean isCustomFloatingView) {
146-
// set safe inset area
147-
final Rect safeInsetRect = new Rect();
148-
// TODO:Rewrite with android-x
149-
// TODO:Consider alternatives
145+
// *** You must follow these rules when obtain the cutout(FloatingViewManager.findCutoutSafeArea) ***
150146
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
151-
final DisplayCutout displayCutout = activity.getWindow().getDecorView().getRootWindowInsets().getDisplayCutout();
152-
if (displayCutout != null) {
153-
safeInsetRect.set(displayCutout.getSafeInsetLeft(), displayCutout.getSafeInsetTop(), displayCutout.getSafeInsetRight(), displayCutout.getSafeInsetBottom());
154-
155-
// *** You must follow these rules when obtain the cutout ***
156-
// 1. 'windowLayoutInDisplayCutoutMode' do not be set to 'never'
157-
if (activity.getWindow().getAttributes().layoutInDisplayCutoutMode == WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) {
158-
throw new RuntimeException("'windowLayoutInDisplayCutoutMode' do not be set to 'never'");
159-
}
160-
// 2. Do not set Activity to landscape
161-
if(activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
162-
throw new RuntimeException("Do not set Activity to landscape");
163-
}
147+
// 1. 'windowLayoutInDisplayCutoutMode' do not be set to 'never'
148+
if (activity.getWindow().getAttributes().layoutInDisplayCutoutMode == WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) {
149+
throw new RuntimeException("'windowLayoutInDisplayCutoutMode' do not be set to 'never'");
150+
}
151+
// 2. Do not set Activity to landscape
152+
if (activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
153+
throw new RuntimeException("Do not set Activity to landscape");
164154
}
165155
}
166156

167157
// launch service
168158
if (isCustomFloatingView) {
169159
final Intent intent = new Intent(activity, CustomFloatingViewService.class);
170-
intent.putExtra(CustomFloatingViewService.EXTRA_CUTOUT_SAFE_AREA, safeInsetRect);
160+
intent.putExtra(CustomFloatingViewService.EXTRA_CUTOUT_SAFE_AREA, FloatingViewManager.findCutoutSafeArea(activity));
171161
ContextCompat.startForegroundService(activity, intent);
172162
} else {
173163
final Intent intent = new Intent(activity, ChatHeadService.class);
174-
intent.putExtra(ChatHeadService.EXTRA_CUTOUT_SAFE_AREA, safeInsetRect);
164+
intent.putExtra(ChatHeadService.EXTRA_CUTOUT_SAFE_AREA, FloatingViewManager.findCutoutSafeArea(activity));
175165
ContextCompat.startForegroundService(activity, intent);
176166
}
177167
}

0 commit comments

Comments
 (0)