/* Swisscom Safe Connect Copyright (C) 2014 Swisscom This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.swisscom.safeconnect.activity; import android.app.Dialog; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.LabeledIntent; import android.net.Uri; import android.net.VpnService; import android.os.Bundle; import android.os.IBinder; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentTransaction; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.support.v7.app.AlertDialog; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.Toast; import com.swisscom.safeconnect.BuildConfig; import com.swisscom.safeconnect.R; import com.swisscom.safeconnect.backend.BackendConnector; import com.swisscom.safeconnect.backend.SubscriptionsManager; import com.swisscom.safeconnect.billing.PurchaseManager; import com.swisscom.safeconnect.fragment.InfoFragment; import com.swisscom.safeconnect.fragment.PipeDialogFragment; import com.swisscom.safeconnect.fragment.StatisticsFragment; import com.swisscom.safeconnect.fragment.VpnInfoDlgFragment; import com.swisscom.safeconnect.fragment.WaitingIndicatorFragment; import com.swisscom.safeconnect.fragment.WhatsnewDialogFragment; import com.swisscom.safeconnect.loader.ConnectionsListLoader; import com.swisscom.safeconnect.model.PlumberAuthResponse; import com.swisscom.safeconnect.model.PlumberLastConnectionLogResponseList; import com.swisscom.safeconnect.model.Subscription; import com.swisscom.safeconnect.sharing.FacebookSharing; import com.swisscom.safeconnect.sharing.Sharing; import com.swisscom.safeconnect.sharing.SharingAdapter; import com.swisscom.safeconnect.sharing.TwitterSharing; import com.swisscom.safeconnect.utils.Config; import com.swisscom.safeconnect.utils.Logger; import com.swisscom.safeconnect.utils.VpnConfigurator; import com.swisscom.safeconnect.view.ConnectionStatusView; import com.swisscom.safeconnect.view.SubscriptionStatusView; import com.swisscom.safeconnect.view.WorldMapView; import org.strongswan.android.data.LogContentProvider; import org.strongswan.android.data.VpnProfile; import org.strongswan.android.logic.CharonVpnService; import org.strongswan.android.logic.VpnStateService; import org.strongswan.android.ui.LogActivity; import java.io.File; import java.util.ArrayList; import java.util.List; public class DashboardActivity extends PipeActivity implements VpnStateService.VpnStateListener, LoaderManager.LoaderCallbacks<PlumberLastConnectionLogResponseList> { public static final String KEY_OPEN_SUBSCRIPTIONS = "DashboardActivity.openSubscriptions"; public static final String KEY_CONN_STATUS = "DashboardActivity.KEY_CONN_STATUS"; private static final int PREPARE_VPN_SERVICE = 0; private static final String DIALOG_TAG = "Dialog"; private static final int LOADER_ID = "DashboardActivity".hashCode(); private static final long SUBCR_FETCH_REPEAT_INTERVAL = 2000; private VpnProfile mProfile; private VpnStateService mService; private VpnStateService.State mVpnState = VpnStateService.State.DISABLED; private BackendConnector backendConnector = new BackendConnector(this); private ConnectionStatusView viewConnStatus; private SubscriptionStatusView viewSubscrStatus; private WorldMapView viewMap; private Button btnRescaleMap; private WaitingIndicatorFragment waitingFragment = new WaitingIndicatorFragment(); private Subscription[] subscriptions; private SubscriptionsManager subscrManager; private boolean mOpenSubscriptions; private PauseHandler handler = new PauseHandler(); public interface VpnServiceIntentProvider { public Intent getIntent(); } private VpnServiceIntentProvider vpnServiceIntentProvider; private Runnable subscriptionsFetchRunnable = new Runnable() { @Override public void run() { subscrManager.init(new PurchaseManager.Callback<Subscription[]>() { @Override public void onRequestCompleted(Subscription[] result) { subscriptions = result; getOwnSubscription(); } @Override public void onRequestFailed(int code) { // failed to get the subscriptions, disconnect VPN and try again if (mVpnState == VpnStateService.State.CONNECTED) { disconnectVpn(); } else { showNoConnection(); } } }); } }; private final ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { mService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = ((VpnStateService.LocalBinder)service).getService(); mService.registerListener(DashboardActivity.this); syncVpnState(); } }; @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); subscrManager.onSaveInstanceState(outState); outState.putBoolean(KEY_OPEN_SUBSCRIPTIONS, mOpenSubscriptions); outState.putSerializable(KEY_CONN_STATUS, viewConnStatus.getConnectionState()); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.dashboard); vpnServiceIntentProvider = new VpnServiceIntentProvider() { @Override public Intent getIntent() { Intent intent = new Intent(DashboardActivity.this, CharonVpnService.class); intent.putExtra(Config.VPN_PROFILE_BUNDLE_KEY, mProfile); return intent; } }; btnRescaleMap = (Button) findViewById(R.id.btn_rescale_map); btnRescaleMap.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { btnRescaleMap.setVisibility(View.GONE); viewMap.setAllowRecalcBounds(); } }); viewSubscrStatus = (SubscriptionStatusView) findViewById(R.id.view_subscr_status); viewSubscrStatus.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mOpenSubscriptions) return; // if subscription expired, make sure that the VPN is disconnected before // opening the subscriptions activity Subscription cur = viewSubscrStatus.getCurrentSubscription(); if (cur != null && cur.getValidTill()*1000 <= System.currentTimeMillis() && mVpnState != VpnStateService.State.DISABLED) { mOpenSubscriptions = true; disconnectVpn(); } else { openSubscriptionsActivity(); } } }); viewConnStatus = (ConnectionStatusView) findViewById(R.id.view_conn_status); viewConnStatus.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { switch (viewConnStatus.getConnectionState()) { case CONNECTED: disconnectVpn(); break; case DISCONNECTED: case UNAVAILABLE: connectVpn(); break; default: break; } } }); viewMap = (WorldMapView) findViewById(R.id.view_world_map); viewMap.setOnUserMapInteractionListener(new View.OnClickListener() { @Override public void onClick(View v) { btnRescaleMap.setVisibility(View.VISIBLE); } }); if (!Config.getInstance().getAuthToken().isEmpty()) { bindVpnService(); } if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction(). replace(R.id.frame_sidebar, new InfoFragment()).commit(); // in case DashboardActivity gets killed in the meantime, the intent of a // newly created DashboardActivity will still contain the old value in its // intent. onSaveInstanceState works though. mOpenSubscriptions = getIntent() != null && (getIntent() .getBooleanExtra(KEY_OPEN_SUBSCRIPTIONS, false) || getIntent().getAction() == Intent.ACTION_VIEW && getIntent().getDataString() != null && getIntent().getDataString().contains("subscribe")); } else { mOpenSubscriptions = savedInstanceState.getBoolean(KEY_OPEN_SUBSCRIPTIONS, false); viewConnStatus.setConnectionState((ConnectionStatusView.VpnState) savedInstanceState.get(KEY_CONN_STATUS)); } subscrManager = new SubscriptionsManager(this, savedInstanceState); handler.postRunnable(subscriptionsFetchRunnable); // show what's new if needed if (Config.getInstance().isAppGotUpdated()) { Config.getInstance().clearAppUpdatedFlag(); WhatsnewDialogFragment dlg = new WhatsnewDialogFragment(); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); Fragment prev = getSupportFragmentManager().findFragmentByTag("whatsnew"); if (prev != null) { ft.remove(prev); } ft.addToBackStack(null); dlg.show(ft, "whatsnew"); } } private void openSubscriptionsActivity() { if (subscriptions == null) return; Intent subscriptionIntent = new Intent(this, SubscriptionActivity.class); subscriptionIntent.putExtra(SubscriptionsManager.KEY_SUBSCRIPTIONS, subscriptions); subscriptionIntent.putExtra(SubscriptionsManager.KEY_CURRENT_SUBSCR, viewSubscrStatus.getCurrentSubscription()); startActivity(subscriptionIntent); } @Override protected void onPause() { super.onPause(); handler.pause(); } @Override protected void onResume() { super.onResume(); handler.resume(); getOwnSubscription(); syncVpnState(); } private void bindVpnService() { Context context = getApplicationContext(); context.bindService(new Intent(context, VpnStateService.class), mServiceConnection, BIND_AUTO_CREATE); } @Override public void onStart() { super.onStart(); if (mService != null) { mService.registerListener(this); syncVpnState(); } } @Override public void onStop() { super.onStop(); if (mService != null) { mService.unregisterListener(this); } } @Override public void onDestroy() { super.onDestroy(); if (mService != null) { getApplicationContext().unbindService(mServiceConnection); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); if (BuildConfig.TESTING) { toolbar.getMenu().findItem(R.id.menu_show_log).setVisible(false); toolbar.getMenu().findItem(R.id.menu_change_num).setVisible(false); } else if (!BuildConfig.DEBUG) { toolbar.getMenu().findItem(R.id.menu_show_log).setVisible(false); toolbar.getMenu().findItem(R.id.menu_change_num).setVisible(false); toolbar.getMenu().findItem(R.id.menu_send_log).setVisible(false); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_show_log: Intent logIntent = new Intent(this, LogActivity.class); startActivity(logIntent); return true; case R.id.menu_send_log: File log = Logger.saveLogs(this); File charonlog = new File(getFilesDir(), CharonVpnService.LOG_FILE); Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE); intent.putExtra(Intent.EXTRA_EMAIL, new String[] {"manuel.cianci1@swisscom.com"}); intent.putExtra(Intent.EXTRA_SUBJECT, "PipeOfTrust LogFiles"); intent.setType("text/plain"); ArrayList<Uri> files = new ArrayList<Uri>(); if (charonlog.exists() && charonlog.length() > 0) { files.add(LogContentProvider.createContentUri()); } if (log.exists() && log.length() > 0) { files.add(Uri.fromFile(log)); } if (files.size() == 0) { Toast.makeText(this, "Log is empty!", Toast.LENGTH_SHORT).show(); return true; } intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, files); startActivity(Intent.createChooser(intent, getString(R.string.send_log))); return true; case R.id.menu_change_num: if (mService.getState() == VpnStateService.State.CONNECTED) { mService.disconnect(); } intent = new Intent(this, RegistrationActivity.class); intent.putExtra(RegistrationActivity.FORCE_ACTIVITY, true); startActivity(intent); finish(); return true; case R.id.menu_help: startActivity(new Intent(this, FaqActivity.class)); return true; case R.id.menu_settings: Intent settingsIntent = new Intent(this, SettingsActivity.class); startActivity(settingsIntent); return true; case R.id.menu_share: share(); return true; default: return super.onOptionsItemSelected(item); } } @Override public void stateChanged() { syncVpnState(); } public void startVpnTunnel(VpnProfile profile) { prepareVpnService(profile); } /** * Prepare the VpnService. If this succeeds the current VPN profile is * started. * @param profile bundle containing the information about the profile to be started */ protected void prepareVpnService(VpnProfile profile) { Intent intent; try { intent = VpnService.prepare(this); } catch (IllegalStateException ex) { /* this happens if the always-on VPN feature (Android 4.2+) is activated */ VpnNotSupportedError.showWithMessage(this, R.string.vpn_not_supported_during_lockdown); return; } /* store profile info until the user grants us permission */ mProfile = profile; if (intent != null) { try { startActivityForResult(intent, PREPARE_VPN_SERVICE); } catch (ActivityNotFoundException ex) { /* it seems some devices, even though they come with Android 4, * don't have the VPN components built into the system image. * com.android.vpndialogs/com.android.vpndialogs.ConfirmDialog * will not be found then */ VpnNotSupportedError.showWithMessage(this, R.string.vpn_not_supported); } } else { /* user already granted permission to use VpnService */ onActivityResult(PREPARE_VPN_SERVICE, RESULT_OK, null); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case PREPARE_VPN_SERVICE: if (resultCode == RESULT_OK && mProfile != null) { startService(vpnServiceIntentProvider.getIntent()); } break; default: super.onActivityResult(requestCode, resultCode, data); } } @Override public Loader<PlumberLastConnectionLogResponseList> onCreateLoader(int id, Bundle args) { return new ConnectionsListLoader(this); } @Override public void onLoadFinished(Loader<PlumberLastConnectionLogResponseList> loader, PlumberLastConnectionLogResponseList data) { if (data != null && data.getConnectionLogs() != null) { viewMap.setConnPoints(data); } } @Override public void onLoaderReset(Loader<PlumberLastConnectionLogResponseList> loader) { } /** * Class representing an error message which is displayed if VpnService is * not supported on the current device. */ public static class VpnNotSupportedError extends DialogFragment { static final String ERROR_MESSAGE_ID = "org.strongswan.android.VpnNotSupportedError.MessageId"; public static void showWithMessage(FragmentActivity activity, int messageId) { Bundle bundle = new Bundle(); bundle.putInt(ERROR_MESSAGE_ID, messageId); VpnNotSupportedError dialog = new VpnNotSupportedError(); dialog.setArguments(bundle); dialog.show(activity.getSupportFragmentManager(), DIALOG_TAG); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Bundle arguments = getArguments(); final int messageId = arguments.getInt(ERROR_MESSAGE_ID); return new AlertDialog.Builder(getActivity(), R.style.AppCompat_Pipe_Dialog_Alert) .setTitle(R.string.vpn_not_supported_title) .setMessage(messageId) .setCancelable(false) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); } }).create(); } } public VpnStateService getMService() { return mService; } public void runBackendConnection(final String token) { viewConnStatus.setConnectionState(ConnectionStatusView.VpnState.CONNECTED); showWaiting(); backendConnector.authenticateUser(Config.getInstance().getPhoneNumber(), Config.getInstance().getDeviceId(), token, new BackendConnector.ResponseCallback<PlumberAuthResponse>() { @Override public void onRequestComplete(int statusCode, final PlumberAuthResponse response) { if (isFinishing()) return; switch (statusCode) { case 0: // connections error, most likely no network showNoConnection(); break; case 404: showAuthError(); break; case 200: final Runnable vpnConnectRunnable = new Runnable() { @Override public void run() { showVpnConnected(); viewSubscrStatus.setVisibility(View.VISIBLE); //save new auth token for next use! Config.getInstance().saveAuthToken(response.getUserToken()); VpnProfile mProfile = VpnConfigurator.getVpnProfile(response.getUsername(), response.getPassword(), response.getPsk()); startVpnTunnel(mProfile); } }; if (Config.getInstance().isFirstVpnLaunch()) { handler.postRunnable(new Runnable() { @Override public void run() { VpnInfoDlgFragment vpnDlg = new VpnInfoDlgFragment(); vpnDlg.setClickRunnable(vpnConnectRunnable); vpnDlg.show(getSupportFragmentManager(), "vpn_dlg"); Config.getInstance().setFirstVpnLaunch(false); } }); } else { vpnConnectRunnable.run(); } break; default: // this should never be reached showNoConnection(); break; } } } ); } private void showAuthError() { handler.postRunnable(new Runnable() { @Override public void run() { PipeDialogFragment dlg = new PipeDialogFragment(); dlg.setTitle(getString(R.string.lab_dlg_rereg_title)); dlg.setMessage(getString(R.string.lab_dlg_rereg_msg)); dlg.setOnPositiveClickListener(new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(getApplicationContext(), RegistrationActivity.class); intent.putExtra(RegistrationActivity.FORCE_ACTIVITY, true); startActivity(intent); finish(); } }); dlg.show(getSupportFragmentManager(), "login_error"); } }); } private void getOwnSubscription() { subscrManager.getOwnSubscription(new PurchaseManager.Callback<Subscription>() { private void changeState() { if (isFinishing()) return; boolean subscrValid = viewSubscrStatus.isSubscriptionValid(); viewConnStatus.setActionVisible(subscrValid); if (!subscrValid) { setInfo(InfoFragment.State.NO_SUBSCIPTION, false); } else { setInfo(InfoFragment.State.DISCONNECTED, false); } } @Override public void onRequestCompleted(Subscription result) { if (isFinishing()) return; if (viewConnStatus.getConnectionState() == ConnectionStatusView.VpnState.UNAVAILABLE) { viewConnStatus.setConnectionState(ConnectionStatusView.VpnState.DISCONNECTED); viewMap.showDisconnected(); } viewSubscrStatus.setVisibility(View.VISIBLE); viewSubscrStatus.setSubscription(result); changeState(); if (mOpenSubscriptions) { // disconnect vpn if no subscription. Subscriptions activity will be // opened once it's disconnected if (result.getValidTill()*1000 <= System.currentTimeMillis() && mVpnState != VpnStateService.State.DISABLED) { disconnectVpn(); } else { mOpenSubscriptions = false; openSubscriptionsActivity(); } } } @Override public void onRequestFailed(int code) { // failed to get the subscriptions, disconnect VPN and try again if (mVpnState == VpnStateService.State.CONNECTED && subscrManager.isFailed()) { disconnectVpn(); } if (code == 404) { showAuthError(); } else { showNoConnection(); } } }); } private void share() { final String appUrl = Config.GPLAY_URL; final String content = getString(R.string.share_content); final String subject = getString(R.string.share_subject); List<Intent> shareIntents = new ArrayList<Intent>(); //fb & twitter shareIntents.add(new FacebookSharing(this).getIntent(content, appUrl)); shareIntents.add(new TwitterSharing(this).getIntent(content, appUrl)); //favorite sharing apps for (String packageName : Sharing.favoriteApps){ Intent i = Sharing.getSharingApp(packageName, subject, content + ' ' + appUrl, this); if (i != null) { shareIntents.add(i); } } //trigger to load all sharing apps LabeledIntent share = new LabeledIntent(new Intent(), "", getString(R.string.share_more_apps), 0); share.putExtra("more", true); shareIntents.add(share); AlertDialog.Builder builder = new AlertDialog.Builder(DashboardActivity.this, R.style.AppCompat_Pipe_Dialog_Alert); builder.setTitle(R.string.share_with); final SharingAdapter adapter = new SharingAdapter(this, R.layout.item_sharing, shareIntents); builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent i = adapter.getItem(which); //Trigger to load the standard android sharing dialog if (i.getBooleanExtra("more", false)) { Intent intent = Intent.createChooser(Sharing.getSharingIntent(subject, content + ' ' + appUrl), getString(R.string.share_with)); startActivity(intent); return; } //fix exception when launching labeled-intent if (i instanceof LabeledIntent) { i = new Intent(i); } startActivity(i); } }); builder.show(); } private void setInfo(final InfoFragment.State state, final boolean forceFragment) { handler.postRunnable(new Runnable() { @Override public void run() { if (isFinishing()) return; Fragment curFragment = getSupportFragmentManager().findFragmentById(R.id.frame_sidebar); if (curFragment instanceof InfoFragment) { ((InfoFragment) curFragment).setState(state); } else if (forceFragment) { InfoFragment frag = new InfoFragment(); getSupportFragmentManager().beginTransaction(). replace(R.id.frame_sidebar, frag).commit(); frag.setState(state); } } }); } public void connectVpn() { if (mService != null) { runBackendConnection(Config.getInstance().getAuthToken()); } } public void disconnectVpn() { showVpnDisconnected(); if (mService != null) { mService.disconnect(); } } private void showVpnConnected() { handler.postRunnable(new Runnable() { @Override public void run() { if (getSupportFragmentManager().findFragmentById(R.id.frame_sidebar) instanceof StatisticsFragment) return; getSupportFragmentManager().beginTransaction(). replace(R.id.frame_sidebar, new StatisticsFragment()).commit(); startConnectionsLoader(); } }); } private void showVpnDisconnected() { handler.postRunnable(new Runnable() { @Override public void run() { if (getSupportFragmentManager().findFragmentById(R.id.frame_sidebar) instanceof InfoFragment) return; viewConnStatus.setConnectionState(ConnectionStatusView.VpnState.DISCONNECTED); viewConnStatus.setConnectionState(ConnectionStatusView.VpnState.WAITING); InfoFragment frag = new InfoFragment(); getSupportFragmentManager().beginTransaction(). replace(R.id.frame_sidebar, frag).commit(); stopConnectionsLoader(); viewMap.showDisconnected(); frag.setState(viewSubscrStatus.isSubscriptionValid() ? InfoFragment.State.DISCONNECTED : InfoFragment.State.NO_SUBSCIPTION); } }); } public void syncVpnState() { VpnStateService.State curVpnState = VpnStateService.State.DISABLED; if (mService != null) { curVpnState = mService.getState(); } else { return; } // state change detected if (curVpnState != mVpnState || getSupportFragmentManager() .findFragmentById(R.id.frame_sidebar) instanceof WaitingIndicatorFragment || viewConnStatus.getConnectionState() == ConnectionStatusView.VpnState.WAITING) { switch (curVpnState) { case DISABLED: showVpnDisconnected(); handler.postRunnable(new Runnable() { @Override public void run() { viewConnStatus.setConnectionState(ConnectionStatusView.VpnState.DISCONNECTED); if (subscrManager.isFailed()) { handler.postRunnable(subscriptionsFetchRunnable); } else if (mOpenSubscriptions) { mOpenSubscriptions = false; openSubscriptionsActivity(); } } }); break; case CONNECTING: if (!(getSupportFragmentManager().findFragmentById(R.id.frame_sidebar) instanceof StatisticsFragment)) { showWaiting(); } break; case CONNECTED: viewConnStatus.setConnectionState(ConnectionStatusView.VpnState.CONNECTED); showVpnConnected(); break; case DISCONNECTING: if (!(getSupportFragmentManager().findFragmentById(R.id.frame_sidebar) instanceof InfoFragment)) { showWaiting(); } break; } } mVpnState = curVpnState; } private void showWaiting() { viewConnStatus.setConnectionState(ConnectionStatusView.VpnState.WAITING); if (!waitingFragment.isVisible()) { handler.postRunnable(new Runnable() { @Override public void run() { getSupportFragmentManager().beginTransaction(). replace(R.id.frame_sidebar, waitingFragment).commit(); } }); } } private void startConnectionsLoader() { viewMap.setAllowRecalcBounds(); btnRescaleMap.setVisibility(View.GONE); getSupportLoaderManager().restartLoader(LOADER_ID, null, this); } private void showNoConnection() { handler.postDelayedRunnable(subscriptionsFetchRunnable, SUBCR_FETCH_REPEAT_INTERVAL, true); viewConnStatus.setConnectionState(ConnectionStatusView.VpnState.UNAVAILABLE); viewMap.showNoNetwork(); viewSubscrStatus.setVisibility(View.GONE); setInfo(InfoFragment.State.NO_NETWORK, true); } private void stopConnectionsLoader(){ getSupportLoaderManager().destroyLoader(LOADER_ID); } }