Creating a Overlay from a Service --to appear on top of other apps
FIRST --lets see what happens when we launch the app (zip of code)
1) on newer Android sdks the user must turn on permission to allow for overlays over other apps. So the program below launches this and it only needs to be done once --so you would really want to remove this from program but...it is here to illustrate. Unfortuantely, you can only go back to app by hitting the "back button" on the device.
After hitting back button we see the app with the Service launched that creates the "Overlay"
Now I have left the app and brought up another and we still see the overlay window
Lets hit the "J" image logo and see what happens it brings up the MainProcessingActivity AND it STOPS the service so the Overlay dissapears--
Lets entery in some text and hit Enter --this will cause 2 things to happen 1) the text is read and filled into a Toast message and 2) it restarts the service
What happens when we hit the "x" close - the service is turned off and overlay is removed
THE CODE EXPLAINED
AndroidManifest.xml file
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="computervision.grewe.serviceoverlaywithcallbacktoactivity">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.INTERNET" />
<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"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
<activity android:name=".MainProcessingActivity" > <category android:name="android.intent.category.DEFAULT" /> </activity>
<service android:name=".HUD" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </service> </application>
</manifest> |
<<declaration of permissions
<<Main activity
<<Journaling Activity interface
<<Service |
MainActvity.java
package computervision.grewe.serviceoverlaywithcallbacktoactivity;
import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.provider.Settings; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.webkit.WebView; import android.webkit.WebViewClient;
//Dummy activity that has a TextView and WebView as interface //important part is that this Android has a Service class called HUD that creates // an overlay window that appears on top of any actively running application window //not just this activity --it runs in the background. You can use it to //return to this app or do whatever public class MainActivity extends AppCompatActivity {
private int request_Code = 1;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
//YOU MAY want to get rid of these --it opens the apps settings so user can turn on permission for overlay //The only way the user can return to the app is to hit the "back" button on the device //Purpose = In newer version of Android any app that creates an Overlay requires the user // to specifically set this permission on the app's settings on the device. It is not enough // to request the permission in the AndroidManifest file
requestSystemAlertPermission(MainActivity.this,1); isSystemAlertPermissionGranted(MainActivity.this);
//start the service startService(new Intent(getApplicationContext(), HUD.class));
}
@Override protected void onStart() { super.onStart(); setupView();
}
/** * method to setup the View with content in Activity */ private void setupView(){
}
//brings up interface to settings for user to allow overlay public static void requestSystemAlertPermission(Activity context, int requestCode) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return; final String packageName = context == null ? context.getPackageName() : context.getPackageName(); final Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + packageName)); if (context != null) context.startActivityForResult(intent, requestCode); else context.startActivityForResult(intent, requestCode); } @TargetApi(23) public static boolean isSystemAlertPermissionGranted(Context context) { final boolean result = Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context); return result; }
//callback function to get results from ACTION_MANAGE_OVERLAY_PERMISSION "activity" when it is done public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == request_Code) { if (resultCode == RESULT_OK) { this.setupView(); } } } }
|
|
HUD.java - the service class
package computervision.grewe.serviceoverlaywithcallbacktoactivity;
import android.app.Service; import android.content.Intent; import android.os.IBinder;
import android.app.Service; import android.content.Intent; import android.graphics.PixelFormat; import android.net.Uri; import android.os.IBinder; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout;
/** * Class that is a background Service that creates an Overlay Window that can reside on top * of any running activity. * WHEN the interface is clicked on close it will launch the */ public class HUD extends Service {
private WindowManager windowManager; private ImageView close; private LinearLayout overlayLayout; private ImageView overlayButton;
/* @Override public IBinder onBind(Intent intent) { // Not used return null; } */ @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); }
@Override public void onCreate() { super.onCreate();
//using the WindowManager launch the Service with interface overlay_layout windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
//will put the "floating window = overlay" to top and left params.gravity = Gravity.TOP | Gravity.LEFT; params.x = 0; params.y = 100;
//Inflate the ouverlay_layout.xml file that contains and Image "J" logo for "journaling" // and an "x" image representing a close of a window overlayLayout = (LinearLayout) inflater.inflate(R.layout.overlay_layout, null); close=(ImageView)overlayLayout.findViewById(R.id.close); overlayButton=(ImageView) overlayLayout.findViewById(R.id.overlayButton);
//register listener for "J" logo ImageView widget that is part of this Service's "floating window //when clicked on it will lauch the Activity associated with this application as an Intent and then //stop THIS service from running overlayButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) {
//remove the view of the Overlay windowManager.removeView(overlayLayout);
//restart the Activity MainActivity associated with this application Intent intent_start_mainProcessing_activity = new Intent(getApplicationContext(), MainProcessingActivity.class); intent_start_mainProcessing_activity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent_start_mainProcessing_activity);
//stop this background service stopService(new Intent(getApplicationContext(), HUD.class)); } });
//when the x image = "close" it simply stops the service close.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { windowManager.removeView(overlayLayout);
//simply stopping Service stopService(new Intent(getApplicationContext(), HUD.class)); // startService(new Intent(getApplicationContext(), HUD.class)); //code to start service in case wanted to see it } });
windowManager.addView(overlayLayout, params); }
@Override public void onDestroy() { super.onDestroy(); /* if (overlayLayout != null) windowManager.removeView(overlayLayout);*/ } }
/*
@Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); } */
|
|
overlaylayout.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent">
<ImageView android:id="@+id/overlayButton" android:layout_width="54dp" android:layout_height="49dp" android:src="@drawable/overlaylogo" />
<ImageView android:id="@+id/close" android:layout_width="45dp" android:layout_height="45dp" android:layout_gravity="right|top" android:src="@drawable/remove" /> </LinearLayout>
|
|
MainProcessingActivity - the Journal Interface and processing it
package computervision.grewe.serviceoverlaywithcallbacktoactivity;
import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.webkit.WebView; import android.widget.Button; import android.widget.EditText; import android.widget.Toast;
public class MainProcessingActivity extends AppCompatActivity {
Button button_enterJournal;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main_processing); button_enterJournal = (Button) findViewById(R.id.button_SubmitJournal); button_enterJournal.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) {
//processing the journal entry String s = getResources().getString(R.string.saving_journal_entry) + ": " + ((EditText) findViewById(R.id.journal_EditText)).getText(); Toast.makeText(getApplicationContext(), s, Toast.LENGTH_LONG).show();
//call method to restart the background Overlay Service //start the service startService(new Intent(getApplicationContext(), HUD.class)); }
});
}
}
|
|
activity_main_processing.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="computervision.grewe.serviceoverlayexample.MainActivity">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Journal" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintVertical_bias="0.0" android:id="@+id/textView" android:layout_marginLeft="8dp" />
<Button android:id="@+id/button_SubmitJournal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/enter" android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="@+id/journal_EditText" android:layout_marginLeft="0dp" app:layout_constraintLeft_toRightOf="@+id/imageView_LoadedJournalImage" android:layout_marginRight="8dp" app:layout_constraintRight_toRightOf="parent" app:layout_constraintHorizontal_bias="0.81" />
<EditText android:id="@+id/journal_EditText" android:layout_width="fill_parent" android:layout_height="121dp" android:layout_marginLeft="0dp" android:layout_marginTop="12dp" android:ems="10" android:inputType="textMultiLine" android:minLines="6" android:singleLine="false" android:text="" android:lines="20" android:scrollHorizontally="false" android:scrollbars="vertical" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toBottomOf="@+id/textView" />
<ImageView android:id="@+id/imageView_LoadedJournalImage" android:layout_width="wrap_content" android:layout_height="wrap_content" app:srcCompat="@mipmap/ic_launcher" android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="@+id/journal_EditText" android:layout_marginLeft="8dp" app:layout_constraintLeft_toLeftOf="parent" />
</android.support.constraint.ConstraintLayout>
|
|
|