Java tutorial
package online.privacy; /** * Configuration UI activity for the Privacy Online for Android app. * * Takes the username / password credentials for the Privacy Online account and validates them * against the Privacy Online API. As well as the default VPN location to use. * * Stores preferences using Android's builtin preferences manager which is private to this app. * * Copyright 2016, privacy.online * All rights reserved. * * This file is part of Privacy Online for Android. * * Privacy Online for Android 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. * * Privacy Online for Android 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 Privacy Online for Android. If not, see <http://www.gnu.org/licenses/>. * * @author James Ronan <jim@dev.uk2.net> */ import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.res.ColorStateList; import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkInfo; import android.net.Uri; import android.os.Bundle; import android.support.v4.content.ContextCompat; import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.animation.Animation; import android.widget.AdapterView; import android.widget.Button; import android.widget.EditText; import android.widget.GridLayout; import android.widget.ProgressBar; import android.widget.Toast; import java.util.ArrayList; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.ProfileManager; public class SetupActivity extends AppCompatActivity { final static private String LOG_TAG = "p.o.setup"; final private Context contextSetup = this; final private Activity activitySetup = this; private String defaultVPNLocationStart; private String defaultVPNLocationUpdate; private VerifyUserAccountReceiver verifyReceiver; private NetworkReceiver networkReceiver; private View connectivityWarning; private int connectivityWarningHeight; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_setup); connectivityWarning = findViewById(R.id.warning_connectivity); connectivityWarningHeight = (int) getResources().getDimension(R.dimen.warning_connectivity_height); SharedPreferences preferences = getSharedPreferences(getString(R.string.privacyonline_preferences), MODE_PRIVATE); defaultVPNLocationUpdate = defaultVPNLocationStart = preferences.getString("default_vpn_location", ""); final EditText usernameInput = (EditText) findViewById(R.id.input_text_username); final EditText passwordInput = (EditText) findViewById(R.id.input_password_password); usernameInput.setText(preferences.getString("username", "")); usernameInput.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { usernameInput.clearFocus(); passwordInput.requestFocus(); return true; } return false; } }); passwordInput.setText(preferences.getString("password", "")); // Ugh, apparently we can't define text on buttons to have the underlined property from // within the XML, so we'll do it here we have to set the intent chooser here anyway. Button buttonSignUp = (Button) findViewById(R.id.button_cta_signup); buttonSignUp.setPaintFlags(buttonSignUp.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); buttonSignUp.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent ctaIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.privacy_online_signup))); startActivity(ctaIntent); } }); final Button buttonSave = (Button) findViewById(R.id.button_save); buttonSave.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { EditText inputTextUsername = (EditText) findViewById(R.id.input_text_username); EditText inputTextPassword = (EditText) findViewById(R.id.input_password_password); clearErrorState(inputTextUsername); clearErrorState(inputTextPassword); setErrorInfoVisibility(View.INVISIBLE); setWorkingState(true); Intent apiIntent = new Intent(contextSetup, PrivacyOnlineAPIService.class); apiIntent.putExtra(PrivacyOnlineAPIService.PARAM_USERNAME, inputTextUsername.getText().toString()); apiIntent.putExtra(PrivacyOnlineAPIService.PARAM_PASSWORD, inputTextPassword.getText().toString()); apiIntent.setAction(PrivacyOnlineAPIService.ACTION_VERIFY_USERNAME); startService(apiIntent); } }); } @Override protected void onStart() { super.onStart(); // Register the IntentService Listeners to get the response of the user check and location list. IntentFilter verifyFilter = new IntentFilter(VerifyUserAccountReceiver.API_RESPONSE); verifyFilter.addCategory(Intent.CATEGORY_DEFAULT); verifyReceiver = new VerifyUserAccountReceiver(); registerReceiver(verifyReceiver, verifyFilter); // Register the NetworkReceiver to listen for changes to connectivity. IntentFilter networkChangeFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); networkReceiver = new NetworkReceiver(); this.registerReceiver(networkReceiver, networkChangeFilter); // Populate the Location list. VPNLocations vpnLocations = new VPNLocations(this); ArrayList<VPNLocation> locationList = vpnLocations.getArrayList(); final VPNLocationAdapter locationAdapter = new VPNLocationAdapter(this, R.layout.spinner_layout_full, locationList); PrivacyOnlineUtility utility = new PrivacyOnlineUtility(); utility.updateSpinnerValues(activitySetup, R.id.input_spinner_default_vpn_location, locationAdapter, false, new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) { VPNLocation location = locationAdapter.getItem(position); defaultVPNLocationUpdate = location.getHostname(); } @Override public void onNothingSelected(AdapterView<?> adapter) { } }); } @Override protected void onResume() { super.onResume(); } @Override protected void onPause() { super.onPause(); } @Override protected void onStop() { super.onStop(); if (verifyReceiver != null) { unregisterReceiver(verifyReceiver); } if (networkReceiver != null) { this.unregisterReceiver(networkReceiver); } } @Override protected void onDestroy() { super.onDestroy(); } // Private method to handle storing the supplied preferences. private void updatePreferences() { SharedPreferences preferences = getSharedPreferences(getString(R.string.privacyonline_preferences), MODE_PRIVATE); final SharedPreferences.Editor preferencesEditor = preferences.edit(); EditText inputTextUsername = (EditText) findViewById(R.id.input_text_username); String username = inputTextUsername.getText().toString(); EditText inputTextPassword = (EditText) findViewById(R.id.input_password_password); String password = inputTextPassword.getText().toString(); if (!defaultVPNLocationStart.equals(defaultVPNLocationUpdate)) { preferencesEditor.putString("default_vpn_location", defaultVPNLocationUpdate); preferencesEditor.putBoolean("default_vpn_location_changed", true); } preferencesEditor.putString("username", username); preferencesEditor.putString("password", password); preferencesEditor.apply(); } // Used to change the UI. This gives the user indication that the button was pressed. private void setWorkingState(boolean isWorking) { Button buttonSave = (Button) findViewById(R.id.button_save); ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_save); if (isWorking) { buttonSave.setVisibility(View.GONE); progressBar.setVisibility(View.VISIBLE); } else { progressBar.setVisibility(View.GONE); buttonSave.setVisibility(View.VISIBLE); } } // Updates the visibility of the Error info container. private void setErrorInfoVisibility(int visibility) { GridLayout errorInfo = (GridLayout) findViewById(R.id.credential_error_info); errorInfo.setVisibility(visibility); } // View.setBackgroundDrawable is deprecated from api16, it's now just setBackground(Drawable) // but we can't use that because we're using api14 as a min. @SuppressWarnings("deprecation") private void setErrorState(View view) { ColorStateList stateList = ContextCompat.getColorStateList(activitySetup, R.color.input_text_error); Drawable wrappedDrawable = DrawableCompat.wrap(view.getBackground()); DrawableCompat.setTintList(wrappedDrawable, stateList); view.setBackgroundDrawable(wrappedDrawable); } // View.setBackgroundDrawable is deprecated from api16, it's now just setBackground(Drawable) // but we can't use that because we're using api14 as a min. @SuppressWarnings("deprecation") private void clearErrorState(View view) { ColorStateList stateList = ContextCompat.getColorStateList(activitySetup, R.color.input_text_normal); Drawable wrappedDrawable = DrawableCompat.wrap(view.getBackground()); DrawableCompat.setTintList(wrappedDrawable, stateList); view.setBackgroundDrawable(wrappedDrawable); } /** * Public sub-class responsible for handling the result Broadcast from the PrivacyOnlineAPIService * service. */ public class VerifyUserAccountReceiver extends BroadcastReceiver { public static final String API_RESPONSE = "online.privacy.privacyonline.intent.action.RESPONSE_VERIFY_ACCOUNT"; @Override public void onReceive(Context context, Intent intent) { int checkResult = intent.getIntExtra(PrivacyOnlineAPIService.CHECK_RESULT, PrivacyOnlineApiRequest.VERIFY_ERROR); // Update the progressbar/button as we got a response. setWorkingState(false); // If the details were good, save the details and launch the ConnectionActivity Activity. if (checkResult == PrivacyOnlineApiRequest.VERIFY_OK) { Log.i(LOG_TAG, "User Account verified"); updatePreferences(); finish(); } else if (checkResult == PrivacyOnlineApiRequest.VERIFY_FAIL) { Log.i(LOG_TAG, "User Account verification failed"); EditText inputTextUsername = (EditText) findViewById(R.id.input_text_username); EditText inputTextPassword = (EditText) findViewById(R.id.input_password_password); setErrorState(inputTextUsername); setErrorState(inputTextPassword); setErrorInfoVisibility(View.VISIBLE); // This really shouldn't happen, but if it does, just prod them to update their app. } else { Toast toast = Toast.makeText(activitySetup, R.string.toast_update_privacy_online, Toast.LENGTH_LONG); toast.show(); } } } /** * BroadcastReceiver that monitors the network state and updates the Save button / message * accordingly. * * TODO - Should probably fix this to be in one place, shared between this and ConnectionActivity. */ public class NetworkReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { final ConnectivityManager connectivityManager = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); Button saveButton = (Button) findViewById(R.id.button_save); if (networkInfo != null && networkInfo.isConnected()) { saveButton.setEnabled(true); ShrinkAnimation shrinkAnimation = new ShrinkAnimation(connectivityWarning, connectivityWarningHeight, connectivityWarningHeight, 800); shrinkAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { connectivityWarning.setVisibility(View.GONE); } }); connectivityWarning.startAnimation(shrinkAnimation); } else { saveButton.setEnabled(false); ExpandAnimation expandAnimation = new ExpandAnimation(connectivityWarning, connectivityWarningHeight, 1, 800); expandAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { connectivityWarning.getLayoutParams().height = 1; connectivityWarning.setVisibility(View.VISIBLE); } @Override public void onAnimationEnd(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } }); connectivityWarning.startAnimation(expandAnimation); } } } }