I built an app on Android studio using Java. I lock the app using a button and wake the screen up after 10 seconds. And also, I would like to restart the app using a foreground service when user closed the app after 1 minute. However, I could not manage where to run in my service. triggerRebirth() does work at first, however it does not work any onclick operation is done or when I use triggerRebirth() in a handler to run after 1 minute. Any help will be useful. Thanks!
First of all, I added permissions on AndroidManifest.xml
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme.FullScreen">
<service
android:name=".services.RestartAppService"
android:enabled="true"
android:exported="false"
android:stopWithTask="true" />
<service
android:name=".services.NotificationService"
android:enabled="true"
android:exported="false"
android:stopWithTask="true" />
<activity
android:name=".Demo"
android:theme="@style/AppTheme.FullScreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".Controller"
android:description="@string/app_description"
android:label="@string/app_name"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/device_admin" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
</application>
my MainActivity is like Demo.java
package com.example.demo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import android.app.AlertDialog;
import android.os.Handler;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.os.PowerManager;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.Toast;
import com.example.demo.services.NotificationService;
import com.example.demo.services.RestartAppService;
import com.jakewharton.processphoenix.ProcessPhoenix;
import java.util.Timer;
import java.util.TimerTask;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
public class Demo extends AppCompatActivity {
//Buttons for lock the screen and enable the app as device administrator
Button lockButton;
static final int RESULT_ENABLE = 1;
DevicePolicyManager devicePolicyManager;
ComponentName componentName;
Activity activity = this;
public static boolean isAppClosed;
Intent restartAppServiceIntent;
boolean isScreenLocked;
@RequiresApi(api = Build.VERSION_CODES.O_MR1)
@SuppressLint({"SetTextI18n", "InvalidWakeLockTag"})
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (ProcessPhoenix.isPhoenixProcess(this)) {
return;
}
isAppClosed = false;
//Locate button on xml
lockButton = findViewById(R.id.sleepButton);
//Create a policy manager and a system service for admin authorization
devicePolicyManager = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
componentName = new ComponentName(Demo.this, Controller.class);
isScreenLocked = false;
boolean active = devicePolicyManager.isAdminActive(componentName);
if (active) {
//App has admin authorization
lockButton.setVisibility(View.VISIBLE);
} else {
//App does not have admin authorization
lockButton.setVisibility(View.GONE);
enableAdminAuthorization();
}
restartAppServiceIntent = new Intent(Demo.this, RestartAppService.class);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
activity.startForegroundService(new Intent(Demo.this, NotificationService.class));
} else {
startService(new Intent(Demo.this, NotificationService.class));
}
//onClickListener for locking the screen and waking up the screen
lockButton.setOnClickListener(v -> {
// Lock the screen
devicePolicyManager.lockNow();
isScreenLocked = true;
// Unlock and turn on screen
turnScreenOnThroughKeyguard(Demo.this);
}
);
}
private void enableAdminAuthorization() {
boolean active1 = devicePolicyManager.isAdminActive(componentName);
if (active1) {
//App is not authorized and lock button is not visible, thus locking error without
// permission is prevented
devicePolicyManager.removeActiveAdmin(componentName);
lockButton.setVisibility(View.GONE);
} else {
//Create an intent to add app as device admin and start its activity
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, componentName);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "Please enable the app as device administrator");
startActivityForResult(intent, RESULT_ENABLE);
}
}
@SuppressLint("SetTextI18n")
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (requestCode == RESULT_ENABLE) {
if (resultCode == Activity.RESULT_OK) {
//Permission is granted and the app has admin authorization now.
//LockButton is now visible and the device can be locked and waken up
lockButton.setVisibility(View.VISIBLE);
} else {
//If a problem occurs send a toast with fail message
Toast.makeText(this, "Failed", Toast.LENGTH_SHORT).show();
}
}
super.onActivityResult(requestCode, resultCode, data);
}
public static void turnScreenOnThroughKeyguard(@NonNull Activity activity) {
// Create a handle and wait for 10 seconds to handle
final Handler handler = new Handler();
handler.postDelayed(() -> {
// Create powerManager and wakelock
userPowerManagerWakeup(activity);
// Create flags
useWindowFlags(activity);
// Turn the screen on
useActivityScreenMethods(activity);
}, 10000); // 10 seconds
}
private static void userPowerManagerWakeup(@NonNull Activity activity) {
PowerManager powerManager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
@SuppressLint("InvalidWakeLockTag") PowerManager.WakeLock wakelock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, " ");
wakelock.acquire(1);
}
@RequiresApi(api = Build.VERSION_CODES.ECLAIR)
private static void useWindowFlags(@NonNull Activity activity) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
}
private static void useActivityScreenMethods(@NonNull Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
try {
activity.setTurnScreenOn(true);
activity.setShowWhenLocked(true);
} catch (NoSuchMethodError e) {
Log.e("enable", "Enable setTurnScreenOn and setShowWhenLocked is not present on device!");
}
}
}
//TODO: When app is closed by clicking yes in alertDialog,
// App is deleted from recent apps and restarts with delay.
// However, it does not restart app if user removes app
// from recent app without using alert dialog.
// If user choose yes, kill the app. Otherwise, cancel the dialog and do nothing
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onBackPressed() {
AlertDialog.Builder builder = new AlertDialog.Builder(Demo.this);
builder.setTitle(R.string.app_name);
builder.setIcon(R.mipmap.ic_launcher);
builder.setMessage("Do you want to exit?")
.setCancelable(false)
.setPositiveButton("Yes", (dialog, id) -> finishAndRemoveTask())
.setNegativeButton("No", (dialog, id) -> dialog.cancel());
AlertDialog alert = builder.create();
alert.show();
}
@RequiresApi(api = Build.VERSION_CODES.O_MR1)
@Override
protected void onResume() {
isAppClosed = false;
super.onResume();
//Revert parameters false in order to turn screen on after each sleep button is clicked
//activity.setTurnScreenOn(false);
//activity.setShowWhenLocked(false);
//getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
@Override
protected void onStop() {
super.onStop();
}
@Override
protected void onDestroy() {
isAppClosed = true;
Log.d("RestartAppService", "onDestroy MainActivity");
// runRestartAppService();
triggerRebirth(this);
super.onDestroy();
}
public static void triggerRebirth(Context context) {
Intent intent = new Intent(context, Demo.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
if (context instanceof Activity) {
((Activity) context).finish();
}
Runtime.getRuntime().exit(0);
}
private void runRestartAppService() {
Log.d("RestartAppService", "openWakeUpService method runs on MainActivity");
startService(restartAppServiceIntent);
}
}
NotificationService.java
package com.example.demo.services;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import com.example.demo.Demo;
import com.example.demo.R;
public class NotificationService extends Service {
public void onCreate() {
super.onCreate();
setNotificationChannel();
}
private void setNotificationChannel() {
if (Build.VERSION.SDK_INT >= 26) {
// Create a notification channel for android 8+
String NOTIFICATION_CHANNEL_ID = "example.demo";
String channelName = "Demo";
// Initialize the channel
NotificationChannel notificationChannel = new NotificationChannel
(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
// Create a notification manager and add into notification channel
NotificationManager notificationManager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
assert notificationManager != null;
notificationManager.createNotificationChannel(notificationChannel);
// Create a pending intent for notification to start main activity
PendingIntent pendingIntent = PendingIntent.getActivity(NotificationService.this,
0,
new Intent(NotificationService.this, Demo.class),
PendingIntent.FLAG_UPDATE_CURRENT);
// Create the notification's properties
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder
(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("Demo App Notification") // Set its title
.setContentText("Application is running on background") // Set its context
.setPriority(NotificationManager.IMPORTANCE_HIGH) // Set its priority
.setCategory(Notification.CATEGORY_SERVICE) // Set its category
.setContentIntent(pendingIntent); // Set its intent when clicked
Notification notification = notificationBuilder.build(); // Build the notification
startForeground(1, notification); // Call foreground
} else {
stopSelf();
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onTaskRemoved(Intent rootIntent) {
Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
PendingIntent restartServicePendingIntent = PendingIntent.getService
(getApplicationContext(), 1, restartServiceIntent,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getApplicationContext()
.getSystemService(Context.ALARM_SERVICE);
alarmService.set
(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1,
restartServicePendingIntent);
super.onTaskRemoved(rootIntent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("RestartAppService", "onStart WakeUpService");
setNotificationChannel();
}
}
RestartAppService.java
package com.example.demo.services;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.Log;
import com.example.demo.Demo;
public class RestartAppService extends Service {
@Override
public void onCreate() {
Log.d("RestartAppService", "onCreate WakeUpService");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//Starts main activity after handler's delay
Log.d("RestartAppService", "onStart WakeUpService");
if(Demo.isAppClosed){
restartApp();
}
return super.onStartCommand(intent, flags, startId);
}
private void restartApp() {
final Handler handler = new Handler();
handler.postDelayed(() -> {
Intent intentMain = new Intent(this, Demo.class);
intentMain.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Demo.isAppClosed=false;
startActivity(intentMain);
}, 1 * 10000); // 5 minutes timer
}
@Override
public IBinder onBind(Intent intent) {
Log.d("RestartAppService", "onBind WakeUpService");
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onTaskRemoved(Intent rootIntent) {
/*
Log.d("onTaskRemoved", "onTaskRemoved called");
super.onTaskRemoved(rootIntent);
// Do
// Stops foregroundService
this.stopSelf();
*/
Log.d("RestartAppService", "onTaskRemoved WakeUpService");
Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
PendingIntent restartServicePendingIntent = PendingIntent.getService
(getApplicationContext(), 1, restartServiceIntent,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getApplicationContext()
.getSystemService(Context.ALARM_SERVICE);
alarmService.set
(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1,
restartServicePendingIntent);
super.onTaskRemoved(rootIntent);
}
@Override
public void onDestroy() {
Log.d("RestartAppService", "onStart WakeUpService");
if(Demo.isAppClosed){
restartApp();
}
super.onDestroy();
}
}
Android 10 (API level 29) and higher place restrictions on when apps can start activities when the app is running in the background.
Learn more here.