Java tutorial
/* * FreeOTP * * Authors: Nathaniel McCallum <npmccallum@redhat.com> * * Copyright (C) 2013 Nathaniel McCallum, Red Hat * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Portions Copyright 2009 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.fedorahosted.freeotp; import android.Manifest; import android.content.pm.PackageManager; import android.os.AsyncTask; import android.os.Handler; import android.os.ParcelFileDescriptor; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.widget.Toast; import org.fedorahosted.freeotp.add.AddActivity; import org.fedorahosted.freeotp.add.ScanActivity; import android.app.Activity; import android.content.Intent; import android.database.DataSetObserver; import android.net.Uri; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.MenuItem.OnMenuItemClickListener; import android.view.View; import android.view.WindowManager.LayoutParams; import android.widget.GridView; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; public class MainActivity extends AppCompatActivity implements OnMenuItemClickListener { private static final int CAMERA_PERMISSION_REQUEST = 10; private TokenAdapter mTokenAdapter; private DataSetObserver mDataSetObserver; private Handler handler; private TokenPersistence tokenPersistence; private static final int READ_REQUEST_CODE = 42; private static final int WRITE_REQUEST_CODE = 43; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); onNewIntent(getIntent()); setContentView(R.layout.main); mTokenAdapter = new TokenAdapter(this); ((GridView) findViewById(R.id.grid)).setAdapter(mTokenAdapter); // Don't permit screenshots since these might contain OTP codes. getWindow().setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE); mDataSetObserver = new DataSetObserver() { @Override public void onChanged() { super.onChanged(); if (mTokenAdapter.getCount() == 0) findViewById(android.R.id.empty).setVisibility(View.VISIBLE); else findViewById(android.R.id.empty).setVisibility(View.GONE); } }; mTokenAdapter.registerDataSetObserver(mDataSetObserver); tokenPersistence = new TokenPersistence(this); } @Override protected void onResume() { super.onResume(); mTokenAdapter.notifyDataSetChanged(); } @Override protected void onPause() { super.onPause(); mTokenAdapter.notifyDataSetChanged(); } @Override protected void onDestroy() { super.onDestroy(); mTokenAdapter.unregisterDataSetObserver(mDataSetObserver); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); menu.findItem(R.id.action_scan).setVisible(ScanActivity.haveCamera()); menu.findItem(R.id.action_scan).setOnMenuItemClickListener(this); menu.findItem(R.id.action_add).setOnMenuItemClickListener(this); menu.findItem(R.id.action_import).setOnMenuItemClickListener(this); menu.findItem(R.id.action_export).setOnMenuItemClickListener(this); menu.findItem(R.id.action_about).setOnMenuItemClickListener(this); return true; } @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.action_scan: scanQRCode(); return true; case R.id.action_add: startActivity(new Intent(this, AddActivity.class)); return true; case R.id.action_import: performFileSearch(); return true; case R.id.action_export: createFile("application/json", "freeotp-backup.json"); return true; case R.id.action_about: startActivity(new Intent(this, AboutActivity.class)); return true; } return false; } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Uri uri = intent.getData(); if (uri != null) TokenPersistence.addWithToast(this, uri.toString()); } @Override public void onActivityResult(int requestCode, int resultCode, Intent resultData) { // The ACTION_OPEN_DOCUMENT intent was sent with the request code // READ_REQUEST_CODE. If the request code seen here doesn't match, it's the // response to some other intent, and the code below shouldn't run at all. if (requestCode == WRITE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { // The document selected by the user won't be returned in the intent. // Instead, a URI to that document will be contained in the return intent // provided to this method as a parameter. // Pull that URI using resultData.getData(). Uri uri = null; if (resultData != null) { uri = resultData.getData(); exportToFile(uri); } } // The ACTION_OPEN_DOCUMENT intent was sent with the request code // READ_REQUEST_CODE. If the request code seen here doesn't match, it's the // response to some other intent, and the code below shouldn't run at all. if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) { // The document selected by the user won't be returned in the intent. // Instead, a URI to that document will be contained in the return intent // provided to this method as a parameter. // Pull that URI using resultData.getData(). Uri uri = null; if (resultData != null) { uri = resultData.getData(); importFromFile(uri); } } } /** * Fires an intent to spin up the "file chooser" UI and select an image. */ public void performFileSearch() { // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file // browser. Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); // Filter to only show results that can be "opened", such as a // file (as opposed to a list of contacts or timezones) intent.addCategory(Intent.CATEGORY_OPENABLE); // Filter to show only images, using the image MIME data type. // If one wanted to search for ogg vorbis files, the type would be "audio/ogg". // To search for all documents available via installed storage providers, // it would be "*/*". intent.setType("application/json"); startActivityForResult(intent, READ_REQUEST_CODE); } @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case CAMERA_PERMISSION_REQUEST: { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { startActivity(new Intent(this, ScanActivity.class)); overridePendingTransition(R.anim.fadein, R.anim.fadeout); } else { Toast.makeText(this, R.string.camera_permission_denied_text, Toast.LENGTH_LONG).show(); } return; } // other 'case' lines to check for other // permissions this app might request } } private void createFile(String mimeType, String fileName) { Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); // Filter to only show results that can be "opened", such as // a file (as opposed to a list of contacts or timezones). intent.addCategory(Intent.CATEGORY_OPENABLE); // Create a file with the requested MIME type. intent.setType(mimeType); intent.putExtra(Intent.EXTRA_TITLE, fileName); startActivityForResult(intent, WRITE_REQUEST_CODE); } private void scanQRCode() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.CAMERA }, CAMERA_PERMISSION_REQUEST); } else { startActivity(new Intent(this, ScanActivity.class)); overridePendingTransition(R.anim.fadein, R.anim.fadeout); } } private void exportToFile(final Uri uri) { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { try { ParcelFileDescriptor pfd = MainActivity.this.getContentResolver().openFileDescriptor(uri, "w"); FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor()); fileOutputStream.write(tokenPersistence.toJSON().getBytes()); // Let the document provider know you're done by closing the stream. fileOutputStream.close(); pfd.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); Toast.makeText(MainActivity.this, R.string.export_succeeded_text, Toast.LENGTH_LONG).show(); } }.execute((Void) null); } private void importFromFile(final Uri uri) { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { try { InputStream inputStream = getContentResolver().openInputStream(uri); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); StringBuilder stringBuilder = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { stringBuilder.append(line); } inputStream.close(); String jsonString = stringBuilder.toString(); tokenPersistence.importFromJSON(jsonString); } catch (IOException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); Toast.makeText(MainActivity.this, R.string.import_succeeded_text, Toast.LENGTH_LONG).show(); mTokenAdapter.notifyDataSetChanged(); } }.execute((Void) null); } }