diff --git a/app/build.gradle b/app/build.gradle index e423523..cacb393 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -22,6 +22,9 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.1.0' + implementation 'com.android.support:design:26.1.0' + // jsoup HTML parser library @ https://jsoup.org/ + compile 'org.jsoup:jsoup:1.11.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c049992..0efebc0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/java/tech/goda/studyck/KeyStoreHelper.java b/app/src/main/java/tech/goda/studyck/KeyStoreHelper.java new file mode 100644 index 0000000..655451f --- /dev/null +++ b/app/src/main/java/tech/goda/studyck/KeyStoreHelper.java @@ -0,0 +1,219 @@ +package tech.goda.studyck; + +import android.content.Context; +import android.os.Build; +import android.security.KeyPairGeneratorSpec; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; +import android.support.annotation.RequiresApi; +import android.util.Base64; +import android.util.Log; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.util.Calendar; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.security.auth.x500.X500Principal; + +/** + * Created by Jerry on 2018/7/8. + */ + +public class KeyStoreHelper { + private static final String TAG = "KEYSTORE"; + + private static final String KEYSTORE_PROVIDER = "AndroidKeyStore"; + private static final String AES_MODE = "AES/GCM/NoPadding"; + private static final String RSA_MODE = "RSA/ECB/PKCS1Padding"; + + private static final String KEYSTORE_ALIAS = "KEYSTORE_DEMO"; + + + private KeyStore keyStore; + private SharedPreferencesHelper prefsHelper; + + public KeyStoreHelper(Context context, SharedPreferencesHelper sharedPreferencesHelper) { + try { + prefsHelper = sharedPreferencesHelper; + keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER); + keyStore.load(null); + + if (!keyStore.containsAlias(KEYSTORE_ALIAS)) { + prefsHelper.setIV(""); + genKeyStoreKey(context); + genAESKey(); + } + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + + private void genKeyStoreKey(Context context) throws Exception { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + generateRSAKey_AboveApi23(); + } else { + generateRSAKey_BelowApi23(context); + } + } + + + + @RequiresApi(api = Build.VERSION_CODES.M) + private void generateRSAKey_AboveApi23() throws Exception { + KeyPairGenerator keyPairGenerator = KeyPairGenerator + .getInstance(KeyProperties.KEY_ALGORITHM_RSA, KEYSTORE_PROVIDER); + + + KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec + .Builder(KEYSTORE_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) + .build(); + + keyPairGenerator.initialize(keyGenParameterSpec); + keyPairGenerator.generateKeyPair(); + + } + + private void generateRSAKey_BelowApi23(Context context) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException { + Calendar start = Calendar.getInstance(); + Calendar end = Calendar.getInstance(); + end.add(Calendar.YEAR, 30); + + KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context) + .setAlias(KEYSTORE_ALIAS) + .setSubject(new X500Principal("CN=" + KEYSTORE_ALIAS)) + .setSerialNumber(BigInteger.TEN) + .setStartDate(start.getTime()) + .setEndDate(end.getTime()) + .build(); + + KeyPairGenerator keyPairGenerator = KeyPairGenerator + .getInstance(KeyProperties.KEY_ALGORITHM_RSA, KEYSTORE_PROVIDER); + + keyPairGenerator.initialize(spec); + keyPairGenerator.generateKeyPair(); + } + + public String encrypt(String plainText) { + try { + return encryptAES(plainText); + + } catch (Exception e) { + Log.d(TAG, Log.getStackTraceString(e)); + return ""; + } + } + public String decrypt(String encryptedText) { + try { + return decryptAES(encryptedText); + + } catch (Exception e) { + Log.d(TAG, Log.getStackTraceString(e)); + return ""; + } + + } + + + private String encryptRSA(byte[] plainText) throws Exception { + PublicKey publicKey = keyStore.getCertificate(KEYSTORE_ALIAS).getPublicKey(); + + Cipher cipher = Cipher.getInstance(RSA_MODE); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + + byte[] encryptedByte = cipher.doFinal(plainText); + return Base64.encodeToString(encryptedByte, Base64.DEFAULT); + } + + + private byte[] decryptRSA(String encryptedText) throws Exception { + PrivateKey privateKey = (PrivateKey) keyStore.getKey(KEYSTORE_ALIAS, null); + + Cipher cipher = Cipher.getInstance(RSA_MODE); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + + byte[] encryptedBytes = Base64.decode(encryptedText, Base64.DEFAULT); + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + + return decryptedBytes; + } + + private void genAESKey() throws Exception { + // Generate AES-Key + byte[] aesKey = new byte[16]; + SecureRandom secureRandom = new SecureRandom(); + secureRandom.nextBytes(aesKey); + + + // Generate 12 bytes iv then save to SharedPrefs + byte[] generated = secureRandom.generateSeed(12); + String iv = Base64.encodeToString(generated, Base64.DEFAULT); + prefsHelper.setIV(iv); + + + // Encrypt AES-Key with RSA Public Key then save to SharedPrefs + String encryptAESKey = encryptRSA(aesKey); + prefsHelper.setAESKey(encryptAESKey); + } + + + /** + * AES Encryption + * @param plainText: A string which needs to be encrypted. + * @return A base64's string after encrypting. + */ + private String encryptAES(String plainText) throws Exception { + Cipher cipher = Cipher.getInstance(AES_MODE); + cipher.init(Cipher.ENCRYPT_MODE, getAESKey(), new IvParameterSpec(getIV())); + + // 加密過後的byte + byte[] encryptedBytes = cipher.doFinal(plainText.getBytes()); + + // 將byte轉為base64的string編碼 + return Base64.encodeToString(encryptedBytes, Base64.DEFAULT); + } + + + private String decryptAES(String encryptedText) throws Exception { + // 將加密過後的Base64編碼格式 解碼成 byte + byte[] decodedBytes = Base64.decode(encryptedText.getBytes(), Base64.DEFAULT); + + // 將解碼過後的byte 使用AES解密 + Cipher cipher = Cipher.getInstance(AES_MODE); + cipher.init(Cipher.DECRYPT_MODE, getAESKey(), new IvParameterSpec(getIV())); + + return new String(cipher.doFinal(decodedBytes)); + } + + + private byte[] getIV() { + String prefIV = prefsHelper.getIV(); + return Base64.decode(prefIV, Base64.DEFAULT); + } + + + + private SecretKeySpec getAESKey() throws Exception { + String encryptedKey = prefsHelper.getAESKey(); + byte[] aesKey = decryptRSA(encryptedKey); + + return new SecretKeySpec(aesKey, AES_MODE); + } + + + +} \ No newline at end of file diff --git a/app/src/main/java/tech/goda/studyck/LoginActivity.java b/app/src/main/java/tech/goda/studyck/LoginActivity.java new file mode 100644 index 0000000..68a732e --- /dev/null +++ b/app/src/main/java/tech/goda/studyck/LoginActivity.java @@ -0,0 +1,314 @@ +package tech.goda.studyck; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.annotation.TargetApi; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.support.annotation.NonNull; +import android.support.design.widget.Snackbar; +import android.support.v7.app.AppCompatActivity; +import android.app.LoaderManager.LoaderCallbacks; + +import android.content.CursorLoader; +import android.content.Loader; +import android.database.Cursor; +import android.net.Uri; +import android.os.AsyncTask; + +import android.os.Build; +import android.os.Bundle; +import android.provider.ContactsContract; +import android.text.TextUtils; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.inputmethod.EditorInfo; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static android.Manifest.permission.READ_CONTACTS; +import static tech.goda.studyck.Network.LOGIN_URI; + +/** + * A login screen that offers login via email/password. + */ +public class LoginActivity extends AppCompatActivity { + + /** + * Id to identity READ_CONTACTS permission request. + */ + private static final int REQUEST_READ_CONTACTS = 0; + + /** + * A dummy authentication store containing known user names and passwords. + * TODO: remove after connecting to a real authentication system. + */ + private static final String[] DUMMY_CREDENTIALS = new String[]{ + "foo@example.com:hello", "bar@example.com:world" + }; + /** + * Keep track of the login task to ensure we can cancel it if requested. + */ + private UserLoginTask mAuthTask = null; + + // UI references. + private AutoCompleteTextView mEmailView; + private EditText mPasswordView; + private View mProgressView; + private View mLoginFormView; + private String response = null; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_login); + // Set up the login form. + + + + + mEmailView = (AutoCompleteTextView) findViewById(R.id.email); + + mPasswordView = (EditText) findViewById(R.id.password); + mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { + if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) { + attemptLogin(); + return true; + } + return false; + } + }); + + Intent intent = this.getIntent(); + + try{ + mEmailView.setText(intent.getExtras().getString("account")); + mPasswordView.setText(intent.getExtras().getString("password")); + } catch(NullPointerException e){ + Log.e("Login", Log.getStackTraceString(e)); + } + + Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button); + mEmailSignInButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + attemptLogin(); + } + }); + + mLoginFormView = findViewById(R.id.login_form); + mProgressView = findViewById(R.id.login_progress); + } + + private boolean mayRequestContacts() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return true; + } + if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) { + return true; + } + if (shouldShowRequestPermissionRationale(READ_CONTACTS)) { + Snackbar.make(mEmailView, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE) + .setAction(android.R.string.ok, new View.OnClickListener() { + @Override + @TargetApi(Build.VERSION_CODES.M) + public void onClick(View v) { + requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS); + } + }); + } else { + requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS); + } + return false; + } + + /** + * Callback received when a permissions request has been completed. + */ + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + if (requestCode == REQUEST_READ_CONTACTS) { + if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + + } + } + } + + + /** + * Attempts to sign in or register the account specified by the login form. + * If there are form errors (invalid email, missing fields, etc.), the + * errors are presented and no actual login attempt is made. + */ + private void attemptLogin() { + if (mAuthTask != null) { + return; + } + + // Reset errors. + mEmailView.setError(null); + mPasswordView.setError(null); + + // Store values at the time of the login attempt. + String email = mEmailView.getText().toString(); + String password = mPasswordView.getText().toString(); + + boolean cancel = false; + View focusView = null; + + // Check for a valid password, if the user entered one. + if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) { + mPasswordView.setError(getString(R.string.error_invalid_password)); + focusView = mPasswordView; + cancel = true; + } + + // Check for a valid email address. + if (TextUtils.isEmpty(email)) { + mEmailView.setError(getString(R.string.error_field_required)); + focusView = mEmailView; + cancel = true; + } else if (!isEmailValid(email)) { + mEmailView.setError(getString(R.string.error_invalid_email)); + focusView = mEmailView; + cancel = true; + } + + if (cancel) { + // There was an error; don't attempt login and focus the first + // form field with an error. + focusView.requestFocus(); + } else { + // Show a progress spinner, and kick off a background task to + // perform the user login attempt. + showProgress(true); + mAuthTask = new UserLoginTask(email, password); + mAuthTask.execute((Void) null); + } + } + + private boolean isEmailValid(String email) { + //TODO: Replace this with your own logic + return true; + } + + private boolean isPasswordValid(String password) { + //TODO: Replace this with your own logic + return true; + } + + /** + * Shows the progress UI and hides the login form. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) + private void showProgress(final boolean show) { + // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow + // for very easy animations. If available, use these APIs to fade-in + // the progress spinner. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { + int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime); + + mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); + mLoginFormView.animate().setDuration(shortAnimTime).alpha( + show ? 0 : 1).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); + } + }); + + mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); + mProgressView.animate().setDuration(shortAnimTime).alpha( + show ? 1 : 0).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); + } + }); + } else { + // The ViewPropertyAnimator APIs are not available, so simply show + // and hide the relevant UI components. + mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); + mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); + } + } + + + /** + * Represents an asynchronous login/registration task used to authenticate + * the user. + */ + public class UserLoginTask extends AsyncTask { + + private final String mEmail; + private final String mPassword; + + UserLoginTask(String email, String password) { + mEmail = email; + mPassword = password; + } + + @Override + protected Boolean doInBackground(Void... params) { + // TODO: attempt authentication against a network service. + + // Simulate network access. + Map param = new HashMap<>(); + param.put("f_uid", mEmail); + param.put("f_pwd", mPassword); + response = Network.requestPost(LOGIN_URI, param); + //Thread.sleep(2000); + Log.e("Login", response); + return !response.contains("錯誤"); + + } + + @Override + protected void onPostExecute(final Boolean success) { + mAuthTask = null; + showProgress(false); + + if (success) { + Intent intent = new Intent(); + intent.putExtra("response", response); + intent.putExtra("account", mEmail); + intent.putExtra("password", mPassword); + setResult(RESULT_OK, intent); + finish(); + } else { + mPasswordView.setError(getString(R.string.error_incorrect_password)); + mPasswordView.requestFocus(); + } + } + + @Override + protected void onCancelled() { + mAuthTask = null; + showProgress(false); + } + } + + public boolean onKeyDown(int keyCode, KeyEvent event) { + if ((keyCode == KeyEvent.KEYCODE_BACK)) { + //Toast.makeText(getApplicationContext(), "請登入SAD", Toast.LENGTH_SHORT).show(); + finishAffinity(); + } + return super.onKeyDown(keyCode, event); + + } +} + diff --git a/app/src/main/java/tech/goda/studyck/MainActivity.java b/app/src/main/java/tech/goda/studyck/MainActivity.java index 1f99ade..fad6140 100644 --- a/app/src/main/java/tech/goda/studyck/MainActivity.java +++ b/app/src/main/java/tech/goda/studyck/MainActivity.java @@ -1,13 +1,305 @@ package tech.goda.studyck; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.app.Activity; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.DocumentsContract; +import android.support.annotation.NonNull; +import android.support.v4.provider.DocumentFile; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; +import android.text.Html; +import android.text.Layout; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.select.Elements; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.CookieHandler; +import java.net.CookieManager; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.KeyStore; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; public class MainActivity extends AppCompatActivity { + private static final int PICK_FILE_REQUEST = 0; + private static final int LOGIN = 1; + TextView messageText; + Button uploadButton, choose; + EditText editFileName; + int serverResponseCode = 0; + + String uploadServerUri = null; + InputStream in; + String fileName; + String loginResponse; + /********** File Path *************/ + String uploadFilePath = Environment.getExternalStorageDirectory().getPath() + "/test.png"; + Button button3; + + KeyStoreHelper keyStoreHelper; + SharedPreferencesHelper preferencesHelper; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + Log.e("Directory", uploadFilePath); + uploadButton = findViewById(R.id.uploadButton); + choose = findViewById(R.id.choose); + messageText = findViewById(R.id.messageText); + editFileName = findViewById(R.id.fileName); + button3 = findViewById(R.id.button3); + + CookieManager manager = new CookieManager(); + CookieHandler.setDefault(manager); + + preferencesHelper = new SharedPreferencesHelper(getApplicationContext()); + keyStoreHelper = new KeyStoreHelper(getApplicationContext(), preferencesHelper); + + messageText.setText("Uploading file path : " + uploadFilePath); + + + //uploadServerUri = "http://study.ck.tp.edu.tw/login_chk.asp"; + uploadServerUri = "http://192.168.173.104/WebPageTest/upload-big5.php"; + + uploadButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + + new Thread(new Runnable() { + public void run() { + runOnUiThread(new Runnable() { + public void run() { + messageText.setText("uploading started....."); + } + }); + + Network.uploadFile(uploadServerUri, in, editFileName.getText().toString()); + + } + }).start(); + } + }); + + button3.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final Map params = new HashMap<>(); + + params.put("f_mnuid", ""); + + new Thread(new Runnable() { + public void run() { + Network.requestPost("http://study.ck.tp.edu.tw", params); + } + }).start(); + } + }); + + choose.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showFileChooser(); + } + }); + + String encryptedText = preferencesHelper.getInput(); + final String mEmail = preferencesHelper.getString(SharedPreferencesHelper.PREF_AC); + final String mPassword = keyStoreHelper.decrypt(encryptedText); + if(mEmail.equals("") || mPassword.equals("")){ + callLogin(mEmail, mPassword); + } + else{ + final Map param = new HashMap<>(); + param.put("f_uid", mEmail); + param.put("f_pwd", mPassword); + Toast.makeText(getApplicationContext(), "自動登入中...", Toast.LENGTH_SHORT).show(); + + View layout = findViewById(android.R.id.content); + layout.setVisibility(View.GONE); + + new Thread(new Runnable() { + public void run() { + loginResponse = Network.requestPost(Network.LOGIN_URI, param); + //Thread.sleep(2000); + Log.e("Login", loginResponse); + + runOnUiThread(new Runnable() { + @Override + public void run() { + if(!loginResponse.contains("錯誤")){ + LoginSuccess(mEmail, mPassword); + } + else{ + Toast.makeText(getApplicationContext(), "Login Failed", Toast.LENGTH_SHORT).show(); + callLogin(mEmail, mPassword); + } + } + }); + } + }).start(); + + + //return !response.contains("錯誤"); + } + } + + private void callLogin(String account, String password) { + Intent intent = new Intent(MainActivity.this, LoginActivity.class); + intent.putExtra("account", account); + intent.putExtra("password", password); + startActivityForResult(intent, LOGIN); + } + + + private void LoginSuccess(String account, String password) { + View layout = findViewById(android.R.id.content); + Toast.makeText(getApplicationContext(), "登入成功!!", Toast.LENGTH_SHORT).show(); + Document doc = Jsoup.parse(loginResponse); + String name = doc.select("form > font").first().text(); + messageText.setText(name); + + // Save Login Information + String encryptedPassword = keyStoreHelper.encrypt(password); + preferencesHelper.setInput(encryptedPassword); + preferencesHelper.putString(SharedPreferencesHelper.PREF_AC, account); + + layout.setVisibility(View.VISIBLE); + + } + + private void showFileChooser() { + Intent intent = new Intent(); + //sets the select file to all types of files + intent.setType("*/*"); + //allows to select data and return it + intent.setAction(Intent.ACTION_GET_CONTENT); + //starts new activity to select file and return data + startActivityForResult(Intent.createChooser(intent,"Choose File to Upload.."), PICK_FILE_REQUEST); + } + + private boolean isVirtualFile(Uri uri) { + if (!DocumentsContract.isDocumentUri(this, uri)) { + return false; + } + + Cursor cursor = getContentResolver().query( + uri, + new String[] { DocumentsContract.Document.COLUMN_FLAGS }, + null, null, null); + + int flags = 0; + if (cursor.moveToFirst()) { + flags = cursor.getInt(0); + } + cursor.close(); + + return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0; + } + + private InputStream getInputStreamForVirtualFile(Uri uri, String mimeTypeFilter) + throws IOException { + + ContentResolver resolver = getContentResolver(); + + String[] openableMimeTypes = resolver.getStreamTypes(uri, mimeTypeFilter); + + if (openableMimeTypes == null || + openableMimeTypes.length < 1) { + throw new FileNotFoundException(); + } + + return resolver + .openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null) + .createInputStream(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + switch (requestCode){ + case PICK_FILE_REQUEST: + if(resultCode == RESULT_OK) { + if (data == null) { + //no data present + return; + } + + + Uri selectedFileUri = data.getData(); + fileName = DocumentFile.fromSingleUri(this, selectedFileUri).getName(); + editFileName.setText(fileName); + messageText.setText("Upload File:" + fileName + "(Change file name below)"); + Log.e("GetPathDocumentName", fileName); + + try { + if (isVirtualFile(selectedFileUri)) { + Log.e("GetPath", "This is virtual file"); + in = getInputStreamForVirtualFile(selectedFileUri, "*/*"); + + } else { + in = getContentResolver().openInputStream(selectedFileUri); + } + + + } catch (IOException e) { + e.printStackTrace(); + Log.e("GetPathError", e.toString()); + } + } + /*if(uploadFilePath != null && !uploadFilePath.equals("")){ + messageText.setText(uploadFilePath); + }else{ + Toast.makeText(this,"Cannot upload file to server",Toast.LENGTH_SHORT).show(); + }*/ + break; + case LOGIN: + if(resultCode == RESULT_OK){ + Bundle bundle = data.getExtras(); + String account = bundle.getString("account"); + String password = bundle.getString("password"); + loginResponse = bundle.getString("response"); + //messageText.setText(loginResponse); + LoginSuccess(account, password); + } + break; + } + + + } + + + } diff --git a/app/src/main/java/tech/goda/studyck/Network.java b/app/src/main/java/tech/goda/studyck/Network.java new file mode 100644 index 0000000..c9be78d --- /dev/null +++ b/app/src/main/java/tech/goda/studyck/Network.java @@ -0,0 +1,381 @@ +package tech.goda.studyck; + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.provider.DocumentsContract; +import android.support.v4.provider.DocumentFile; +import android.util.Log; +import android.widget.Toast; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Map; +import java.util.Set; + +/** + * Created by Jerry on 2018/7/7. + */ + +public class Network { + public static final String LOGIN_URI = "http://study.ck.tp.edu.tw/login_chk.asp"; + + public static String uploadFile(String sourceFileUri, InputStream in, String uploadFileName) { + + + //String fileName = sourceFileUri; + HttpURLConnection conn = null; + //CookieManager cookieManager = null; + DataOutputStream dos = null; + String lineEnd = "\r\n"; + String twoHyphens = "--"; + String boundary = "*****"; + int bytesRead, bytesAvailable, bufferSize; + byte[] buffer; + int maxBufferSize = 1 * 1024 * 1024; + + //File sourceFile = new File(sourceFileUri); + String response = null; + if(in == null){ + /*runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(MainActivity.this, "Please Choose File First.", Toast.LENGTH_SHORT).show(); + } + });*/ + return null; + } + else{ + try { + + // open a URL connection to the Servlet + //FileInputStream fileInputStream = new FileInputStream(sourceFile); + URL url = new URL(sourceFileUri); + + // Open a HTTP connection to the URL + conn = (HttpURLConnection) url.openConnection(); + conn.setDoInput(true); // Allow Inputs + conn.setDoOutput(true); // Allow Outputs + conn.setUseCaches(false); // Don't use a Cached Copy + conn.setRequestMethod("POST"); + conn.setRequestProperty("Connection", "Keep-Alive"); + + conn.setRequestProperty("ENCTYPE", "multipart/form-data"); + conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); + conn.setRequestProperty("f_file", uploadFileName); + + dos = new DataOutputStream(conn.getOutputStream()); + + dos.writeBytes(twoHyphens + boundary + lineEnd); + dos.writeBytes("Content-Disposition: form-data; name=\"f_hwintro\""+ lineEnd + lineEnd); + dos.writeBytes("" + lineEnd); + dos.writeBytes(twoHyphens + boundary + lineEnd); + dos.writeBytes("Content-Disposition: form-data; name=\"f_file\";filename=\""); + dos.write(uploadFileName.getBytes()); + dos.writeBytes("\"" + lineEnd); + + dos.writeBytes(lineEnd); + + // create a buffer of maximum size + bytesAvailable = in.available(); + + bufferSize = Math.min(bytesAvailable, maxBufferSize); + buffer = new byte[bufferSize]; + + // read file and write it into form... + bytesRead = in.read(buffer, 0, bufferSize); + while (bytesRead > 0) { + + dos.write(buffer, 0, bufferSize); + bytesAvailable = in.available(); + bufferSize = Math.min(bytesAvailable, maxBufferSize); + bytesRead = in.read(buffer, 0, bufferSize); + + } + + // send multipart form data necessary after file data... + dos.writeBytes(lineEnd); + dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); + + // Responses from the server (code and message) + int serverResponseCode = conn.getResponseCode(); + String serverResponseMessage = conn.getResponseMessage(); + try{ + BufferedReader br; + if (200 <= serverResponseCode && serverResponseCode <= 299) { + br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "big5")); + } else { + br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "big5")); + } + StringBuilder sb = new StringBuilder(); + String output; + while ((output = br.readLine()) != null) { + sb.append(output); + } + + response = sb.toString(); + Log.e("Response", response); + } catch(java.io.IOException e){ + e.printStackTrace(); + } + Log.e("uploadFile", "HTTP Response is : " + + serverResponseMessage + ": " + serverResponseCode); + + if(serverResponseCode == 200){ + + /*runOnUiThread(new Runnable() { + public void run() { + + String msg = "File Upload Completed."; + + messageText.setText(msg); + //Toast.makeText(MainActivity.this, "File Upload Completed.", + //Toast.LENGTH_SHORT).show(); + } + });*/ + } + + //close the streams // + in.close(); + dos.flush(); + dos.close(); + + } catch (MalformedURLException e) { + + e.printStackTrace(); + Log.e("Upload file to server", "error: " + e.getMessage(), e); + } catch (Exception e) { + e.printStackTrace(); + Log.e("Upload", "Exception : " + + e.getMessage(), e); + } + return response; + } + /*if (!sourceFile.isFile()) { + + Log.e("uploadFile", "Source File not exist :" + +uploadFilePath); + + return 0; + + } + + else + { + try { + + // open a URL connection to the Servlet + FileInputStream fileInputStream = new FileInputStream(sourceFile); + URL url = new URL(uploadServerUri); + + // Open a HTTP connection to the URL + conn = (HttpURLConnection) url.openConnection(); + conn.setDoInput(true); // Allow Inputs + conn.setDoOutput(true); // Allow Outputs + conn.setUseCaches(false); // Don't use a Cached Copy + conn.setRequestMethod("POST"); + conn.setRequestProperty("Connection", "Keep-Alive"); + conn.setRequestProperty("ENCTYPE", "multipart/form-data"); + conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); + conn.setRequestProperty("f_file", fileName); + + dos = new DataOutputStream(conn.getOutputStream()); + + dos.writeBytes(twoHyphens + boundary + lineEnd); + dos.writeBytes("Content-Disposition: form-data; name=\"f_hwintro\""+ lineEnd + lineEnd); + dos.writeBytes("" + lineEnd); + dos.writeBytes(twoHyphens + boundary + lineEnd); + dos.writeBytes("Content-Disposition: form-data; name=\"f_file\";filename=\"" + + fileName + "\"" + lineEnd); + + dos.writeBytes(lineEnd); + + // create a buffer of maximum size + bytesAvailable = fileInputStream.available(); + + bufferSize = Math.min(bytesAvailable, maxBufferSize); + buffer = new byte[bufferSize]; + + // read file and write it into form... + bytesRead = fileInputStream.read(buffer, 0, bufferSize); + while (bytesRead > 0) { + + dos.write(buffer, 0, bufferSize); + bytesAvailable = fileInputStream.available(); + bufferSize = Math.min(bytesAvailable, maxBufferSize); + bytesRead = fileInputStream.read(buffer, 0, bufferSize); + + } + + // send multipart form data necesssary after file data... + dos.writeBytes(lineEnd); + dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); + + + + // Responses from the server (code and message) + serverResponseCode = conn.getResponseCode(); + String serverResponseMessage = conn.getResponseMessage(); + try{ + BufferedReader br = null; + if (200 <= serverResponseCode && serverResponseCode <= 299) { + br = new BufferedReader(new InputStreamReader(conn.getInputStream())); + } else { + br = new BufferedReader(new InputStreamReader(conn.getErrorStream())); + } + StringBuilder sb = new StringBuilder(); + String output; + while ((output = br.readLine()) != null) { + sb.append(output); + } + + Log.e("Response", sb.toString()); + } catch(java.io.IOException e){ + e.printStackTrace(); + } + Log.e("uploadFile", "HTTP Response is : " + + serverResponseMessage + ": " + serverResponseCode); + + if(serverResponseCode == 200){ + + runOnUiThread(new Runnable() { + public void run() { + + String msg = "File Upload Completed."; + + messageText.setText(msg); + Toast.makeText(MainActivity.this, "File Upload Complete.", + Toast.LENGTH_SHORT).show(); + } + }); + } + + //close the streams // + fileInputStream.close(); + dos.flush(); + dos.close(); + + } catch (MalformedURLException e) { + + e.printStackTrace(); + + + + Log.e("Upload file to server", "error: " + e.getMessage(), e); + } catch (Exception e) { + + e.printStackTrace(); + + + Log.e("Upload", "Exception : " + + e.getMessage(), e); + } + return serverResponseCode; + + } // End else block + */ + } + + + + public static String requestPost(String uri, Map params){ + HttpURLConnection conn; + DataOutputStream dos; + String response = null; + try { + // open a URL connection to the Servlet + URL url = new URL(uri); + // Open a HTTP connection to the URL + conn = (HttpURLConnection) url.openConnection(); + conn.setDoInput(true); // Allow Inputs + conn.setDoOutput(true); // Allow Outputs + conn.setUseCaches(false); // Don't use a Cached Copy + conn.setRequestMethod("POST"); + conn.setRequestProperty("Connection", "Keep-Alive"); + dos = new DataOutputStream(conn.getOutputStream()); + + Set keySet = params.keySet(); + StringBuilder sb = new StringBuilder(); + for (Object objKey : keySet) { + //有了鍵就可以通過map集合的get方法獲取其對應的値 + + String key = objKey.toString(); + String value = params.get(key); + + sb.append(key).append("=").append(value).append("&"); + + Log.e("Params", "key: " + key + ", value: " + value); + } + if(params.size() != 0){ + sb.deleteCharAt(sb.length()-1); + } + + Log.e("StringBuilder", sb.toString()); + dos.writeBytes(sb.toString()); + + + // Responses from the server (code and message) + int serverResponseCode = conn.getResponseCode(); + String serverResponseMessage = conn.getResponseMessage(); + try{ + BufferedReader br; + if (200 <= serverResponseCode && serverResponseCode <= 299) { + br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "big5")); + } else { + br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "UTF-8")); + } + sb = new StringBuilder(); + String output; + while ((output = br.readLine()) != null) { + sb.append(output); + } + response = sb.toString(); + Log.e("Response", response); + } catch(java.io.IOException e){ + e.printStackTrace(); + } + Log.e("uploadFile", "HTTP Response is : " + + serverResponseMessage + ": " + serverResponseCode); + + if(serverResponseCode == 200){ + + /*runOnUiThread(new Runnable() { + public void run() { + + String msg = "File Upload Completed."; + + messageText.setText(msg); + //Toast.makeText(MainActivity.this, "File Upload Completed.", + //Toast.LENGTH_SHORT).show(); + } + });*/ + } + + dos.flush(); + dos.close(); + + } catch (MalformedURLException e) { + + e.printStackTrace(); + Log.e("Upload file to server", "error: " + e.getMessage(), e); + } catch (Exception e) { + e.printStackTrace(); + Log.e("Upload", "Exception : " + + e.getMessage(), e); + } + + return response; + } + + + +} diff --git a/app/src/main/java/tech/goda/studyck/SharedPreferencesHelper.java b/app/src/main/java/tech/goda/studyck/SharedPreferencesHelper.java new file mode 100644 index 0000000..8cc6431 --- /dev/null +++ b/app/src/main/java/tech/goda/studyck/SharedPreferencesHelper.java @@ -0,0 +1,76 @@ +package tech.goda.studyck; + +import android.content.Context; +import android.content.SharedPreferences; + +/** + * Created by Jerry on 2018/7/8. + */ + + +public class SharedPreferencesHelper { + + private static final String SHARED_PREF_NAME = "KEYSTORE_SETTING"; + + private static final String PREF_KEY_AES = "PREF_KEY_AES"; + private static final String PREF_KEY_IV = "PREF_KEY_IV"; + private static final String PREF_KEY_INPUT = "PREF_KEY_INPUT"; + public static final String PREF_AC = "PREF_AC"; + + private SharedPreferences sharedPreferences; + + + + SharedPreferencesHelper(Context context){ + sharedPreferences = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE); + } + + + String getString(String key) { + return sharedPreferences.getString(key, ""); + } + + void putString(String key, String value) { + sharedPreferences.edit() + .putString(key, value) + .apply(); + } + + private boolean getBoolean(String key) { + return sharedPreferences.getBoolean(key, false); + } + + private void putBoolean(String key, boolean value) { + sharedPreferences.edit() + .putBoolean(key, value) + .apply(); + } + + + + + void setIV(String value) { + putString(PREF_KEY_IV, value); + } + + String getIV() { + return getString(PREF_KEY_IV); + } + + void setAESKey(String value) { + putString(PREF_KEY_AES, value); + } + + String getAESKey() { + return getString(PREF_KEY_AES); + } + + public void setInput(String value) { + putString(PREF_KEY_INPUT, value); + } + + String getInput() { + return getString(PREF_KEY_INPUT); + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..1388501 --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + +