diff --git a/COPYING.icons b/COPYING.icons deleted file mode 100644 index dfdcaf3a..00000000 --- a/COPYING.icons +++ /dev/null @@ -1,10 +0,0 @@ -Unless otherwise noted, icons used by this application are reproduced from -the Android Action Bar Icon Pack which is work created and shared by the -Android Open Source Project and used according to terms described in the -Creative Commons 2.5 Attribution License. - - * http://creativecommons.org/licenses/by/2.5/legalcode - * https://developer.android.com/license.html - -Original Icons: - * ic_launcher.png - Copyright 2013: Red Hat, Inc. diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index 82370767..00000000 --- a/app/build.gradle +++ /dev/null @@ -1,36 +0,0 @@ -apply plugin: 'com.android.application' - -dependencies { - implementation 'com.google.zxing:core:3.3.0' - implementation 'com.google.code.gson:gson:2.8.5' - implementation 'com.squareup.picasso:picasso:2.5.2' - implementation 'io.fotoapparat.fotoapparat:library:1.4.1' - testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:1.10.19' -} - -allprojects { - gradle.projectsEvaluated { - tasks.withType(JavaCompile) { - options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" - } - } -} - -android { - lintOptions { - abortOnError false - } - - testOptions { - unitTests.returnDefaultValues = true - } - - defaultConfig { - minSdkVersion 23 - targetSdkVersion 29 - } - - compileSdkVersion 29 // NOTE: update build-tools-* in .travis.yml - buildToolsVersion '29.0.2' // NOTE: update build-tools-* in .travis.yml -} diff --git a/app/src/main/ic_freeotp_logo-web.png b/app/src/main/ic_freeotp_logo-web.png deleted file mode 100644 index 540fb101..00000000 Binary files a/app/src/main/ic_freeotp_logo-web.png and /dev/null differ diff --git a/app/src/main/java/com/google/android/apps/authenticator/Base32String.java b/app/src/main/java/com/google/android/apps/authenticator/Base32String.java deleted file mode 100644 index d5d55313..00000000 --- a/app/src/main/java/com/google/android/apps/authenticator/Base32String.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2009 Google Inc. All Rights Reserved. - * - * 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 com.google.android.apps.authenticator; - -import java.util.HashMap; -import java.util.Locale; - -/** - * Encodes arbitrary byte arrays as case-insensitive base-32 strings. - *

