CS4521:   Mobile and Topics in Web Programming

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>

 

 

 

 

© Lynne Grewe