CallBaseActivity.java 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /*
  2. * Nextcloud Talk application
  3. *
  4. * @author Marcel Hibbe
  5. * Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. package com.nextcloud.talk.activities;
  21. import android.annotation.SuppressLint;
  22. import android.app.AppOpsManager;
  23. import android.app.KeyguardManager;
  24. import android.app.PictureInPictureParams;
  25. import android.content.Context;
  26. import android.content.pm.PackageManager;
  27. import android.os.Build;
  28. import android.os.Bundle;
  29. import android.util.Log;
  30. import android.util.Rational;
  31. import android.view.View;
  32. import android.view.Window;
  33. import android.view.WindowManager;
  34. import com.nextcloud.talk.BuildConfig;
  35. import androidx.activity.OnBackPressedCallback;
  36. public abstract class CallBaseActivity extends BaseActivity {
  37. public static final String TAG = "CallBaseActivity";
  38. public PictureInPictureParams.Builder mPictureInPictureParamsBuilder;
  39. public Boolean isInPipMode = Boolean.FALSE;
  40. long onCreateTime;
  41. private OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) {
  42. @Override
  43. public void handleOnBackPressed() {
  44. if (isPipModePossible()) {
  45. enterPipMode();
  46. }
  47. }
  48. };
  49. @SuppressLint("ClickableViewAccessibility")
  50. @Override
  51. public void onCreate(Bundle savedInstanceState) {
  52. super.onCreate(savedInstanceState);
  53. onCreateTime = System.currentTimeMillis();
  54. requestWindowFeature(Window.FEATURE_NO_TITLE);
  55. dismissKeyguard();
  56. getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
  57. getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  58. if (isGreaterEqualOreo() && isPipModePossible()) {
  59. mPictureInPictureParamsBuilder = new PictureInPictureParams.Builder();
  60. }
  61. getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
  62. }
  63. public void hideNavigationIfNoPipAvailable(){
  64. if (!isPipModePossible()) {
  65. getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
  66. View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
  67. View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
  68. View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
  69. suppressFitsSystemWindows();
  70. }
  71. }
  72. void dismissKeyguard() {
  73. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
  74. setShowWhenLocked(true);
  75. setTurnScreenOn(true);
  76. KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
  77. keyguardManager.requestDismissKeyguard(this, null);
  78. } else {
  79. getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
  80. getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
  81. getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
  82. }
  83. }
  84. void enableKeyguard() {
  85. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
  86. setShowWhenLocked(false);
  87. } else {
  88. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
  89. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
  90. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
  91. }
  92. }
  93. @Override
  94. public void onStop() {
  95. super.onStop();
  96. if (isInPipMode) {
  97. finish();
  98. }
  99. }
  100. @Override
  101. protected void onUserLeaveHint() {
  102. long onUserLeaveHintTime = System.currentTimeMillis();
  103. long diff = onUserLeaveHintTime - onCreateTime;
  104. Log.d(TAG, "onUserLeaveHintTime - onCreateTime: " + diff);
  105. if (diff < 3000) {
  106. Log.d(TAG, "enterPipMode skipped");
  107. } else {
  108. enterPipMode();
  109. }
  110. }
  111. void enterPipMode() {
  112. enableKeyguard();
  113. if (isGreaterEqualOreo() && isPipModePossible()) {
  114. Rational pipRatio = new Rational(300, 500);
  115. mPictureInPictureParamsBuilder.setAspectRatio(pipRatio);
  116. enterPictureInPictureMode(mPictureInPictureParamsBuilder.build());
  117. } else {
  118. // we don't support other solutions than PIP to have a call in the background.
  119. // If PIP is not available the call is ended when user presses the home button.
  120. Log.d(TAG, "Activity was finished because PIP is not available.");
  121. finish();
  122. }
  123. }
  124. boolean isPipModePossible() {
  125. if (isGreaterEqualOreo()) {
  126. boolean deviceHasPipFeature = getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE);
  127. AppOpsManager appOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
  128. boolean isPipFeatureGranted = appOpsManager.checkOpNoThrow(
  129. AppOpsManager.OPSTR_PICTURE_IN_PICTURE,
  130. android.os.Process.myUid(),
  131. BuildConfig.APPLICATION_ID) == AppOpsManager.MODE_ALLOWED;
  132. return deviceHasPipFeature && isPipFeatureGranted;
  133. }
  134. return false;
  135. }
  136. boolean isGreaterEqualOreo(){
  137. return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
  138. }
  139. public abstract void updateUiForPipMode();
  140. public abstract void updateUiForNormalMode();
  141. public abstract void suppressFitsSystemWindows();
  142. }