From 3eb5405141d14a0c75c669c168b272878346cc38 Mon Sep 17 00:00:00 2001 From: Fup Duck Date: Sat, 27 Jan 2018 16:09:38 +0100 Subject: fix comments for mergerequest --- .../bitmaskclient/ProviderListBaseActivity.java | 478 +++++++++++++++++++++ 1 file changed, 478 insertions(+) create mode 100644 app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java (limited to 'app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java') diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java new file mode 100644 index 00000000..f60b5cc7 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java @@ -0,0 +1,478 @@ +/** + * Copyright (c) 2017 LEAP Encryption Access Project and contributors + * + * 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 . + */ + +package se.leap.bitmaskclient; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentTransaction; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.ListView; + +import com.pedrogomez.renderers.Renderer; + +import org.jetbrains.annotations.NotNull; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import butterknife.InjectView; +import butterknife.OnItemClick; +import se.leap.bitmaskclient.fragments.AboutFragment; + +import static android.view.View.GONE; +import static se.leap.bitmaskclient.Constants.APP_ACTION_QUIT; +import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; +import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; +import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP; +import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.ERRORS; +import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE; +import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_API_EVENT; +import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_NOK; +import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_OK; +import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_SET_UP; +import static se.leap.bitmaskclient.ProviderAPI.RESULT_CODE; +import static se.leap.bitmaskclient.ProviderAPI.RESULT_KEY; + +/** + * abstract base Activity that builds and shows the list of known available providers. + * The implementation of ProviderListBaseActivity differ in that they may or may not allow to bypass + * secure download mechanisms including certificate validation. + *

+ * It also allows the user to enter custom providers with a button. + * + * @author parmegv + * @author cyberta + */ + +public abstract class ProviderListBaseActivity extends ConfigWizardBaseActivity + implements NewProviderDialog.NewProviderDialogInterface, DownloadFailedDialog.DownloadFailedDialogInterface, ProviderAPIResultReceiver.Receiver { + + @InjectView(R.id.provider_list) + protected ListView providerListView; + @Inject + protected ProviderListAdapter adapter; + + private ProviderManager providerManager; + protected Intent mConfigState = new Intent(PROVIDER_NOT_SET); + + final public static String TAG = ProviderListActivity.class.getSimpleName(); + + final private static String ACTIVITY_STATE = "ACTIVITY STATE"; + + final protected static String PROVIDER_NOT_SET = "PROVIDER NOT SET"; + final protected static String SETTING_UP_PROVIDER = "PROVIDER GETS SET"; + final private static String SHOWING_PROVIDER_DETAILS = "SHOWING PROVIDER DETAILS"; + final private static String PENDING_SHOW_FAILED_DIALOG = "SHOW FAILED DIALOG"; + final private static String REASON_TO_FAIL = "REASON TO FAIL"; + final protected static String SERVICES_RETRIEVED = "SERVICES RETRIEVED"; + + public ProviderAPIResultReceiver providerAPIResultReceiver; + private ProviderAPIBroadcastReceiver providerAPIBroadcastReceiver; + + FragmentManagerEnhanced fragmentManager; + + private boolean isActivityShowing; + private String reasonToFail; + + public abstract void retrySetUpProvider(); + + protected abstract void onItemSelectedLogic(); + + + private void initProviderList() { + List> prototypes = new ArrayList<>(); + prototypes.add(new ProviderRenderer(this)); + ProviderRendererBuilder providerRendererBuilder = new ProviderRendererBuilder(prototypes); + adapter = new ProviderListAdapter(getLayoutInflater(), providerRendererBuilder, providerManager); + providerListView.setAdapter(adapter); + } + + @Override + protected void onSaveInstanceState(@NotNull Bundle outState) { + outState.putString(ACTIVITY_STATE, mConfigState.getAction()); + outState.putParcelable(Provider.KEY, provider); + + DialogFragment dialogFragment = (DialogFragment) fragmentManager.findFragmentByTag(DownloadFailedDialog.TAG); + if (dialogFragment != null) { + outState.putString(REASON_TO_FAIL, reasonToFail); + dialogFragment.dismiss(); + } + + super.onSaveInstanceState(outState); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + fragmentManager = new FragmentManagerEnhanced(getSupportFragmentManager()); + providerManager = ProviderManager.getInstance(getAssets(), getExternalFilesDir(null)); + + setUpInitialUI(); + + initProviderList(); + + if (savedInstanceState != null) + restoreState(savedInstanceState); + setUpProviderAPIResultReceiver(); + } + + private void restoreState(Bundle savedInstanceState) { + + provider = savedInstanceState.getParcelable(Provider.KEY); + mConfigState.setAction(savedInstanceState.getString(ACTIVITY_STATE, PROVIDER_NOT_SET)); + + reasonToFail = savedInstanceState.getString(REASON_TO_FAIL); + if(reasonToFail != null) { + showDownloadFailedDialog(); + } + + if (SETTING_UP_PROVIDER.equals(mConfigState.getAction()) || + PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction()) + ) { + showProgressBar(); + } + } + + @Override + protected void onResume() { + Log.d(TAG, "resuming with ConfigState: " + mConfigState.getAction()); + super.onResume(); + hideProgressBar(); + isActivityShowing = true; + if (SETTING_UP_PROVIDER.equals(mConfigState.getAction())) { + showProgressBar(); + adapter.hideAllBut(adapter.indexOf(provider)); + checkProviderSetUp(); + } else if (PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction())) { + showDownloadFailedDialog(); + } else if (SHOWING_PROVIDER_DETAILS.equals(mConfigState.getAction())) { + cancelAndShowAllProviders(); + } + } + + private void setUpInitialUI() { + setContentView(R.layout.configuration_wizard_activity); + setProviderHeaderText(R.string.setup_provider); + hideProgressBar(); + } + + @Override + protected void onPause() { + super.onPause(); + isActivityShowing = false; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (providerAPIBroadcastReceiver != null) + unregisterReceiver(providerAPIBroadcastReceiver); + providerAPIResultReceiver = null; + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_CODE_CONFIGURE_LEAP) { + if (resultCode == RESULT_OK) { + setResult(resultCode, data); + finish(); + } + } + } + + private void setUpProviderAPIResultReceiver() { + providerAPIResultReceiver = new ProviderAPIResultReceiver(new Handler(), this); + providerAPIBroadcastReceiver = new ProviderAPIBroadcastReceiver(); + + IntentFilter updateIntentFilter = new IntentFilter(PROVIDER_API_EVENT); + updateIntentFilter.addCategory(Intent.CATEGORY_DEFAULT); + registerReceiver(providerAPIBroadcastReceiver, updateIntentFilter); + } + + void handleProviderSetUp() { + try { + String providerJsonString = preferences.getString(Provider.KEY, ""); + if (!providerJsonString.isEmpty()) + provider.define(new JSONObject(providerJsonString)); + String caCert = preferences.getString(Provider.CA_CERT, ""); + provider.setCACert(caCert); + } catch (JSONException e) { + e.printStackTrace(); + } + + if (preferences.getBoolean(PROVIDER_ALLOW_ANONYMOUS, false)) { + mConfigState.putExtra(SERVICES_RETRIEVED, true); + + downloadVpnCertificate(); + } else { + showProviderDetails(); + } + } + + void handleProviderSetupFailed(Bundle resultData) { + mConfigState.setAction(PROVIDER_NOT_SET); + preferences.edit().remove(Provider.KEY).apply(); + + setResult(RESULT_CANCELED, mConfigState); + + reasonToFail = resultData.getString(ERRORS); + showDownloadFailedDialog(); + } + + void handleCorrectlyDownloadedCertificate() { + showProviderDetails(); + } + + void handleIncorrectlyDownloadedCertificate() { + mConfigState.setAction(PROVIDER_NOT_SET); + hideProgressBar(); + setResult(RESULT_CANCELED, mConfigState); + } + + @Override + public void onReceiveResult(int resultCode, Bundle resultData) { + if (resultCode == ProviderAPI.PROVIDER_OK) { + handleProviderSetUp(); + } else if (resultCode == AboutFragment.VIEWED) { + // Do nothing, right now + // I need this for CW to wait for the About activity to end before going back to Dashboard. + } + } + + @OnItemClick(R.id.provider_list) + void onItemSelected(int position) { + if (SETTING_UP_PROVIDER.equals(mConfigState.getAction()) || + PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction())) { + return; + } + + //TODO Code 2 pane view + mConfigState.setAction(SETTING_UP_PROVIDER); + provider = adapter.getItem(position); + showProgressBar(); + onItemSelectedLogic(); + } + + @Override + public void onBackPressed() { + if (SETTING_UP_PROVIDER.equals(mConfigState.getAction()) || + PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction())) { + stopSettingUpProvider(); + } else { + askDashboardToQuitApp(); + super.onBackPressed(); + } + } + + private void stopSettingUpProvider() { + ProviderAPI.stop(); + loadingScreen.setVisibility(GONE); + + cancelSettingUpProvider(); + } + + @Override + public void cancelSettingUpProvider() { + hideProgressBar(); + mConfigState.setAction(PROVIDER_NOT_SET); + adapter.showAllProviders(); + preferences.edit().remove(Provider.KEY).remove(PROVIDER_ALLOW_ANONYMOUS).remove(PROVIDER_KEY).apply(); + } + + @Override + public void updateProviderDetails() { + mConfigState.setAction(SETTING_UP_PROVIDER); + Intent providerAPICommand = new Intent(this, ProviderAPI.class); + + providerAPICommand.setAction(ProviderAPI.UPDATE_PROVIDER_DETAILS); + Bundle parameters = new Bundle(); + parameters.putString(Provider.MAIN_URL, provider.getMainUrl().toString()); + providerAPICommand.putExtra(ProviderAPI.PARAMETERS, parameters); + + startService(providerAPICommand); + } + + public void checkProviderSetUp() { + Intent providerAPICommand = new Intent(this, ProviderAPI.class); + providerAPICommand.setAction(PROVIDER_SET_UP); + providerAPICommand.putExtra(ProviderAPI.RECEIVER_KEY, providerAPIResultReceiver); + startService(providerAPICommand); + } + + private void askDashboardToQuitApp() { + Intent askQuit = new Intent(); + askQuit.putExtra(APP_ACTION_QUIT, APP_ACTION_QUIT); + setResult(RESULT_CANCELED, askQuit); + } + + /** + * Asks ProviderApiService to download an anonymous (anon) VPN certificate. + */ + private void downloadVpnCertificate() { + Intent providerAPICommand = new Intent(this, ProviderAPI.class); + providerAPICommand.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE); + startService(providerAPICommand); + } + + /** + * Open the new provider dialog + */ + public void addAndSelectNewProvider() { + FragmentTransaction fragmentTransaction = fragmentManager.removePreviousFragment(NewProviderDialog.TAG); + new NewProviderDialog().show(fragmentTransaction, NewProviderDialog.TAG); + } + + /** + * Open the new provider dialog with data + */ + public void addAndSelectNewProvider(String main_url) { + FragmentTransaction fragmentTransaction = fragmentManager.removePreviousFragment(NewProviderDialog.TAG); + + DialogFragment newFragment = new NewProviderDialog(); + Bundle data = new Bundle(); + data.putString(Provider.MAIN_URL, main_url); + newFragment.setArguments(data); + newFragment.show(fragmentTransaction, NewProviderDialog.TAG); + } + + /** + * Shows an error dialog, if configuring of a provider failed. + */ + public void showDownloadFailedDialog() { + try { + FragmentTransaction fragmentTransaction = fragmentManager.removePreviousFragment(DownloadFailedDialog.TAG); + DialogFragment newFragment; + try { + JSONObject errorJson = new JSONObject(reasonToFail); + newFragment = DownloadFailedDialog.newInstance(errorJson); + } catch (JSONException e) { + e.printStackTrace(); + newFragment = DownloadFailedDialog.newInstance(reasonToFail); + } + newFragment.show(fragmentTransaction, DownloadFailedDialog.TAG); + } catch (IllegalStateException e) { + e.printStackTrace(); + mConfigState.setAction(PENDING_SHOW_FAILED_DIALOG); + mConfigState.putExtra(REASON_TO_FAIL, reasonToFail); + } + + } + + /** + * Once selected a provider, this fragment offers the user to log in, + * use it anonymously (if possible) + * or cancel his/her election pressing the back button. + * + * + */ + public void showProviderDetails() { + // show only if current activity is shown + if (isActivityShowing && mConfigState.getAction() != null && + !mConfigState.getAction().equalsIgnoreCase(SHOWING_PROVIDER_DETAILS)) { + mConfigState.setAction(SHOWING_PROVIDER_DETAILS); + Intent intent = new Intent(this, ProviderDetailActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + intent.putExtra(PROVIDER_KEY, provider); + startActivityForResult(intent, REQUEST_CODE_CONFIGURE_LEAP); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.configuration_wizard_activity, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.about_leap: + startActivityForResult(new Intent(this, AboutFragment.class), 0); + return true; + case R.id.new_provider: + addAndSelectNewProvider(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + public void cancelAndShowAllProviders() { + mConfigState.setAction(PROVIDER_NOT_SET); + provider = null; + adapter.showAllProviders(); + } + + public class ProviderAPIBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Log.d(TAG, "received Broadcast"); + + String action = intent.getAction(); + if (action == null || !action.equalsIgnoreCase(PROVIDER_API_EVENT)) { + return; + } + + if (mConfigState.getAction() != null && + mConfigState.getAction().equalsIgnoreCase(SETTING_UP_PROVIDER)) { + int resultCode = intent.getIntExtra(RESULT_CODE, -1); + Log.d(TAG, "Broadcast resultCode: " + Integer.toString(resultCode)); + + Bundle resultData = intent.getParcelableExtra(RESULT_KEY); + String handledProvider = resultData.getString(Provider.KEY); + + String providerName = ConfigHelper.getProviderName(handledProvider); + String providerDomain = ConfigHelper.getProviderDomain(handledProvider); + + if (providerName != null && providerName.equalsIgnoreCase(provider.getName()) && + providerDomain != null && + providerDomain.equalsIgnoreCase(provider.getDomain())) { + switch (resultCode) { + case PROVIDER_OK: + handleProviderSetUp(); + break; + case PROVIDER_NOK: + handleProviderSetupFailed(resultData); + break; + } + } else { + switch (resultCode) { + case CORRECTLY_DOWNLOADED_CERTIFICATE: + handleCorrectlyDownloadedCertificate(); + break; + case INCORRECTLY_DOWNLOADED_CERTIFICATE: + handleIncorrectlyDownloadedCertificate(); + break; + } + } + } + } + } +} -- cgit v1.2.3