- * The implementation is slightly different than in RFC 4648. During encoding, - * padding is not added, and during decoding the last incomplete chunk is not - * taken into account. The result is that multiple strings decode to the same - * byte array, for example, string of sixteen 7s ("7...7") and seventeen 7s both - * decode to the same byte array. - * TODO(sarvar): Revisit this encoding and whether this ambiguity needs fixing. - * - * @author sweis@google.com (Steve Weis) - * @author Neal Gafter - */ -public class Base32String { - // singleton - - private static final Base32String INSTANCE = - new Base32String("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"); // RFC 4648/3548 - - static Base32String getInstance() { - return INSTANCE; - } - - // 32 alpha-numeric characters. - private String ALPHABET; - private char[] DIGITS; - private int MASK; - private int SHIFT; - private HashMap CHAR_MAP; - - static final String SEPARATOR = "-"; - - protected Base32String(String alphabet) { - this.ALPHABET = alphabet; - DIGITS = ALPHABET.toCharArray(); - MASK = DIGITS.length - 1; - SHIFT = Integer.numberOfTrailingZeros(DIGITS.length); - CHAR_MAP = new HashMap(); - for (int i = 0; i < DIGITS.length; i++) { - CHAR_MAP.put(DIGITS[i], i); - } - } - - public static byte[] decode(String encoded) throws DecodingException { - return getInstance().decodeInternal(encoded); - } - - protected byte[] decodeInternal(String encoded) throws DecodingException { - // Remove whitespace and separators - encoded = encoded.trim().replaceAll(SEPARATOR, "").replaceAll(" ", ""); - - // Remove padding. Note: the padding is used as hint to determine how many - // bits to decode from the last incomplete chunk (which is commented out - // below, so this may have been wrong to start with). - encoded = encoded.replaceFirst("[=]*$", ""); - - // Canonicalize to all upper case - encoded = encoded.toUpperCase(Locale.US); - if (encoded.length() == 0) { - return new byte[0]; - } - int encodedLength = encoded.length(); - int outLength = encodedLength * SHIFT / 8; - byte[] result = new byte[outLength]; - int buffer = 0; - int next = 0; - int bitsLeft = 0; - for (char c : encoded.toCharArray()) { - if (!CHAR_MAP.containsKey(c)) { - throw new DecodingException("Illegal character: " + c); - } - buffer <<= SHIFT; - buffer |= CHAR_MAP.get(c) & MASK; - bitsLeft += SHIFT; - if (bitsLeft >= 8) { - result[next++] = (byte) (buffer >> (bitsLeft - 8)); - bitsLeft -= 8; - } - } - // We'll ignore leftover bits for now. - // - // if (next != outLength || bitsLeft >= SHIFT) { - // throw new DecodingException("Bits left: " + bitsLeft); - // } - return result; - } - - public static String encode(byte[] data) { - return getInstance().encodeInternal(data); - } - - protected String encodeInternal(byte[] data) { - if (data.length == 0) { - return ""; - } - - // SHIFT is the number of bits per output character, so the length of the - // output is the length of the input multiplied by 8/SHIFT, rounded up. - if (data.length >= (1 << 28)) { - // The computation below will fail, so don't do it. - throw new IllegalArgumentException(); - } - - int outputLength = (data.length * 8 + SHIFT - 1) / SHIFT; - StringBuilder result = new StringBuilder(outputLength); - - int buffer = data[0]; - int next = 1; - int bitsLeft = 8; - while (bitsLeft > 0 || next < data.length) { - if (bitsLeft < SHIFT) { - if (next < data.length) { - buffer <<= 8; - buffer |= (data[next++] & 0xff); - bitsLeft += 8; - } else { - int pad = SHIFT - bitsLeft; - buffer <<= pad; - bitsLeft += pad; - } - } - int index = MASK & (buffer >> (bitsLeft - SHIFT)); - bitsLeft -= SHIFT; - result.append(DIGITS[index]); - } - return result.toString(); - } - - @Override - // enforce that this class is a singleton - public Object clone() throws CloneNotSupportedException { - throw new CloneNotSupportedException(); - } - - public static class DecodingException extends Exception { - public DecodingException(String message) { - super(message); - } - } -} diff --git a/app/src/main/java/org/fedorahosted/freeotp/AboutActivity.java b/app/src/main/java/org/fedorahosted/freeotp/AboutActivity.java deleted file mode 100644 index 22715d89..00000000 --- a/app/src/main/java/org/fedorahosted/freeotp/AboutActivity.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * FreeOTP - * - * Authors: Nathaniel McCallum - * - * 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. - */ - -package org.fedorahosted.freeotp; - -import android.app.Activity; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.os.Bundle; -import android.text.Html; -import android.text.method.LinkMovementMethod; -import android.widget.TextView; - -public class AboutActivity extends Activity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.about); - } - - @Override - @SuppressWarnings("deprecation") - //suppress because Html.fromHtml(String, int) requires minSdkVersion 24 - public void onStart() { - super.onStart(); - - Resources res = getResources(); - TextView tv; - - try { - PackageManager pm = getPackageManager(); - PackageInfo info = pm.getPackageInfo(getPackageName(), 0); - String version = res.getString(R.string.about_version, info.versionName, info.versionCode); - tv = findViewById(R.id.about_version); - tv.setText(version); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - - String apache2 = res.getString(R.string.link_apache2); - String license = res.getString(R.string.about_license, apache2); - tv = findViewById(R.id.about_license); - tv.setMovementMethod(LinkMovementMethod.getInstance()); - tv.setText(Html.fromHtml(license)); - - String lwebsite = res.getString(R.string.link_website); - String swebsite = res.getString(R.string.about_website, lwebsite); - tv = findViewById(R.id.about_website); - tv.setMovementMethod(LinkMovementMethod.getInstance()); - tv.setText(Html.fromHtml(swebsite)); - } -} diff --git a/app/src/main/java/org/fedorahosted/freeotp/BaseReorderableAdapter.java b/app/src/main/java/org/fedorahosted/freeotp/BaseReorderableAdapter.java deleted file mode 100644 index b0bfe229..00000000 --- a/app/src/main/java/org/fedorahosted/freeotp/BaseReorderableAdapter.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * FreeOTP - * - * Authors: Nathaniel McCallum - * - * 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. - */ - -package org.fedorahosted.freeotp; - -import android.content.ClipData; -import android.view.DragEvent; -import android.view.View; -import android.view.View.DragShadowBuilder; -import android.view.View.OnDragListener; -import android.view.View.OnLongClickListener; -import android.view.ViewGroup; -import android.widget.BaseAdapter; - -public abstract class BaseReorderableAdapter extends BaseAdapter { - private class Reference { - public Reference(T t) { - reference = t; - } - - T reference; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (convertView == null) { - int type = getItemViewType(position); - convertView = createView(parent, type); - - convertView.setOnDragListener(new OnDragListener() { - @Override - @SuppressWarnings("unchecked") - //unavoidable generic type problems -> Reference - public boolean onDrag(View dstView, DragEvent event) { - Reference ref = (Reference) event.getLocalState(); - final View srcView = ref.reference; - - switch (event.getAction()) { - case DragEvent.ACTION_DRAG_ENTERED: - srcView.setVisibility(View.VISIBLE); - dstView.setVisibility(View.INVISIBLE); - - move(((Integer) srcView.getTag(R.id.reorder_key)), - ((Integer) dstView.getTag(R.id.reorder_key))); - ref.reference = dstView; - break; - - case DragEvent.ACTION_DRAG_ENDED: - srcView.post(new Runnable() { - @Override - public void run() { - srcView.setVisibility(View.VISIBLE); - } - }); - break; - } - - return true; - } - }); - - convertView.setOnLongClickListener(new OnLongClickListener() { - @Override - public boolean onLongClick(final View view) { - // Force a reset of any states - notifyDataSetChanged(); - - // Start the drag on the main loop to allow - // the above state reset to settle. - view.post(new Runnable() { - @Override - @SuppressWarnings("deprecation") - //startDrag() --> suppress deprecation because startDragAndDrop() requires minSdkVersion 24 - public void run() { - ClipData data = ClipData.newPlainText("", ""); - DragShadowBuilder sb = new View.DragShadowBuilder(view); - view.startDrag(data, sb, new Reference<>(view), 0); - } - }); - - return true; - } - }); - } - - convertView.setTag(R.id.reorder_key, position); - bindView(convertView, position); - return convertView; - } - - protected abstract void move(int fromPosition, int toPosition); - - protected abstract void bindView(View view, int position); - - protected abstract View createView(ViewGroup parent, int type); -} diff --git a/app/src/main/java/org/fedorahosted/freeotp/MainActivity.java b/app/src/main/java/org/fedorahosted/freeotp/MainActivity.java deleted file mode 100644 index 4f2444a8..00000000 --- a/app/src/main/java/org/fedorahosted/freeotp/MainActivity.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * FreeOTP - * - * Authors: Nathaniel McCallum - * Authors: Siemens AG - * - * Copyright (C) 2013 Nathaniel McCallum, Red Hat - * Copyright (C) 2017 Max Wittig, Siemens AG - * - * 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.widget.Toast; - -import org.fedorahosted.freeotp.add.ScanActivity; - -import android.app.Activity; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.IntentFilter; -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; - -public class MainActivity extends Activity implements OnMenuItemClickListener { - private TokenAdapter mTokenAdapter; - public static final String ACTION_IMAGE_SAVED = "org.fedorahosted.freeotp.ACTION_IMAGE_SAVED"; - private DataSetObserver mDataSetObserver; - private final int PERMISSIONS_REQUEST_CAMERA = 1; - private RefreshListBroadcastReceiver receiver; - - private class RefreshListBroadcastReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - mTokenAdapter.notifyDataSetChanged(); - } - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - onNewIntent(getIntent()); - setContentView(R.layout.main); - - mTokenAdapter = new TokenAdapter(this); - receiver = new RefreshListBroadcastReceiver(); - registerReceiver(receiver, new IntentFilter(ACTION_IMAGE_SAVED)); - ((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); - } - - @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); - unregisterReceiver(receiver); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.main, menu); - menu.findItem(R.id.action_scan).setVisible(ScanActivity.hasCamera(this)); - menu.findItem(R.id.action_scan).setOnMenuItemClickListener(this); - menu.findItem(R.id.action_about).setOnMenuItemClickListener(this); - return true; - } - - private void tryOpenCamera() { - if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[]{Manifest.permission.CAMERA}, PERMISSIONS_REQUEST_CAMERA); - } - else { - // permission is already granted - openCamera(); - } - } - - private void openCamera() { - startActivity(new Intent(this, ScanActivity.class)); - overridePendingTransition(R.anim.fadein, R.anim.fadeout); - } - - @Override - public boolean onMenuItemClick(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_scan: - tryOpenCamera(); - return true; - - case R.id.action_about: - startActivity(new Intent(this, AboutActivity.class)); - return true; - } - - return false; - } - - @Override - public void onRequestPermissionsResult(int requestCode, - String permissions[], int[] grantResults) { - switch (requestCode) { - case PERMISSIONS_REQUEST_CAMERA: { - if (grantResults.length > 0 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - openCamera(); - } else { - Toast.makeText(MainActivity.this, R.string.error_permission_camera_open, Toast.LENGTH_LONG).show(); - } - return; - } - } - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - - Uri uri = intent.getData(); - if (uri != null) { - try { - TokenPersistence.saveAsync(this, new Token(uri)); - } catch (Token.TokenUriInvalidException e) { - e.printStackTrace(); - } - } - - } -} diff --git a/app/src/main/java/org/fedorahosted/freeotp/ProgressCircle.java b/app/src/main/java/org/fedorahosted/freeotp/ProgressCircle.java deleted file mode 100644 index ed7bad62..00000000 --- a/app/src/main/java/org/fedorahosted/freeotp/ProgressCircle.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * FreeOTP - * - * Authors: Nathaniel McCallum - * - * 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. - */ - -package org.fedorahosted.freeotp; - -import android.content.Context; -import android.content.res.Resources.Theme; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Paint.Style; -import android.graphics.Rect; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.view.View; - -public class ProgressCircle extends View { - private Paint mPaint; - private RectF mRectF; - private Rect mRect; - private int mProgress; - private int mMax; - private boolean mHollow; - private float mPadding; - private float mStrokeWidth; - - public ProgressCircle(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - setup(context, attrs); - } - - public ProgressCircle(Context context, AttributeSet attrs) { - super(context, attrs); - setup(context, attrs); - } - - public ProgressCircle(Context context) { - super(context); - setup(context, null); - } - - private void setup(Context context, AttributeSet attrs) { - DisplayMetrics dm = getResources().getDisplayMetrics(); - mPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, dm); - mStrokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, dm); - - mRectF = new RectF(); - mRect = new Rect(); - - mPaint = new Paint(); - mPaint.setARGB(0x99, 0x33, 0x33, 0x33); - mPaint.setAntiAlias(true); - mPaint.setStrokeCap(Paint.Cap.BUTT); - - if (attrs != null) { - Theme t = context.getTheme(); - TypedArray a = t.obtainStyledAttributes(attrs, R.styleable.ProgressCircle, 0, 0); - - try { - setMax(a.getInteger(R.styleable.ProgressCircle_max, 100)); - setHollow(a.getBoolean(R.styleable.ProgressCircle_hollow, false)); - } finally { - a.recycle(); - } - } - } - - public void setMax(int max) { - this.mMax = max; - } - - public int getMax() { - return mMax; - } - - public void setHollow(boolean hollow) { - mHollow = hollow; - mPaint.setStyle(hollow ? Style.STROKE : Style.FILL); - mPaint.setStrokeWidth(hollow ? mStrokeWidth : 0); - } - - public boolean getHollow() { - return mHollow; - } - - public void setProgress(int progress) { - mProgress = progress; - - int percent = mProgress * 100 / getMax(); - if (percent > 25 || mProgress == 0) - mPaint.setARGB(0x99, 0x33, 0x33, 0x33); - else - mPaint.setARGB(0x99, 0xff, 0xe0 * percent / 25, 0x00); - - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - getDrawingRect(mRect); - - mRect.left += getPaddingLeft() + mPadding; - mRect.top += getPaddingTop() + mPadding; - mRect.right -= getPaddingRight() + mPadding; - mRect.bottom -= getPaddingBottom() + mPadding; - mRectF.set(mRect); - - canvas.drawArc(mRectF, -90, mProgress * 360 / getMax(), !mHollow, mPaint); - } -} diff --git a/app/src/main/java/org/fedorahosted/freeotp/Token.java b/app/src/main/java/org/fedorahosted/freeotp/Token.java deleted file mode 100644 index e6c8ac0a..00000000 --- a/app/src/main/java/org/fedorahosted/freeotp/Token.java +++ /dev/null @@ -1,346 +0,0 @@ -/* - * FreeOTP - * - * Authors: Nathaniel McCallum - * - * 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. - */ - -package org.fedorahosted.freeotp; - -import java.io.File; -import java.nio.ByteBuffer; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.Locale; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -import android.net.Uri; - -import com.google.android.apps.authenticator.Base32String; -import com.google.android.apps.authenticator.Base32String.DecodingException; - -public class Token { - public static class TokenUriInvalidException extends Exception { - private static final long serialVersionUID = -1108624734612362345L; - } - - public static enum TokenType { - HOTP, TOTP - } - - private static char[] STEAMCHARS = new char[] { - '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C', - 'D', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', - 'R', 'T', 'V', 'W', 'X', 'Y'}; - - private String issuerInt; - private String issuerExt; - private String issuerAlt; - private String label; - private String labelAlt; - private String image; - private String imageAlt; - private TokenType type; - private String algo; - private byte[] secret; - private int digits; - private long counter; - private int period; - - private Token(Uri uri, boolean internal) throws TokenUriInvalidException { - validateTokenURI(uri); - - String path = uri.getPath(); - // Strip the path of its leading '/' - path = path.replaceFirst("/",""); - - if (path.length() == 0) - throw new TokenUriInvalidException(); - - int i = path.indexOf(':'); - issuerExt = i < 0 ? "" : path.substring(0, i); - issuerInt = uri.getQueryParameter("issuer"); - label = path.substring(i >= 0 ? i + 1 : 0); - - algo = uri.getQueryParameter("algorithm"); - if (algo == null) - algo = "sha1"; - algo = algo.toUpperCase(Locale.US); - try { - Mac.getInstance("Hmac" + algo); - } catch (NoSuchAlgorithmException e1) { - throw new TokenUriInvalidException(); - } - - try { - String d = uri.getQueryParameter("digits"); - if (d == null) - d = "6"; - digits = Integer.parseInt(d); - if (!issuerExt.equals("Steam") && digits != 6 && digits != 8) - throw new TokenUriInvalidException(); - } catch (NumberFormatException e) { - throw new TokenUriInvalidException(); - } - - try { - String p = uri.getQueryParameter("period"); - if (p == null) - p = "30"; - period = Integer.parseInt(p); - period = (period > 0) ? period : 30; // Avoid divide-by-zero - } catch (NumberFormatException e) { - throw new TokenUriInvalidException(); - } - - if (type == TokenType.HOTP) { - try { - String c = uri.getQueryParameter("counter"); - if (c == null) - c = "0"; - counter = Long.parseLong(c); - } catch (NumberFormatException e) { - throw new TokenUriInvalidException(); - } - } - - try { - String s = uri.getQueryParameter("secret"); - secret = Base32String.decode(s); - } catch (DecodingException e) { - throw new TokenUriInvalidException(); - } catch (NullPointerException e) { - throw new TokenUriInvalidException(); - } - - image = uri.getQueryParameter("image"); - - if (internal) { - setIssuer(uri.getQueryParameter("issueralt")); - setLabel(uri.getQueryParameter("labelalt")); - } - } - - private void validateTokenURI(Uri uri) throws TokenUriInvalidException{ - if (uri == null) throw new TokenUriInvalidException(); - - if (uri.getScheme() == null || !uri.getScheme().equals("otpauth")){ - throw new TokenUriInvalidException(); - } - - if (uri.getAuthority() == null) throw new TokenUriInvalidException(); - - if (uri.getAuthority().equals("totp")) { - type = TokenType.TOTP; - } else if (uri.getAuthority().equals("hotp")) - type = TokenType.HOTP; - else { - throw new TokenUriInvalidException(); - } - - if (uri.getPath() == null) throw new TokenUriInvalidException(); - } - - private String getHOTP(long counter) { - // Encode counter in network byte order - ByteBuffer bb = ByteBuffer.allocate(8); - bb.putLong(counter); - - // Create digits divisor - int div = 1; - for (int i = digits; i > 0; i--) - div *= 10; - - // Create the HMAC - try { - Mac mac = Mac.getInstance("Hmac" + algo); - mac.init(new SecretKeySpec(secret, "Hmac" + algo)); - - // Do the hashing - byte[] digest = mac.doFinal(bb.array()); - - // Truncate - int binary; - int off = digest[digest.length - 1] & 0xf; - binary = (digest[off] & 0x7f) << 0x18; - binary |= (digest[off + 1] & 0xff) << 0x10; - binary |= (digest[off + 2] & 0xff) << 0x08; - binary |= (digest[off + 3] & 0xff); - - String hotp = ""; - if (issuerExt.equals("Steam")) { - for (int i = 0; i < digits; i++) { - hotp += STEAMCHARS[binary % STEAMCHARS.length]; - binary /= STEAMCHARS.length; - } - } else { - binary = binary % div; - - // Zero pad - hotp = Integer.toString(binary); - while (hotp.length() != digits) - hotp = "0" + hotp; - } - - return hotp; - } catch (InvalidKeyException e) { - e.printStackTrace(); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } - - return ""; - } - - public Token(String uri, boolean internal) throws TokenUriInvalidException { - this(Uri.parse(uri), internal); - } - - public Token(Uri uri) throws TokenUriInvalidException { - this(uri, false); - } - - public Token(String uri) throws TokenUriInvalidException { - this(Uri.parse(uri)); - } - - public String getID() { - String id; - if (issuerInt != null && !issuerInt.equals("")) - id = issuerInt + ":" + label; - else if (issuerExt != null && !issuerExt.equals("")) - id = issuerExt + ":" + label; - else - id = label; - - return id; - } - - // NOTE: This changes internal data. You MUST save the token immediately. - public void setIssuer(String issuer) { - issuerAlt = (issuer == null || issuer.equals(this.issuerExt)) ? null : issuer; - } - - public String getIssuer() { - if (issuerAlt != null) - return issuerAlt; - return issuerExt != null ? issuerExt : ""; - } - - // NOTE: This changes internal data. You MUST save the token immediately. - public void setLabel(String label) { - labelAlt = (label == null || label.equals(this.label)) ? null : label; - } - - public String getLabel() { - if (labelAlt != null) - return labelAlt; - return label != null ? label : ""; - } - - public int getDigits() { - return digits; - } - - // NOTE: This may change internal data. You MUST save the token immediately. - public TokenCode generateCodes() { - long cur = System.currentTimeMillis(); - - switch (type) { - case HOTP: - return new TokenCode(getHOTP(counter++), cur, cur + (period * 1000)); - - case TOTP: - long counter = cur / 1000 / period; - return new TokenCode(getHOTP(counter + 0), - (counter + 0) * period * 1000, - (counter + 1) * period * 1000, - new TokenCode(getHOTP(counter + 1), - (counter + 1) * period * 1000, - (counter + 2) * period * 1000)); - } - - return null; - } - - public TokenType getType() { - return type; - } - - public Uri toUri() { - String issuerLabel = !issuerExt.equals("") ? issuerExt + ":" + label : label; - - Uri.Builder builder = new Uri.Builder().scheme("otpauth").path(issuerLabel) - .appendQueryParameter("secret", Base32String.encode(secret)) - .appendQueryParameter("issuer", issuerInt == null ? issuerExt : issuerInt) - .appendQueryParameter("algorithm", algo) - .appendQueryParameter("digits", Integer.toString(digits)) - .appendQueryParameter("period", Integer.toString(period)); - - switch (type) { - case HOTP: - builder.authority("hotp"); - builder.appendQueryParameter("counter", Long.toString(counter + 1)); - break; - case TOTP: - builder.authority("totp"); - break; - } - - return builder.build(); - } - - @Override - public String toString() { - return toUri().toString(); - } - - /** - * delete image, which is attached to the token from storage - */ - public void deleteImage() { - Uri imageUri = getImage(); - if (imageUri != null) { - File image = new File(imageUri.getPath()); - if (image.exists()) - image.delete(); - } - } - - public void setImage(Uri image) { - //delete old token image, before assigning the new one - deleteImage(); - - imageAlt = null; - if (image == null) - return; - - if (this.image == null || !Uri.parse(this.image).equals(image)) - imageAlt = image.toString(); - } - - public Uri getImage() { - if (imageAlt != null) - return Uri.parse(imageAlt); - - if (image != null) - return Uri.parse(image); - - return null; - } -} diff --git a/app/src/main/java/org/fedorahosted/freeotp/TokenAdapter.java b/app/src/main/java/org/fedorahosted/freeotp/TokenAdapter.java deleted file mode 100644 index cdf1c7a4..00000000 --- a/app/src/main/java/org/fedorahosted/freeotp/TokenAdapter.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * FreeOTP - * - * Authors: Nathaniel McCallum - * - * 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. - */ - -package org.fedorahosted.freeotp; - -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.Intent; -import android.database.DataSetObserver; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.PopupMenu; -import android.widget.Toast; - -import org.fedorahosted.freeotp.edit.DeleteActivity; -import org.fedorahosted.freeotp.edit.EditActivity; - -import java.util.HashMap; -import java.util.Map; - -public class TokenAdapter extends BaseReorderableAdapter { - private final TokenPersistence mTokenPersistence; - private final LayoutInflater mLayoutInflater; - private final ClipboardManager mClipMan; - private final Map mTokenCodes; - - public TokenAdapter(Context ctx) { - mTokenPersistence = new TokenPersistence(ctx); - mLayoutInflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mClipMan = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE); - mTokenCodes = new HashMap<>(); - registerDataSetObserver(new DataSetObserver() { - @Override - public void onChanged() { - mTokenCodes.clear(); - } - - @Override - public void onInvalidated() { - mTokenCodes.clear(); - } - }); - } - - @Override - public int getCount() { - return mTokenPersistence.length(); - } - - @Override - public Token getItem(int position) { - return mTokenPersistence.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - protected void move(int fromPosition, int toPosition) { - mTokenPersistence.move(fromPosition, toPosition); - notifyDataSetChanged(); - } - - @Override - protected void bindView(View view, final int position) { - final Context ctx = view.getContext(); - TokenLayout tl = (TokenLayout) view; - Token token = getItem(position); - - tl.bind(token, R.menu.token, new PopupMenu.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - Intent i; - - switch (item.getItemId()) { - case R.id.action_edit: - i = new Intent(ctx, EditActivity.class); - i.putExtra(EditActivity.EXTRA_POSITION, position); - ctx.startActivity(i); - break; - - case R.id.action_delete: - i = new Intent(ctx, DeleteActivity.class); - i.putExtra(DeleteActivity.EXTRA_POSITION, position); - ctx.startActivity(i); - break; - } - - return true; - } - }); - - tl.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - TokenPersistence tp = new TokenPersistence(ctx); - - // Increment the token. - Token token = tp.get(position); - TokenCode codes = token.generateCodes(); - //save token. Image wasn't changed here, so just save it in sync - new TokenPersistence(ctx).save(token); - - // Copy code to clipboard. - mClipMan.setPrimaryClip(ClipData.newPlainText(null, codes.getCurrentCode())); - Toast.makeText(v.getContext().getApplicationContext(), - R.string.code_copied, - Toast.LENGTH_SHORT).show(); - - mTokenCodes.put(token.getID(), codes); - ((TokenLayout) v).start(token.getType(), codes, true); - } - }); - - TokenCode tc = mTokenCodes.get(token.getID()); - if (tc != null && tc.getCurrentCode() != null) - tl.start(token.getType(), tc, false); - } - - @Override - protected View createView(ViewGroup parent, int type) { - return mLayoutInflater.inflate(R.layout.token, parent, false); - } -} diff --git a/app/src/main/java/org/fedorahosted/freeotp/TokenCode.java b/app/src/main/java/org/fedorahosted/freeotp/TokenCode.java deleted file mode 100644 index 772e59b4..00000000 --- a/app/src/main/java/org/fedorahosted/freeotp/TokenCode.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * FreeOTP - * - * Authors: Nathaniel McCallum - * - * Copyright (C) 2014 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. - */ - -package org.fedorahosted.freeotp; - -public class TokenCode { - private final String mCode; - private final long mStart; - private final long mUntil; - private TokenCode mNext; - - public TokenCode(String code, long start, long until) { - mCode = code; - mStart = start; - mUntil = until; - } - - public TokenCode(TokenCode prev, String code, long start, long until) { - this(code, start, until); - prev.mNext = this; - } - - public TokenCode(String code, long start, long until, TokenCode next) { - this(code, start, until); - mNext = next; - } - - public String getCurrentCode() { - TokenCode active = getActive(System.currentTimeMillis()); - if (active == null) - return null; - return active.mCode; - } - - public int getTotalProgress() { - long cur = System.currentTimeMillis(); - long total = getLast().mUntil - mStart; - long state = total - (cur - mStart); - return (int) (state * 1000 / total); - } - - public int getCurrentProgress() { - long cur = System.currentTimeMillis(); - TokenCode active = getActive(cur); - if (active == null) - return 0; - - long total = active.mUntil - active.mStart; - long state = total - (cur - active.mStart); - return (int) (state * 1000 / total); - } - - private TokenCode getActive(long curTime) { - if (curTime >= mStart && curTime < mUntil) - return this; - - if (mNext == null) - return null; - - return this.mNext.getActive(curTime); - } - - private TokenCode getLast() { - if (mNext == null) - return this; - return this.mNext.getLast(); - } -} diff --git a/app/src/main/java/org/fedorahosted/freeotp/TokenLayout.java b/app/src/main/java/org/fedorahosted/freeotp/TokenLayout.java deleted file mode 100644 index 64751080..00000000 --- a/app/src/main/java/org/fedorahosted/freeotp/TokenLayout.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * FreeOTP - * - * Authors: Nathaniel McCallum - * - * 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. - */ - -package org.fedorahosted.freeotp; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.PopupMenu; -import android.widget.TextView; - -import com.squareup.picasso.Picasso; - -public class TokenLayout extends FrameLayout implements View.OnClickListener, Runnable { - private ProgressCircle mProgressInner; - private ProgressCircle mProgressOuter; - private ImageView mImage; - private TextView mCode; - private TextView mIssuer; - private TextView mLabel; - private ImageView mMenu; - private PopupMenu mPopupMenu; - - private TokenCode mCodes; - private Token.TokenType mType; - private String mPlaceholder; - private long mStartTime; - - public TokenLayout(Context context) { - super(context); - } - - public TokenLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public TokenLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - mProgressInner = findViewById(R.id.progressInner); - mProgressOuter = findViewById(R.id.progressOuter); - mImage = findViewById(R.id.image); - mCode = findViewById(R.id.code); - mIssuer = findViewById(R.id.issuer); - mLabel = findViewById(R.id.label); - mMenu = findViewById(R.id.menu); - - mPopupMenu = new PopupMenu(getContext(), mMenu); - mMenu.setOnClickListener(this); - } - - public void bind(Token token, int menu, PopupMenu.OnMenuItemClickListener micl) { - mCodes = null; - - // Setup menu. - mPopupMenu.getMenu().clear(); - mPopupMenu.getMenuInflater().inflate(menu, mPopupMenu.getMenu()); - mPopupMenu.setOnMenuItemClickListener(micl); - - // Cancel all active animations. - setEnabled(true); - removeCallbacks(this); - mImage.clearAnimation(); - mProgressInner.clearAnimation(); - mProgressOuter.clearAnimation(); - mProgressInner.setVisibility(View.GONE); - mProgressOuter.setVisibility(View.GONE); - - // Get the code placeholder. - char[] placeholder = new char[token.getDigits()]; - for (int i = 0; i < placeholder.length; i++) - placeholder[i] = '-'; - mPlaceholder = new String(placeholder); - - // Show the image. - Picasso.with(getContext()) - .load(token.getImage()) - .placeholder(R.mipmap.ic_freeotp_logo_foreground) - .fit() - .into(mImage); - - // Set the labels. - mLabel.setText(token.getLabel()); - mIssuer.setText(token.getIssuer()); - mCode.setText(mPlaceholder); - if (mIssuer.getText().length() == 0) { - mIssuer.setText(token.getLabel()); - mLabel.setVisibility(View.GONE); - } else { - mLabel.setVisibility(View.VISIBLE); - } - } - - private void animate(View view, int anim, boolean animate) { - Animation a = AnimationUtils.loadAnimation(view.getContext(), anim); - if (!animate) - a.setDuration(0); - view.startAnimation(a); - } - - public void start(Token.TokenType type, TokenCode codes, boolean animate) { - mCodes = codes; - mType = type; - - // Start animations. - mProgressInner.setVisibility(View.VISIBLE); - animate(mProgressInner, R.anim.fadein, animate); - animate(mImage, R.anim.token_image_fadeout, animate); - - // Handle type-specific UI. - switch (type) { - case HOTP: - setEnabled(false); - break; - case TOTP: - mProgressOuter.setVisibility(View.VISIBLE); - animate(mProgressOuter, R.anim.fadein, animate); - break; - } - - mStartTime = System.currentTimeMillis(); - post(this); - } - - @Override - public void onClick(View v) { - mPopupMenu.show(); - } - - @Override - public void run() { - // Get the current data - String code = mCodes == null ? null : mCodes.getCurrentCode(); - if (code != null) { - // Determine whether to enable/disable the view. - if (!isEnabled()) - setEnabled(System.currentTimeMillis() - mStartTime > 5000); - - // Update the fields - mCode.setText(code); - mProgressInner.setProgress(mCodes.getCurrentProgress()); - if (mType != Token.TokenType.HOTP) - mProgressOuter.setProgress(mCodes.getTotalProgress()); - - postDelayed(this, 100); - return; - } - - mCode.setText(mPlaceholder); - mProgressInner.setVisibility(View.GONE); - mProgressOuter.setVisibility(View.GONE); - animate(mImage, R.anim.token_image_fadein, true); - } -} diff --git a/app/src/main/java/org/fedorahosted/freeotp/TokenPersistence.java b/app/src/main/java/org/fedorahosted/freeotp/TokenPersistence.java deleted file mode 100644 index 253ac158..00000000 --- a/app/src/main/java/org/fedorahosted/freeotp/TokenPersistence.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * FreeOTP - * - * Authors: Nathaniel McCallum - * - * 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. - */ - -package org.fedorahosted.freeotp; - -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.graphics.Bitmap; -import android.net.Uri; -import android.os.AsyncTask; - -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; -import com.google.gson.reflect.TypeToken; -import com.squareup.picasso.Picasso; - -import org.fedorahosted.freeotp.Token.TokenUriInvalidException; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.lang.reflect.Type; -import java.util.LinkedList; -import java.util.List; -import java.util.UUID; - -public class TokenPersistence { - private static final String NAME = "tokens"; - private static final String ORDER = "tokenOrder"; - private final SharedPreferences prefs; - private final Gson gson; - - private List getTokenOrder() { - Type type = new TypeToken>(){}.getType(); - String str = prefs.getString(ORDER, "[]"); - List order = gson.fromJson(str, type); - return order == null ? new LinkedList() : order; - } - - private SharedPreferences.Editor setTokenOrder(List order) { - return prefs.edit().putString(ORDER, gson.toJson(order)); - } - - public TokenPersistence(Context ctx) { - prefs = ctx.getApplicationContext().getSharedPreferences(NAME, Context.MODE_PRIVATE); - gson = new Gson(); - } - - public int length() { - return getTokenOrder().size(); - } - - public boolean tokenExists(Token token) { - return prefs.contains(token.getID()); - } - - public Token get(int position) { - String key = getTokenOrder().get(position); - String str = prefs.getString(key, null); - - try { - return gson.fromJson(str, Token.class); - } catch (JsonSyntaxException jse) { - // Backwards compatibility for URL-based persistence. - try { - return new Token(str, true); - } catch (TokenUriInvalidException tuie) { - tuie.printStackTrace(); - } - } - - return null; - } - - public void save(Token token) { - String key = token.getID(); - - //if token exists, just update it - if (prefs.contains(key)) { - prefs.edit().putString(token.getID(), gson.toJson(token)).apply(); - return; - } - - List order = getTokenOrder(); - order.add(0, key); - setTokenOrder(order).putString(key, gson.toJson(token)).apply(); - } - - public void move(int fromPosition, int toPosition) { - if (fromPosition == toPosition) - return; - - List order = getTokenOrder(); - if (fromPosition < 0 || fromPosition > order.size()) - return; - if (toPosition < 0 || toPosition > order.size()) - return; - - order.add(toPosition, order.remove(fromPosition)); - setTokenOrder(order).apply(); - } - - public void delete(int position) { - List order = getTokenOrder(); - String key = order.remove(position); - setTokenOrder(order).remove(key).apply(); - } - - /** - * Save token async, because Image needs to be downloaded/copied to storage - * @param context Application Context - * @param token Token (with Image, Image will be saved by the async task) - */ - public static void saveAsync(Context context, final Token token) { - File outFile = null; - if(token.getImage() != null) - outFile = new File(context.getFilesDir(), "img_" + UUID.randomUUID().toString() + ".png"); - new SaveTokenTask().execute(new TaskParams(token, outFile, context)); - } - - /** - * Data class for SaveTokenTask - */ - private static class ReturnParams { - private final Token token; - private final Context context; - - public ReturnParams(Token token, Context context) { - this.token = token; - this.context = context; - } - - public Token getToken() { - return token; - } - - public Context getContext() { - return context; - } - } - - /** - * Data class for SaveTokenTask - */ - private static class TaskParams { - private final File outFile; - private final Context mContext; - private final Token token; - - public TaskParams(Token token, File outFile, Context mContext) { - this.token = token; - this.outFile = outFile; - this.mContext = mContext; - } - - public Context getContext() { - return mContext; - } - - public Token getToken() { - return token; - } - - public File getOutFile() { - return outFile; - } - } - - /** - * Downloads/copies images to FreeOTP storage - * Saves token in PostExecute - */ - private static class SaveTokenTask extends AsyncTask { - protected ReturnParams doInBackground(TaskParams... params) { - final TaskParams taskParams = params[0]; - if(taskParams.getToken().getImage() != null) { - try { - Bitmap bitmap = Picasso.with(taskParams.getContext()) - .load(taskParams.getToken() - .getImage()) - .resize(200, 200) // it's just an icon - .onlyScaleDown() //resize image, if bigger than 200x200 - .get(); - File outFile = taskParams.getOutFile(); - //saveAsync image - FileOutputStream out = new FileOutputStream(outFile); - bitmap.compress(Bitmap.CompressFormat.PNG, 50, out); - out.close(); - taskParams.getToken().setImage(Uri.fromFile(outFile)); - } catch (IOException e) { - e.printStackTrace(); - //set image to null to prevent internet link in image, in case image - //was scanned, when no connection existed - taskParams.getToken().setImage(null); - } - } - return new ReturnParams(taskParams.getToken(), taskParams.getContext()); - } - - @Override - protected void onPostExecute(ReturnParams returnParams) { - super.onPostExecute(returnParams); - //we downloaded the image, now save it normally - new TokenPersistence(returnParams.getContext()).save(returnParams.getToken()); - //refresh TokenAdapter - returnParams.context.sendBroadcast(new Intent(MainActivity.ACTION_IMAGE_SAVED)); - } - } -} diff --git a/app/src/main/java/org/fedorahosted/freeotp/add/ScanActivity.java b/app/src/main/java/org/fedorahosted/freeotp/add/ScanActivity.java deleted file mode 100644 index e1b37565..00000000 --- a/app/src/main/java/org/fedorahosted/freeotp/add/ScanActivity.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * FreeOTP - * - * Authors: Nathaniel McCallum - * Authors: Siemens AG - * - * Copyright (C) 2013 Nathaniel McCallum, Red Hat - * Copyright (C) 2017 Max Wittig, Siemens AG - * - * 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.add; - -import org.fedorahosted.freeotp.R; -import org.fedorahosted.freeotp.Token; -import org.fedorahosted.freeotp.TokenPersistence; - -import android.app.Activity; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.view.View; -import android.widget.ImageView; -import com.squareup.picasso.Callback; -import com.squareup.picasso.Picasso; -import io.fotoapparat.Fotoapparat; -import io.fotoapparat.parameter.ScaleType; -import io.fotoapparat.parameter.selector.FocusModeSelectors; -import io.fotoapparat.view.CameraView; -import static io.fotoapparat.parameter.selector.FocusModeSelectors.autoFocus; -import static io.fotoapparat.parameter.selector.FocusModeSelectors.fixed; -import static io.fotoapparat.parameter.selector.LensPositionSelectors.back; -import static io.fotoapparat.parameter.selector.Selectors.firstAvailable; -import static io.fotoapparat.parameter.selector.SizeSelectors.biggestSize; - -public class ScanActivity extends Activity { - private Fotoapparat fotoapparat; - private static ScanBroadcastReceiver receiver; - - public class ScanBroadcastReceiver extends BroadcastReceiver { - public static final String ACTION = "org.fedorahosted.freeotp.ACTION_CODE_SCANNED"; - - @Override - public void onReceive(Context context, Intent intent) { - String text = intent.getStringExtra("scanResult"); - addTokenAndFinish(text); - } - } - - public static boolean hasCamera(Context context) { - PackageManager pm = context.getPackageManager(); - return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA); - } - - private void addTokenAndFinish(String text) { - Token token = null; - try { - token = new Token(text); - } catch (Token.TokenUriInvalidException e) { - e.printStackTrace(); - } - - //do not receive any more broadcasts - this.unregisterReceiver(receiver); - - //check if token already exists - if (new TokenPersistence(ScanActivity.this).tokenExists(token)) { - finish(); - return; - } - - TokenPersistence.saveAsync(ScanActivity.this, token); - if (token == null || token.getImage() == null) { - finish(); - return; - } - - final ImageView image = (ImageView) findViewById(R.id.image); - Picasso.with(ScanActivity.this) - .load(token.getImage()) - .placeholder(R.drawable.scan) - .into(image, new Callback() { - @Override - public void onSuccess() { - findViewById(R.id.progress).setVisibility(View.INVISIBLE); - image.setAlpha(0.9f); - image.postDelayed(new Runnable() { - @Override - public void run() { - finish(); - } - }, 2000); - } - - @Override - public void onError() { - finish(); - } - }); - } - - @Override - public void onDestroy() { - super.onDestroy(); - try { - this.unregisterReceiver(receiver); - } - catch (IllegalArgumentException e) { - // catch exception, when trying to unregister receiver again - // there seems to be no way to check, if receiver if registered - } - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - receiver = new ScanBroadcastReceiver(); - this.registerReceiver(receiver, new IntentFilter(ScanBroadcastReceiver.ACTION)); - setContentView(R.layout.scan); - CameraView cameraView = findViewById(R.id.camera_view); - - fotoapparat = Fotoapparat - .with(this) - .into(cameraView) - .previewScaleType(ScaleType.CENTER_CROP) - .photoSize(biggestSize()) - .lensPosition(back()) - .focusMode(firstAvailable( - FocusModeSelectors.continuousFocus(), - autoFocus(), - fixed() - )) - .frameProcessor(new ScanFrameProcessor(this)) - .build(); - } - - @Override - protected void onStart() { - super.onStart(); - fotoapparat.start(); - } - - @Override - protected void onStop() { - super.onStop(); - fotoapparat.stop(); - } -} diff --git a/app/src/main/java/org/fedorahosted/freeotp/add/ScanFrameProcessor.java b/app/src/main/java/org/fedorahosted/freeotp/add/ScanFrameProcessor.java deleted file mode 100644 index 13b4cd70..00000000 --- a/app/src/main/java/org/fedorahosted/freeotp/add/ScanFrameProcessor.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * FreeOTP - * - * Authors: Nathaniel McCallum - * Authors: Siemens AG - * - * Copyright (C) 2013 Nathaniel McCallum, Red Hat - * Copyright (C) 2017 Max Wittig, Siemens AG - * - * 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.add; - -import android.content.Context; -import android.content.Intent; -import android.os.Handler; -import android.os.Looper; -import com.google.zxing.*; -import com.google.zxing.common.HybridBinarizer; -import com.google.zxing.qrcode.QRCodeReader; -import io.fotoapparat.preview.Frame; -import io.fotoapparat.preview.FrameProcessor; - -public class ScanFrameProcessor implements FrameProcessor { - - private static Handler MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper()); - private Reader reader; - private Context scanActivityContext; - - public ScanFrameProcessor(Context context) { - scanActivityContext = context; - } - - @Override - public void processFrame(final Frame frame) { - MAIN_THREAD_HANDLER.post(new Runnable() { - @Override - public void run() { - try { - reader = new QRCodeReader(); - LuminanceSource ls = new PlanarYUVLuminanceSource( - frame.image, frame.size.width, frame.size.height, - 0, 0, frame.size.width, frame.size.height, false); - Result r = reader.decode(new BinaryBitmap(new HybridBinarizer(ls))); - sendTextToActivity(r.getText()); - } - catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - - private void sendTextToActivity(String text) { - Intent intent = new Intent(); - intent.setAction(ScanActivity.ScanBroadcastReceiver.ACTION); - intent.putExtra("scanResult", text); - scanActivityContext.sendBroadcast(intent); - } -} diff --git a/app/src/main/java/org/fedorahosted/freeotp/edit/BaseActivity.java b/app/src/main/java/org/fedorahosted/freeotp/edit/BaseActivity.java deleted file mode 100644 index 6f635bdf..00000000 --- a/app/src/main/java/org/fedorahosted/freeotp/edit/BaseActivity.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.fedorahosted.freeotp.edit; - -import android.app.Activity; -import android.os.Bundle; -import org.fedorahosted.freeotp.BuildConfig; - -public abstract class BaseActivity extends Activity { - public static final String EXTRA_POSITION = "position"; - private int mPosition; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Get the position of the token. This MUST exist. - mPosition = getIntent().getIntExtra(EXTRA_POSITION, -1); - if(BuildConfig.DEBUG && mPosition < 0) - throw new RuntimeException("Could not create BaseActivity"); - } - - protected int getPosition() { - return mPosition; - } -} diff --git a/app/src/main/java/org/fedorahosted/freeotp/edit/DeleteActivity.java b/app/src/main/java/org/fedorahosted/freeotp/edit/DeleteActivity.java deleted file mode 100644 index 51403cd3..00000000 --- a/app/src/main/java/org/fedorahosted/freeotp/edit/DeleteActivity.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.fedorahosted.freeotp.edit; - -import org.fedorahosted.freeotp.R; -import org.fedorahosted.freeotp.Token; -import org.fedorahosted.freeotp.TokenPersistence; - -import android.os.Bundle; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import com.squareup.picasso.Picasso; - -public class DeleteActivity extends BaseActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.delete); - - final Token token = new TokenPersistence(this).get(getPosition()); - ((TextView) findViewById(R.id.issuer)).setText(token.getIssuer()); - ((TextView) findViewById(R.id.label)).setText(token.getLabel()); - Picasso.with(this) - .load(token.getImage()) - .placeholder(R.mipmap.ic_freeotp_logo_foreground) - .into((ImageView) findViewById(R.id.image)); - - findViewById(R.id.cancel).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - finish(); - } - }); - - findViewById(R.id.delete).setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - //delete the image that was copied to storage, before deleting the token - token.deleteImage(); - new TokenPersistence(DeleteActivity.this).delete(getPosition()); - finish(); - } - }); - } -} diff --git a/app/src/main/java/org/fedorahosted/freeotp/edit/EditActivity.java b/app/src/main/java/org/fedorahosted/freeotp/edit/EditActivity.java deleted file mode 100644 index 4ecb5622..00000000 --- a/app/src/main/java/org/fedorahosted/freeotp/edit/EditActivity.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * FreeOTP - * - * Authors: Nathaniel McCallum - * - * Copyright (C) 2014 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. - */ - -package org.fedorahosted.freeotp.edit; - -import android.widget.Toast; - -import org.fedorahosted.freeotp.R; -import org.fedorahosted.freeotp.Token; -import org.fedorahosted.freeotp.TokenPersistence; - -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.text.Editable; -import android.text.TextWatcher; -import android.view.View; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageButton; - -import com.squareup.picasso.Picasso; - -public class EditActivity extends BaseActivity implements TextWatcher, View.OnClickListener { - private EditText mIssuer; - private EditText mLabel; - private ImageButton mImage; - private Button mRestore; - private Button mSave; - - private String mIssuerCurrent; - private String mIssuerDefault; - private String mLabelCurrent; - private String mLabelDefault; - private Uri mImageCurrent; - private Uri mImageDefault; - private Uri mImageDisplay; - private Token token; - private final int REQUEST_IMAGE_OPEN = 1; - - private void showImage(Uri uri) { - mImageDisplay = uri; - onTextChanged(null, 0, 0, 0); - Picasso.with(this) - .load(uri) - .placeholder(R.mipmap.ic_freeotp_logo_foreground) - .into(mImage); - } - - private boolean imageIs(Uri uri) { - if (uri == null) - return mImageDisplay == null; - - return uri.equals(mImageDisplay); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.edit); - - // Get token values. - token = new TokenPersistence(this).get(getPosition()); - mIssuerCurrent = token.getIssuer(); - mLabelCurrent = token.getLabel(); - mImageCurrent = token.getImage(); - mIssuerDefault = token.getIssuer(); - mLabelDefault = token.getLabel(); - mImageDefault = token.getImage(); - - // Get references to widgets. - mIssuer = findViewById(R.id.issuer); - mLabel = findViewById(R.id.label); - mImage = findViewById(R.id.image); - mRestore = findViewById(R.id.restore); - mSave = findViewById(R.id.save); - - // Setup text changed listeners. - mIssuer.addTextChangedListener(this); - mLabel.addTextChangedListener(this); - - // Setup click callbacks. - findViewById(R.id.cancel).setOnClickListener(this); - findViewById(R.id.save).setOnClickListener(this); - findViewById(R.id.restore).setOnClickListener(this); - mImage.setOnClickListener(this); - - // Setup initial state. - showImage(mImageCurrent); - mLabel.setText(mLabelCurrent); - mIssuer.setText(mIssuerCurrent); - mIssuer.setSelection(mIssuer.getText().length()); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - if (resultCode == RESULT_OK) { - if (requestCode == REQUEST_IMAGE_OPEN) { - //mImageDisplay is set in showImage - showImage(data.getData()); - token.setImage(mImageDisplay); - } - else { - Toast.makeText(EditActivity.this, R.string.error_image_open, Toast.LENGTH_LONG).show(); - } - } - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - String label = mLabel.getText().toString(); - String issuer = mIssuer.getText().toString(); - mSave.setEnabled(!label.equals(mLabelCurrent) || !issuer.equals(mIssuerCurrent) || !imageIs(mImageCurrent)); - mRestore.setEnabled(!label.equals(mLabelDefault) || !issuer.equals(mIssuerDefault) || !imageIs(mImageDefault)); - } - - @Override - public void afterTextChanged(Editable s) { - - } - - @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.image: - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - intent.setType("image/*"); - intent.addCategory(Intent.CATEGORY_OPENABLE); - startActivityForResult(intent, REQUEST_IMAGE_OPEN); - break; - - case R.id.restore: - mLabel.setText(mLabelDefault); - mIssuer.setText(mIssuerDefault); - mIssuer.setSelection(mIssuer.getText().length()); - showImage(mImageDefault); - break; - - case R.id.save: - TokenPersistence tp = new TokenPersistence(this); - Token token = tp.get(getPosition()); - token.setIssuer(mIssuer.getText().toString()); - token.setLabel(mLabel.getText().toString()); - token.setImage(mImageDisplay); - TokenPersistence.saveAsync(this, token); - - case R.id.cancel: - finish(); - break; - } - } -} diff --git a/app/src/main/res/anim/fadein.xml b/app/src/main/res/anim/fadein.xml deleted file mode 100644 index df8e48b1..00000000 --- a/app/src/main/res/anim/fadein.xml +++ /dev/null @@ -1,8 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/anim/fadeout.xml b/app/src/main/res/anim/fadeout.xml deleted file mode 100644 index b04fa183..00000000 --- a/app/src/main/res/anim/fadeout.xml +++ /dev/null @@ -1,8 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/anim/token_image_fadein.xml b/app/src/main/res/anim/token_image_fadein.xml deleted file mode 100644 index 7e0b4f58..00000000 --- a/app/src/main/res/anim/token_image_fadein.xml +++ /dev/null @@ -1,8 +0,0 @@ - - diff --git a/app/src/main/res/anim/token_image_fadeout.xml b/app/src/main/res/anim/token_image_fadeout.xml deleted file mode 100644 index 2ebbc41d..00000000 --- a/app/src/main/res/anim/token_image_fadeout.xml +++ /dev/null @@ -1,8 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/color/menu.xml b/app/src/main/res/color/menu.xml deleted file mode 100644 index cefc6c32..00000000 --- a/app/src/main/res/color/menu.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/ic_action_edit.png b/app/src/main/res/drawable-hdpi/ic_action_edit.png deleted file mode 100644 index 5f7c6eff..00000000 Binary files a/app/src/main/res/drawable-hdpi/ic_action_edit.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index 0112f764..00000000 Binary files a/app/src/main/res/drawable-hdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/qrcode.png b/app/src/main/res/drawable-hdpi/qrcode.png deleted file mode 100644 index e5806e64..00000000 Binary files a/app/src/main/res/drawable-hdpi/qrcode.png and /dev/null differ diff --git a/app/src/main/res/drawable-ldpi/ic_launcher.png b/app/src/main/res/drawable-ldpi/ic_launcher.png deleted file mode 100644 index 34bf9dc9..00000000 Binary files a/app/src/main/res/drawable-ldpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_edit.png b/app/src/main/res/drawable-mdpi/ic_action_edit.png deleted file mode 100644 index 650b4d89..00000000 Binary files a/app/src/main/res/drawable-mdpi/ic_action_edit.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_launcher.png b/app/src/main/res/drawable-mdpi/ic_launcher.png deleted file mode 100644 index 6fd3f45e..00000000 Binary files a/app/src/main/res/drawable-mdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/qrcode.png b/app/src/main/res/drawable-mdpi/qrcode.png deleted file mode 100644 index b4d964d9..00000000 Binary files a/app/src/main/res/drawable-mdpi/qrcode.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_edit.png b/app/src/main/res/drawable-xhdpi/ic_action_edit.png deleted file mode 100644 index 8ab436d8..00000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_action_edit.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher.png b/app/src/main/res/drawable-xhdpi/ic_launcher.png deleted file mode 100644 index 14cc3905..00000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/qrcode.png b/app/src/main/res/drawable-xhdpi/qrcode.png deleted file mode 100644 index b9a691c3..00000000 Binary files a/app/src/main/res/drawable-xhdpi/qrcode.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_edit.png b/app/src/main/res/drawable-xxhdpi/ic_action_edit.png deleted file mode 100644 index f2b2078b..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_edit.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/logo.png b/app/src/main/res/drawable-xxhdpi/logo.png deleted file mode 100644 index 8939e47b..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/logo.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/qrcode.png b/app/src/main/res/drawable-xxhdpi/qrcode.png deleted file mode 100644 index 1f3b6263..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/qrcode.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_freeotp.xml b/app/src/main/res/drawable/ic_freeotp.xml deleted file mode 100644 index 03c14358..00000000 --- a/app/src/main/res/drawable/ic_freeotp.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_freeotp_logo_background.xml b/app/src/main/res/drawable/ic_freeotp_logo_background.xml deleted file mode 100644 index 01f0af0a..00000000 --- a/app/src/main/res/drawable/ic_freeotp_logo_background.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/menu.xml b/app/src/main/res/drawable/menu.xml deleted file mode 100644 index d66b1090..00000000 --- a/app/src/main/res/drawable/menu.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/scan.xml b/app/src/main/res/drawable/scan.xml deleted file mode 100644 index ad7b1852..00000000 --- a/app/src/main/res/drawable/scan.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/about.xml b/app/src/main/res/layout/about.xml deleted file mode 100644 index a3691ee5..00000000 --- a/app/src/main/res/layout/about.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/delete.xml b/app/src/main/res/layout/delete.xml deleted file mode 100644 index a8c51ae0..00000000 --- a/app/src/main/res/layout/delete.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - -