My application makes a call to the server which may take a few seconds. The user can minimize the App before receiving the response from the server.
So what I am trying to do is that if the App is minimized when it receives the response from the server, it will go to PIP mode.
When I try to enter Pip mode with the application minimized I get this error:
java.lang.IllegalStateException: Activity must be resumed to enter picture-in-picture
The error occurs when this line is executed:
enterPictureInPictureMode();
Thanks.
Need to check whether the special permission Picture-in-Picture is given before entering the mode.
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
if (manager != null) {
int modeAllowed = manager.unsafeCheckOpNoThrow(OPSTR_PICTURE_IN_PICTURE, Process.myUid(),
context.getPackageName());
if (modeAllowed == AppOpsManager.MODE_ALLOWED) {
// Enter picture-in-picture
}
}
If you are using rn-android-pip libary:
import android.app.AppOpsManager;
import android.content.Context;
import android.os.Process;
public void enterPictureInPictureMode() {
if (isPipSupported) {
AppOpsManager manager = (AppOpsManager) reactContext.getSystemService(Context.APP_OPS_SERVICE);
if (manager != null) {
int modeAllowed = manager.checkOpNoThrow(AppOpsManager.OPSTR_PICTURE_IN_PICTURE, Process.myUid(),
reactContext.getPackageName());
if (modeAllowed == AppOpsManager.MODE_ALLOWED) {
if (isCustomAspectRatioSupported) {
PictureInPictureParams params = new PictureInPictureParams.Builder()
.setAspectRatio(this.aspectRatio).build();
getCurrentActivity().enterPictureInPictureMode(params);
} else {
getCurrentActivity().enterPictureInPictureMode();
}
}
}
}
}
According to the doc:
Also, specify that your activity handles layout configuration changes so that your activity doesn't relaunch when layout changes occur during PiP mode transitions.
Switching to PiP mode triggers configuration changes, and this causes the Activity to be recreated, says going through stopped -> destroyed -> created -> ...
And we can find how Activity decides whether it can enter PiP mode or not:
https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/Activity.java
final void performStop(boolean preserveWindow, String reason) {
...
// Disallow entering picture-in-picture after the activity has been stopped
mCanEnterPictureInPicture = false;
...
}
public boolean enterPictureInPictureMode(#NonNull PictureInPictureParams params) {
...
if (!mCanEnterPictureInPicture) {
throw new IllegalStateException("Activity must be resumed to enter"
+ " picture-in-picture");
}
...
mIsInPictureInPictureMode = ActivityClient.getInstance().enterPictureInPictureMode(
mToken, params);
return mIsInPictureInPictureMode;
}
So you may need to check you AndroidManifest again to ensure you have already properly handled configuration changes to avoid Activity recreation:
<activity android:name="VideoActivity"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation"
...
Related
My app MainActivity have banner、video,but when using gotoSleep,after amonent,open the screen,they will stop play and scroll,how to make the app running when screen open after screen close
my code is :
public ScreenUtils(Context contx) {
this.mContext = contx;
this.pManager = (PowerManager)this.mContext.getSystemService("power");
this.wakeLock = this.pManager.newWakeLock(268435462, "TAG");
this.wakeLock.setReferenceCounted(false);
this.wakeLock.acquire();
}
public boolean lcdPowerOff() {
if (this.wakeLock.isHeld()) {
this.wakeLock.release();
}
this.pManager.goToSleep(SystemClock.uptimeMillis());
return true;
}
public boolean lcdPowerOn() {
this.wakeLock.acquire();
return true;
}
don't use wakeLock, Android's power control is very strict now
When you reopen the app, you can restart the player in the onResume lifecycle
I am receiving a push notification, On that, calling a foreground service.
From the service, I am calling one Activity.
Here, I have 2 functionality.
1. Sound alarm for emergency
2. Call using ACTION_CALL.
Both are working fine if device unlocked.
But if a device is locked with password or pattern it did not work when push receives.
Below code to unlock the device. this method is called from onStart.
private void unlockDevice() {
KeyguardManager loKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
Window loWindow = this.getWindow();
if (Common.isAboveAPI27()) {
setShowWhenLocked(true);
setTurnScreenOn(true);
} else if (Common.isAboveAPI26()) {
loWindow.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
loWindow.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
dismissKeyguard(loKeyguardManager);
} else {
if (loKeyguardManager != null) {
KeyguardManager.KeyguardLock loKeyguardLock = loKeyguardManager.newKeyguardLock("FullWakeUps");
loKeyguardLock.disableKeyguard();
}
loWindow.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); // Deprecated in 26
loWindow.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); // Deprecated in 27
loWindow.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); // Deprecated in 27
}
//Keep screen on
loWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
#RequiresApi(api = Build.VERSION_CODES.O)
private void dismissKeyguard(KeyguardManager loKeyguardManager) {
if (loKeyguardManager != null) {
loKeyguardManager.requestDismissKeyguard(SOSCallAndAlarmActivity.this, new KeyguardManager.KeyguardDismissCallback() {
#Override
public void onDismissError() {
super.onDismissError();
Log.i(TAG, Build.VERSION.SDK_INT + " : onDismissError");
}
#Override
public void onDismissSucceeded() {
super.onDismissSucceeded();
Log.i(TAG, Build.VERSION.SDK_INT + " : onDismissSucceeded");
}
#Override
public void onDismissCancelled() {
super.onDismissCancelled();
Log.i(TAG, Build.VERSION.SDK_INT + " : onDismissCancelled");
}
});
}
}
The below method is call in onDestroy to reenable the lock:
private void reEnabledKeyguard() {
KeyguardManager loKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (loKeyguardManager != null) {
KeyguardManager.KeyguardLock loKeyguardLock = loKeyguardManager.newKeyguardLock("FullWakeUps");
loKeyguardLock.reenableKeyguard();
}
Window loWindow = this.getWindow();
loWindow.addFlags(WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON);
}
Code to initiate the call.
public void callOnNumbers(String fsPhoneNumber) {
Intent loCallIntent = new Intent(Intent.ACTION_CALL);
loCallIntent.setData(Uri.parse("tel:" + fsPhoneNumber));
//callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // tried this but not helped.
if (ActivityCompat.checkSelfPermission(CallAndAlarmActivity.this,
android.Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "call phone permission not granted");
return;
}
startActivity(loCallIntent);
}
Strange is when this method opens the call screen blink and again displays the password screen on lock screen simply press back button I can see the call screen.
I need to know one more thing, even I set all app killing option and disabled battery optimization. the same code did not execute on push receive.
When device inactive half an hour and if push receives, the above code did not even turn on light. when I click on the lock/unlock button I can see my screens properly. even I press it after 30 seconds of push receives time.
The problem facing from android N.
Additional
When the ACTION_CALL intent calls it to execute my activities onPause and I did not add any code in onPause and I can see one error in logcate
2020-04-27 16:23:47.400 25826-25826/app.safety E/ActivityThread: Performing stop of activity that is already stopped: {app.safety/app.safety.CallAndAlarmActivity}
java.lang.RuntimeException: Performing stop of activity that is already stopped: {app.safety/app.safety.CallAndAlarmActivity}
at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:4089)
at android.app.ActivityThread.handleStopActivity(ActivityThread.java:4177)
at android.app.ActivityThread.-wrap24(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1648)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6687)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:810)
2020-04-27 16:23:47.400 25826-25826/app.safety E/ActivityThread: ActivityClientRecord{paused=true, stopped=true, hideForNow=false, startsNotResumed=false, isForward=false, pendingConfigChanges=0, onlyLocalRequest=false, preserveWindow=false, Activity{resumed=false, stopped=true, finished=false, destroyed=false, startedActivity=false, temporaryPause=false, changingConfigurations=false}}
Thanks
I wonder if it has something to do with the Background Execution Limit. in Android. I would recommend looking at this article.
https://medium.com/exploring-android/exploring-background-execution-limits-on-android-oreo-ab384762a66c
I hope this is of any help.
We have a launcher application that works fine on older versions of Android. We have a device that is running Android 5.1, and are running into issues.
When pressing the back button from within the application, we allow the user to go to the settings page. Pressing the home key re-launches the application. Pressing the back button on other devices also relaunches the application.
On the new device, pressing the back button allows us to navigate to the Android home page. It does not launch the application.
We are overriding the back button like so:
#override
public void onBackPressed() {
// Display the password prompt if required
if (PreferencesManager.isPasswordPresent()) {
LeaveApplicationPasswordDialogFragment dialog = LeaveApplicationPasswordDialogFragment.getInstance();
dialog.show(getSupportFragmentManager(), "password");
}
else {
// Prompt whether we are about to leave the app
LeaveApplicationDialogFragment dialog = null;
MyApplication application = (MyApplication )
getApplication();
if (application.isDefaultLauncher()) {
dialog = LeaveApplicationDialogFragment.getInstance("Are you sure you want to leave ** to access the device's settings?");
}
else {
dialog = LeaveApplicationDialogFragment.getInstance("Are you sure you want to leave ***");
}
dialog.show(getFragmentManager(), "leaving");
}
}
In the dialog fragment, we accept the confirmation and process it like so:
public void exitToSettings() {
GUIAndroidTouchBaseActivity.this.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS));
shutdownOperations();
finish();
}
Per some research and other threads, I worked with our exit method like so:
public void exitToSettings() {
Intent intent = new Intent(android.provider.Settings.ACTION_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP );
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
GUIAndroidTouchBaseActivity.this.startActivity(intent);
shutdownOperations();
finish();
}
No dice. Same behavior.
What am I missing? Is there something in OS 5.1 that's overriding our launcher? Again, pressing the home button launches the app as expected. Navigating to the home page from the settings page by pressing the back button does not.
What we have works on other devices and OSs. We've had no issue with 4.1 and 6.1.
We are also overriding the back button like so:
#Override
public boolean onKeyDown(int keyCode, KeyEvent KEvent) {
int deviceid = KEvent.getDeviceId();
//Making sure not processing same key again
if (KEvent.getRepeatCount() != 0) {
return true;
}
if (!SettingsOpened) {
int keyaction = KEvent.getAction();
// "Esc" key can not be stooped id diveceid is non zero because it can be back key of android
if (KEvent.getKeyCode() == KeyEvent.KEYCODE_BACK && deviceid != 0) {
return super.onKeyDown(keyCode, KEvent);
}
if (keyaction == KeyEvent.ACTION_DOWN) {
String key = KeyEvent.keyCodeToString(keyCode); //wont work in version 11 or less
if (keyCode != KeyEvent.KEYCODE_ENVELOPE) {
Matcher matcher = KEYCODE_PATTERN.matcher(key);
if (matcher.matches() || ExternalKeyboard.keyMatches(KEvent)) {
int keyunicode = KEvent.getUnicodeChar(KEvent.getMetaState());
char character = (char) keyunicode;
//toast.makeText(this, "onKeyDown" + _lastChar + repeatcount, toast.LENGTH_SHORT).show();
_lastChar = character;
_actionDown = true;
ExternalKeyboard.KeyboardAddChar(character);
}
}
}
return true;
}
else {
return super.onKeyDown(keyCode, KEvent);
}
}
Thanks!
Adding
android:stateNotNeeded="true"
android:clearTaskOnLaunch="false"
to my manifest took care of it.
There seems to be a bug of some sort with the play store installer, whenever I try to resume my app from the home screen icon rather than the app screen it will launch again on top of my already running app. This is true for the other way around
I've tried this code
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER)
&& intentAction != null
&& intentAction.equals(Intent.ACTION_MAIN)) {
finish();
}
}
But all it does it crash the app if it tries launching in the fashion I stated.
My manifest is also set to
android:launchMode="singleTask"
I found that if I take exactly the same APK and install it using "adb install", my app works correctly and as I expect.
However, if I (or my users) download the apk and install it from the Downloads, I find the behaviour described above, namely a new instance of my Activity being created on the stack when the user navigates to home and then back to the app via the launcher. This can be verified with "adb shell dumpsys activity "
I fixed the issue by using the following at my Launch Activity
ublic class StartupActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (needStartApp()) {
Intent i = new Intent(StartupActivity.this, GameActivity.class);
startActivity(i);
}
finish();
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
// this prevents StartupActivity recreation on Configuration changes
// (device orientation changes or hardware keyboard open/close).
// just do nothing on these changes:
super.onConfigurationChanged(null);
}
private boolean needStartApp() {
final ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
final List<RunningTaskInfo> tasksInfo = am.getRunningTasks(1024);
if (!tasksInfo.isEmpty()) {
final String ourAppPackageName = getPackageName();
RunningTaskInfo taskInfo;
final int size = tasksInfo.size();
for (int i = 0; i < size; i++) {
taskInfo = tasksInfo.get(i);
if (ourAppPackageName.equals(taskInfo.baseActivity.getPackageName())) {
// continue application start only if there is the only Activity in the task
// (BTW in this case this is the StartupActivity)
return taskInfo.numActivities == 1;
}
}
}
return true;
}
}
I am wondering when exactly the NFC Service is started and stopped.
The source code for android 4.0.3 seems to state that the polling is dependent on a single constant (located in NfcService.java)
/** minimum screen state that enables NFC polling (discovery) */
static final int POLLING_MODE = SCREEN_STATE_ON_UNLOCKED;
I would interpret this as "the screen light is on, therefore the nfc service is active".
BUT when the screen is locked, a NFC Tag wont be recognized, altough the screen is lit.
So I am curious: Is the NFC Service already deactivated when the lock screen appears, or is it still running but not processing the Tags?
Actually, I do not think that NFC Service is deactivated. When the screen has lower value then SCREEN_STATE_ON_UNLOCKED a device stops to ask NFC tags around. You can see this from this code:
// configure NFC-C polling
if (mScreenState >= POLLING_MODE) {
if (force || !mNfcPollingEnabled) {
Log.d(TAG, "NFC-C ON");
mNfcPollingEnabled = true;
mDeviceHost.enableDiscovery();
}
} else {
if (force || mNfcPollingEnabled) {
Log.d(TAG, "NFC-C OFF");
mNfcPollingEnabled = false;
mDeviceHost.disableDiscovery();
}
}
But NFC-EE routing is enabled util screen state is higher then SCREEN_STATE_ON_LOCKED:
// configure NFC-EE routing
if (mScreenState >= SCREEN_STATE_ON_LOCKED &&
mEeRoutingState == ROUTE_ON_WHEN_SCREEN_ON) {
if (force || !mNfceeRouteEnabled) {
Log.d(TAG, "NFC-EE ON");
mNfceeRouteEnabled = true;
mDeviceHost.doSelectSecureElement();
}
} else {
if (force || mNfceeRouteEnabled) {
Log.d(TAG, "NFC-EE OFF");
mNfceeRouteEnabled = false;
mDeviceHost.doDeselectSecureElement();
}
}
The service itself is started and stopped in other parts of this class.
See related http://forum.xda-developers.com/showthread.php?t=1712024&page=14