I't trying to implement a local VpnService to have my app do some tasks, but I'm a little confused as to how to stop it one it started. The VpnService class and client activity are based on this repo:
https://github.com/hexene/LocalVPN
The caller activity is basically this:
public class MainActivity extends AppCompatActivity {
private static final int VPN_REQUEST_CODE = 0x0F;
private boolean waitingForVPNStart;
private BroadcastReceiver vpnStateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (LocalVPNService.BROADCAST_VPN_STATE.equals(intent.getAction()))
if (intent.getBooleanExtra("running", false))
waitingForVPNStart = false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button vpnButton = (Button)findViewById(R.id.vpn);
vpnButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startVPN();
}
});
final Button vpnStopButton = (Button)findViewById(R.id.stopVpnButton);
vpnStopButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
stopVPN();
}
});
waitingForVPNStart = false;
LocalBroadcastManager.getInstance(this).registerReceiver(vpnStateReceiver,
new IntentFilter(LocalVPNService.BROADCAST_VPN_STATE));
}
private void startVPN() {
Intent vpnIntent = VpnService.prepare(this);
if (vpnIntent != null)
startActivityForResult(vpnIntent, VPN_REQUEST_CODE); //Prepare to establish a VPN connection. This method returns null if the VPN application is already prepared or if the user has previously consented to the VPN application. Otherwise, it returns an Intent to a system activity.
else
onActivityResult(VPN_REQUEST_CODE, RESULT_OK, null);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == VPN_REQUEST_CODE && resultCode == RESULT_OK) {
waitingForVPNStart = true;
startService(new Intent(this, LocalVPNService.class));
enableButton(false);
}
}
What confuses me is: how would I call the service's onDestroy() method or something similar if I don't keep an instance if it in my main activity?
I looked at this answer and this and seen implementations of stopService, but I'm not sure how to handle the Intent, because it's not only used to call startService() but also involved in calling VpnService.prepare().
Edit: I tried
stopService(new Intent(this, LocalVPNService.class)); but it doesn't stop it. I tried stopService(vpnIntent); and IT WORKS, but makes my app crash/close.
In your LocalVPNService class create a new broadcast:
private BroadcastReceiver stopBr = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if ("stop_kill".equals(intent.getAction())) {
stopself();
}
}
};
and in the onCreate method add this:
LocalBroadcastManager lbm =
LocalBroadcastManager.getInstance(this);
lbm.registerReceiver(stopBr, new IntentFilter("stop_kill"));
in your activity:
Intent intent = new Intent("stop_kill");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
Related
I am trying to show a countdown in a textview from a loop inside an Intent Service. I am using the result receiver class for the communication between Intent Service and Activity. It works fine when I start the service for the first time. The textview shows the countdown for each time the loop runs in the service.
But when I close and launch the app again the textview doesn't show the countdown and only shows the hard coded text, while on the other hand the service stills runs in the background.
Here is my code snippet for the MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
public static final String RECEIVER_INTENT_EXTRA_NAME = "message_receiver_intent_extra";
private static final String TAG = "MainActivity";
private Intent intent;
MyIntentService myIntentService;
public TextView serviceCountdown;
private Button startButton, stopButton;
private Handler handler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportActionBar().hide();
myIntentService = new MyIntentService();
startButton = findViewById(R.id.startServiceButton);
stopButton = findViewById(R.id.stopServiceButton);
startButton.setOnClickListener(this);
stopButton.setOnClickListener(this);
handler = new Handler();
serviceCountdown = findViewById(R.id.serviceCountdown);
MessageReceiver messageReceiver = new MessageReceiver(handler);
// send intent service
intent = new Intent(this, MyIntentService.class);
intent.putExtra(RECEIVER_INTENT_EXTRA_NAME, messageReceiver);
}
#Override
public void onClick(View v) {
if (startButton.equals(v)) {
ContextCompat.startForegroundService(getApplicationContext(), intent);
}
if (stopButton.equals(v)){
stopService(intent);
}
}
public class MessageReceiver extends ResultReceiver {
public MessageReceiver(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
if (resultCode == 1 && resultData != null){
final String countdown = resultData.getString("countdown");
handler.post(new Runnable() {
#Override
public void run() {
serviceCountdown.setText(countdown);
}
});
}
}
}
}
And here is my code for the Intent Service Class
public class MyIntentService extends IntentService {
private static final String CHANNEL_ID = "my_channel_id";
private static final String TAG = "MyIntentService";
public MyIntentService() {
super("MyIntentService");
setIntentRedelivery(true);
}
#Override
public void onCreate() {
super.onCreate();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent,0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("My Service Title")
.setContentText("This is sample notification text")
.setSmallIcon(R.drawable.ic_battery)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
ResultReceiver resultReceiver = intent.getParcelableExtra(MainActivity.RECEIVER_INTENT_EXTRA_NAME);
Log.d(TAG, "onHandleIntent: called");
synchronized (this) {
for (int i = 10; i >= 0; i--) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "Service is looping : " + i);
Bundle bundle = new Bundle();
bundle.putString("countdown", String.valueOf(i));
resultReceiver.send(1, bundle);
}
}
}
#Override
public void onDestroy() {
Log.d(TAG, "onDestroy: called");
super.onDestroy();
}
}
In the real project, my intention is not to using the loop to show a countdown. It is just for testing and debugging purpose.
Use Local Broadcast Receiver from your service to activity. Now you getting the ResultReceiver from the MainActivity Intent. When the activity destroyed intents are also destroyed.
Use this Broadcast Receiver code in your service class.
LocalBroadcastManager localBroadcastManager =
LocalBroadcastManager.getInstance(this);
Intent sendIntent = new Intent(INTENT_ACTION_KEY);
sendIntent.putExtra(DATA_KEY, data);
localBroadcastManager.sendBroadcast(sendIntent);
Get this local broadcast receiver in your activity.
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent != null) {
//Perform your logic.
}
}
};
Make sure you register this broadcast when activity starts and unregister it when stop.
#Override
public void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver((broadcastReceiver),
new IntentFilter(INTENT_ACTION_KEY));
}
#Override
public void onStop() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
super.onStop();
}
I am having an activity and on Launching the activity through Intent from one of my service, onCreate(), onPause() and onResume() is called.
I am not very sure where i am doing wrong, i have removed all the code, just the basic function codes are available, and also i have removed all the codes which were opening that particular activity, still the behaviour is same.
I am including the simplest code i am using
ActivityClass.java
public class ActivityClass extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
System.out.println("TEST : onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lockscreen);
ButterKnife.bind(this);
}
#Override
protected void onPause() {
System.out.println("TEST : onPause");
super.onPause();
}
#Override
protected void onDestroy() {
System.out.println("TEST : onDestroy");
super.onDestroy();
}
}
Service class
public class OverlayService extends Service {
Context context;
public static final String TAG = OverlayService.class.getSimpleName();
public OverlayService(Context applicationContext) {
super();
context = applicationContext;
}
public OverlayService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "[onCreateService]");
super.onStartCommand(intent, flags, startId);
KeyguardManager myKM = (KeyguardManager) getApplicationContext().getSystemService(Context.KEYGUARD_SERVICE);
if(myKM.inKeyguardRestrictedInputMode()) {
//it is locked
showOverlayActivity();
} else {
//it is not locked
}
registerOverlayReceiver();
context = this;
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterOverlayReceiver();
Log.i("EXIT", "ondestroy!");
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
Intent broadcastIntent = new Intent("ac.in.ActivityRecognition.RestartSensor");
sendBroadcast(broadcastIntent);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void unregisterOverlayReceiver() {
if (overlayReceiver != null) {
unregisterReceiver(overlayReceiver);
}
}
private static final String ACTION_DEBUG = "kunal.lockoverlay.action.DEBUG";
private void registerOverlayReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(ACTION_DEBUG);
registerReceiver(overlayReceiver, filter);
}
private BroadcastReceiver overlayReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "[onReceive]" + action);
if (action.equals(Intent.ACTION_SCREEN_ON)) {
// ACTON_SCREEN_ON
showOverlayActivity();
} else if (action.equals(ACTION_DEBUG)) {
showOverlayActivity();
}
}
};
private void showOverlayActivity() {
Intent intent = new Intent();
intent.setClass(this, ActivityClass.class);
intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
Can someone point some of the possible reasons for this behaviour or could identify where i am doing wrong?
Well everything will be called regardless, you have defined it in your ClassName.java file.
You override the method and define it in your class to perform certain action/function when that is called.
For eg.,
You could use onResume to clear an arraylist and add updated elements to arraylist (OR) dismiss all notifications from your app on moving to that activity, say
#Override
public void onResume() {
super.onResume();
calendarList.clear();
//dismiss all notifications here
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager!=null)
notificationManager.cancelAll();
}
OR eg., onDestroy to destroy the bluetooth service,
#Override
public void onDestroy() {
super.onDestroy();
if (mBluetoothService != null) {
mBluetoothService.stop();
}
}
Hope that clarifies. Happy coding!
About service class, you should call registerReceiver() or initialize variables in onCreate instead of onStartCommand because onCreate is only called once at the first time of starting service and onStartCommand will be called every time you want to trigger an action to the service by calling startService(Intent).
In my application i want use EventBus for manage events.
In my app i open activity B on the Activity A ! and i want when activity B finished, call one event in Activity A.
I write below codes
Activity B :
private void finishWithAnimate() {
EventBus.getDefault().post(new EventShowDialog(ExtraConstants.NOTE_DIALOG.name()));
if (pageType.equals(Extras.DETAIL)) {
Intent intent = new Intent();
setResult(4757, intent);
finish();
} else {
finish();
RegisterInAuctionActivity.this.overridePendingTransition(0, android.R.anim.fade_out);
}
}
Activity A :
#Subscribe(threadMode = ThreadMode.MAIN)
public void getEventShowDialog(EventShowDialog eventShowDialog) {
Log.e("showDialogEvent", "Root");
if (eventShowDialog.getDialogName().equals(ExtraConstants.NOTE_DIALOG.name())) {
Log.e("showDialogEvent", "Call");
if (!prefsUtils.getFromSharedBool(PrefsKeys.NEW_NOTE_DIALOG.name())) {
closeDialog.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
noteDialog.dismiss();
prefsUtils.setToSharedBool(PrefsKeys.NEW_NOTE_DIALOG.name(), true);
}
});
noteDialog.show();
}
}
}
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
Update codes:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 4757) {
//Note dialog
noteDialog = new Dialog(this);
noteDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
noteDialog.setContentView(R.layout.important_note);
noteDialog.setCancelable(false);
closeDialog = noteDialog.findViewById(R.id.okPayFrag_submitOK);
closeDialog.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
noteDialog.dismiss();
prefsUtils.setToSharedBool(PrefsKeys.NEW_NOTE_DIALOG.name(), true);
}
});
if (!prefsUtils.getFromSharedBool(PrefsKeys.NEW_NOTE_DIALOG.name())) {
Toast.makeText(context, "Show", Toast.LENGTH_SHORT).show();
noteDialog.show();
closeDialog.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
noteDialog.dismiss();
prefsUtils.setToSharedBool(PrefsKeys.NEW_NOTE_DIALOG.name(), true);
}
});
}
finish();
overridePendingTransition(android.R.anim.fade_in, 0);
startActivity(getIntent());
overridePendingTransition(android.R.anim.fade_in, 0);
}
}
But not call any log in getEventShowDialog . Not show me any log in this method!
how can i fix it?
You are trying to perform some actions on getting a result from an activity.
So when your activity B finishes, do a setResult and catch the same on Activity A onActivityResult and perform your action(call the function you want).
You need to do a startActivityForResult while opening B from A though.
I am developing android app which work as battery indicator, open activity when battery low or full.
It works fine when i call finish() in Main activity from onCreate event after start of service.
but when i comment finish method it open activity multiple times, which i open from BroadCast receiver.
Here is MainActivity Code:
public class Main extends Activity {
private MyService service;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (service == null) {
Intent i = new Intent(this, MyService.class);
startService(i);
}
finish();
}}
Here is my service code:
I think i am doing something wrong when i start Activity.
At line
getApplication().startActivity(intent);
Complete Service Code is:
public class MyService extends Service{
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("MyService", "onStartCommand");
// do not receive all available system information (it is a filter!)
final IntentFilter battChangeFilter = new IntentFilter(
Intent.ACTION_BATTERY_CHANGED);
// register our receiver
this.registerReceiver(this.batteryChangeReceiver, battChangeFilter);
return super.onStartCommand(intent, flags, startId);
}
private final BroadcastReceiver batteryChangeReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
checkBatteryLevel(intent);
}
};
private void checkBatteryLevel(Intent batteryChangeIntent) {
// some calculations
final int currLevel = batteryChangeIntent.getIntExtra(
BatteryManager.EXTRA_LEVEL, -1);
final int maxLevel = batteryChangeIntent.getIntExtra(
BatteryManager.EXTRA_SCALE, -1);
final int percentage = (int) Math.round((currLevel * 100.0) / maxLevel);
if(percentage==100)
{
Intent intent = new Intent(getBaseContext(), Last.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplication().startActivity(intent);
}
if(percentage==15)
{
Intent intent = new Intent(getBaseContext(), Last.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplication().startActivity(intent);
}
}}
And this is my last "Last.cs" activity which is opening multiple time.
but it works fine when i call finish() into Main Activity.
public class Last extends Activity {
Button btnCancel;
Uri notification;
Ringtone r;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_last);
notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
r = RingtoneManager.getRingtone(getApplicationContext(), notification);
r.play();
btnCancel = (Button) findViewById(R.id.stopsound);
btnCancel.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
r.stop();
}
});
} }
Make your Last activity launchMode as singleTask in Manifest
<activity
android:name=".Last"
android:configChanges="orientation|screenSize"
android:launchMode="singleTask"
>
</activity>
My main activity launches a login activity. The ApiResponseHandler object calls activity.finish() if the user is successfully logged in. It seems as though everything is done correctly. I can't see any gaps in my passing the intent that might cause it to be null.
The point of error is noted by a comment below within MainActivity
public class MainActivity extends Activity {
static final int LOGIN_INTENT_ID = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//launch login activity
startActivityForResult(new Intent(this, LoginActivity.class), LOGIN_INTENT_ID);
}
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
//handle activity response
if (requestCode == LOGIN_INTENT_ID) {
if (resultCode == Activity.RESULT_OK) {
//intent is null, so .getSerializableExtra() fails
User user = (User) intent.getSerializableExtra("User");
Toast.makeText(getApplicationContext(), "Logged in as: " + user.getFirstName() + " " + user.getLastName(), Toast.LENGTH_SHORT).show();
}
}
}
}
My Login activity:
public class LoginActivity extends Activity {
public static final Logger logger = Logger.getLogger(LoginActivity.class.getName());
Button loginButton;
EditText loginField;
EditText passwordField;
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
loginButton = (Button)findViewById(R.id.loginFormLabelButton_Login);
loginField = (EditText)findViewById(R.id.loginFormLogin);
passwordField = (EditText)findViewById(R.id.loginFormPassword);
}
/**
* Login a user when the button is clicked
* #param v
*/
public void logUserIn(View v) {
loginButton.setText(R.string.loginFormLabelButton_Login_Working);
ApiRequest request = new ApiRequest();
request.setLogin(loginField.getText().toString());
request.setPassword(passwordField.getText().toString());
if (request.getLogin().length() == 0) {
showErrorDialog(getString(R.string.loginErrorDialog_LoginRequired));
return;
}
if (request.getPassword().length() == 0) {
showErrorDialog(getString(R.string.loginErrorDialog_PasswordRequired));
return;
}
//make request and handle results
ApiRequestHandler<User> apiHandler = new ApiRequestHandler<User>(User.class);
apiHandler.setUrl(getString(R.string.loginFormApiUrl));
apiHandler.setApiRequest(request);
apiHandler.setResponseHandler(new ApiResponseHandler(this, getIntent()));
apiHandler.execute();
loginButton.setText(R.string.loginFormLabelButton_Login);
}
....
}
The new ApiResponseHandler(this, getIntent()) looks like this...
public class ApiResponseHandler implements com.Bible_Bowl_Management.Api.ApiResponseHandler<User> {
private Activity activity;
private Intent intent;
public ApiResponseHandler(Activity activity, Intent intent) {
this.activity = activity;
this.intent = intent;
}
#Override
public void ResponseSuccessful(User user) {
intent.putExtra("User", user);
activity.setResult(Activity.RESULT_OK);
activity.finish();
}
#Override
public void ResponseNoContent() {
Toast.makeText(this.activity.getApplicationContext(), "No account found with these credentials", Toast.LENGTH_LONG).show();
}
}
You are passing getIntent() as Intent to return to the Activity
You should create a new Intent object for this.
For example:
Intent returnIntent = new Intent();
returnIntent.putExtra("SelectedBook",book);
setResult(RESULT_OK,returnIntent);