diff --git a/app/src/main/java/me/impy/aegis/crypto/KeyInfo.java b/app/src/main/java/me/impy/aegis/crypto/KeyInfo.java index 8b4d8408..5d6f9231 100644 --- a/app/src/main/java/me/impy/aegis/crypto/KeyInfo.java +++ b/app/src/main/java/me/impy/aegis/crypto/KeyInfo.java @@ -42,7 +42,11 @@ public class KeyInfo implements Serializable { } public long getMillisTillNextRotation() { - long p = _period * 1000; + return KeyInfo.getMillisTillNextRotation(_period); + } + + public static long getMillisTillNextRotation(int period) { + long p = period * 1000; return p - (System.currentTimeMillis() % p); } diff --git a/app/src/main/java/me/impy/aegis/ui/MainActivity.java b/app/src/main/java/me/impy/aegis/ui/MainActivity.java index f0789c62..4c603dc0 100644 --- a/app/src/main/java/me/impy/aegis/ui/MainActivity.java +++ b/app/src/main/java/me/impy/aegis/ui/MainActivity.java @@ -1,22 +1,17 @@ package me.impy.aegis.ui; import android.Manifest; -import android.animation.ObjectAnimator; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; -import android.graphics.PorterDuff; import android.graphics.Rect; -import android.os.Handler; import android.support.design.widget.BottomSheetDialog; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; -import android.view.animation.LinearInterpolator; import android.widget.LinearLayout; -import android.widget.ProgressBar; import android.widget.Toast; import com.getbase.floatingactionbutton.FloatingActionsMenu; @@ -50,12 +45,9 @@ public class MainActivity extends AegisActivity implements KeyProfileView.Listen private AegisApplication _app; private DatabaseManager _db; private KeyProfileView _keyProfileView; - private ProgressBar _progressBar; private Menu _menu; private FloatingActionsMenu _fabMenu; - private Handler _uiHandler; - private boolean _running = false; @Override protected void onCreate(Bundle savedInstanceState) { @@ -63,8 +55,6 @@ public class MainActivity extends AegisActivity implements KeyProfileView.Listen _app = (AegisApplication) getApplication(); _db = _app.getDatabaseManager(); - _uiHandler = new Handler(); - // set up the main view setContentView(R.layout.activity_main); @@ -73,10 +63,6 @@ public class MainActivity extends AegisActivity implements KeyProfileView.Listen _keyProfileView.setListener(this); _keyProfileView.setShowIssuer(getPreferences().isIssuerVisible()); - _progressBar = findViewById(R.id.progressBar); - int primaryColorId = getResources().getColor(R.color.colorPrimary); - _progressBar.getProgressDrawable().setColorFilter(primaryColorId, PorterDuff.Mode.SRC_IN); - // set up the floating action button _fabMenu = findViewById(R.id.fab); findViewById(R.id.fab_enter).setOnClickListener(view -> { @@ -431,52 +417,6 @@ public class MainActivity extends AegisActivity implements KeyProfileView.Listen for (DatabaseEntry entry : _db.getKeys()) { _keyProfileView.addKey(new KeyProfile(entry)); } - - if(_keyProfileView.allSamePeriod()) - { - startRefreshLoop(); - } - } - - public void refreshCode() - { - _keyProfileView.refresh(); - KeyProfile keyProfile = _keyProfileView.getKeyProfile(0); - - // reset the progress bar - int maxProgress = _progressBar.getMax(); - _progressBar.setProgress(maxProgress); - - // calculate the progress the bar should start at - long millisTillRotation = keyProfile.getEntry().getInfo().getMillisTillNextRotation(); - long period = keyProfile.getEntry().getInfo().getPeriod() * maxProgress; - int currentProgress = maxProgress - (int) ((((double) period - millisTillRotation) / period) * maxProgress); - - // start progress animation - ObjectAnimator animation = ObjectAnimator.ofInt(_progressBar, "progress", currentProgress, 0); - animation.setDuration(millisTillRotation); - animation.setInterpolator(new LinearInterpolator()); - animation.start(); - } - - public void startRefreshLoop() { - if (_running) { - return; - } - _running = true; - - KeyProfile keyProfile = _keyProfileView.getKeyProfile(0); - - refreshCode(); - _uiHandler.postDelayed(new Runnable() { - @Override - public void run() { - if (_running) { - refreshCode(); - _uiHandler.postDelayed(this, keyProfile.getEntry().getInfo().getMillisTillNextRotation()); - } - } - }, keyProfile.getEntry().getInfo().getMillisTillNextRotation()); } private void updateLockIcon() { diff --git a/app/src/main/java/me/impy/aegis/ui/views/KeyProfileAdapter.java b/app/src/main/java/me/impy/aegis/ui/views/KeyProfileAdapter.java index 0a3ecb20..cfa18e7d 100644 --- a/app/src/main/java/me/impy/aegis/ui/views/KeyProfileAdapter.java +++ b/app/src/main/java/me/impy/aegis/ui/views/KeyProfileAdapter.java @@ -49,10 +49,6 @@ public class KeyProfileAdapter extends RecyclerView.Adapter im notifyDataSetChanged(); } - public ArrayList getKeys() { - return _keyProfiles; - } - public void replaceKey(KeyProfile newProfile) { KeyProfile oldProfile = getKeyByUUID(newProfile.getEntry().getUUID()); int position = _keyProfiles.indexOf(oldProfile); @@ -60,13 +56,6 @@ public class KeyProfileAdapter extends RecyclerView.Adapter im notifyItemChanged(position); } - public void refresh() { - for (KeyProfile profile : _keyProfiles) { - profile.refreshCode(); - } - notifyDataSetChanged(); - } - private KeyProfile getKeyByUUID(UUID uuid) { for (KeyProfile profile : _keyProfiles) { if (profile.getEntry().getUUID().equals(uuid)) { @@ -104,17 +93,16 @@ public class KeyProfileAdapter extends RecyclerView.Adapter im @Override public void onViewRecycled(KeyProfileHolder holder) { - holder.setData(null, _showIssuer); + holder.stopRefreshLoop(); super.onViewRecycled(holder); } @Override public void onBindViewHolder(final KeyProfileHolder holder, int position) { + boolean uniform = isPeriodUniform(); final KeyProfile profile = _keyProfiles.get(position); - holder.setData(profile, _showIssuer); - - if(!allSamePeriod()) - { + holder.setData(profile, _showIssuer, !uniform); + if (!uniform) { holder.startRefreshLoop(); } @@ -134,19 +122,23 @@ public class KeyProfileAdapter extends RecyclerView.Adapter im }); } - public boolean allSamePeriod() - { - ArrayList profiles = getKeys(); - int period = profiles.get(0).getEntry().getInfo().getPeriod(); + public int getUniformPeriod() { + if (_keyProfiles.isEmpty()) { + return -1; + } - for (KeyProfile profile : profiles) { - if(period != profile.getEntry().getInfo().getPeriod()) - { - return false; + int period = _keyProfiles.get(0).getEntry().getInfo().getPeriod(); + for (KeyProfile profile : _keyProfiles) { + if (period != profile.getEntry().getInfo().getPeriod()) { + return -1; } } - return true; + return period; + } + + public boolean isPeriodUniform() { + return getUniformPeriod() != -1; } @Override diff --git a/app/src/main/java/me/impy/aegis/ui/views/KeyProfileHolder.java b/app/src/main/java/me/impy/aegis/ui/views/KeyProfileHolder.java index 6028b59d..91753136 100644 --- a/app/src/main/java/me/impy/aegis/ui/views/KeyProfileHolder.java +++ b/app/src/main/java/me/impy/aegis/ui/views/KeyProfileHolder.java @@ -1,14 +1,10 @@ package me.impy.aegis.ui.views; -import android.animation.ObjectAnimator; -import android.graphics.Color; import android.graphics.PorterDuff; import android.os.Handler; import android.support.v7.widget.RecyclerView; import android.view.View; -import android.view.animation.LinearInterpolator; import android.widget.ImageView; -import android.widget.ProgressBar; import android.widget.TextView; import com.amulyakhare.textdrawable.TextDrawable; @@ -21,7 +17,8 @@ public class KeyProfileHolder extends RecyclerView.ViewHolder { private TextView _profileIssuer; private ImageView _profileDrawable; private KeyProfile _profile; - private ProgressBar _progressBar; + + private PeriodProgressBar _progressBar; private Handler _uiHandler; private boolean _running = false; @@ -39,16 +36,15 @@ public class KeyProfileHolder extends RecyclerView.ViewHolder { _progressBar.getProgressDrawable().setColorFilter(primaryColorId, PorterDuff.Mode.SRC_IN); } - public void setData(KeyProfile profile, boolean showIssuer) { - if (profile == null) { - _profile = null; - _running = false; - return; - } + public void setData(KeyProfile profile, boolean showIssuer, boolean showProgress) { _profile = profile; + _progressBar.setVisibility(showProgress ? View.VISIBLE : View.INVISIBLE); + if (showProgress) { + _progressBar.setPeriod(profile.getEntry().getInfo().getPeriod()); + } + _profileName.setText(profile.getEntry().getName()); - _profileCode.setText(profile.getCode()); _profileIssuer.setText(""); if (showIssuer) { _profileIssuer.setText(" - " + profile.getEntry().getInfo().getIssuer()); @@ -56,6 +52,8 @@ public class KeyProfileHolder extends RecyclerView.ViewHolder { TextDrawable drawable = profile.getDrawable(); _profileDrawable.setImageDrawable(drawable); + + refreshCode(); } public void startRefreshLoop() { @@ -64,35 +62,29 @@ public class KeyProfileHolder extends RecyclerView.ViewHolder { } _running = true; - refreshCode(); + refresh(); _uiHandler.postDelayed(new Runnable() { @Override public void run() { if (_running) { - refreshCode(); + refresh(); _uiHandler.postDelayed(this, _profile.getEntry().getInfo().getMillisTillNextRotation()); } } }, _profile.getEntry().getInfo().getMillisTillNextRotation()); } - public void refreshCode() { + public void stopRefreshLoop() { + _running = false; + } + + private void refresh() { + refreshCode(); + _progressBar.refresh(); + } + + private void refreshCode() { String otp = _profile.refreshCode(); - - // reset the progress bar - int maxProgress = _progressBar.getMax(); - _progressBar.setProgress(maxProgress); _profileCode.setText(otp.substring(0, otp.length() / 2) + " " + otp.substring(otp.length() / 2)); - - // calculate the progress the bar should start at - long millisTillRotation = _profile.getEntry().getInfo().getMillisTillNextRotation(); - long period = _profile.getEntry().getInfo().getPeriod() * maxProgress; - int currentProgress = maxProgress - (int) ((((double) period - millisTillRotation) / period) * maxProgress); - - // start progress animation - ObjectAnimator animation = ObjectAnimator.ofInt(_progressBar, "progress", currentProgress, 0); - animation.setDuration(millisTillRotation); - animation.setInterpolator(new LinearInterpolator()); - animation.start(); } } diff --git a/app/src/main/java/me/impy/aegis/ui/views/KeyProfileView.java b/app/src/main/java/me/impy/aegis/ui/views/KeyProfileView.java index ce28f703..a0b0920a 100644 --- a/app/src/main/java/me/impy/aegis/ui/views/KeyProfileView.java +++ b/app/src/main/java/me/impy/aegis/ui/views/KeyProfileView.java @@ -1,6 +1,8 @@ package me.impy.aegis.ui.views; +import android.graphics.PorterDuff; import android.os.Bundle; +import android.os.Handler; import android.support.v4.app.Fragment; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -9,10 +11,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import java.lang.reflect.Array; -import java.util.ArrayList; - import me.impy.aegis.R; +import me.impy.aegis.crypto.KeyInfo; import me.impy.aegis.db.DatabaseEntry; import me.impy.aegis.helpers.SimpleItemTouchHelperCallback; @@ -20,6 +20,11 @@ public class KeyProfileView extends Fragment implements KeyProfileAdapter.Listen private KeyProfileAdapter _adapter; private Listener _listener; + private PeriodProgressBar _progressBar; + private Handler _uiHandler; + private boolean _running = false; + private boolean _showProgress = false; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -30,6 +35,11 @@ public class KeyProfileView extends Fragment implements KeyProfileAdapter.Listen public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_keyprofile_view, container, false); + _uiHandler = new Handler(); + _progressBar = view.findViewById(R.id.progressBar); + int primaryColorId = getResources().getColor(R.color.colorPrimary); + _progressBar.getProgressDrawable().setColorFilter(primaryColorId, PorterDuff.Mode.SRC_IN); + // set up the recycler view RecyclerView rvKeyProfiles = view.findViewById(R.id.rvKeyProfiles); LinearLayoutManager mLayoutManager = new LinearLayoutManager(view.getContext()); @@ -42,6 +52,53 @@ public class KeyProfileView extends Fragment implements KeyProfileAdapter.Listen return view; } + public void refresh() { + if (_showProgress) { + _progressBar.refresh(); + } + _adapter.notifyDataSetChanged(); + } + + private void checkPeriodUniformity() { + boolean uniform = _adapter.isPeriodUniform(); + if (uniform == _showProgress) { + return; + } + _showProgress = uniform; + + if (_showProgress) { + _progressBar.setVisibility(View.VISIBLE); + _progressBar.setPeriod(_adapter.getUniformPeriod()); + startRefreshLoop(); + } else { + _progressBar.setVisibility(View.INVISIBLE); + stopRefreshLoop(); + } + } + + public void startRefreshLoop() { + if (_running) { + return; + } + _running = true; + + refresh(); + _uiHandler.postDelayed(new Runnable() { + @Override + public void run() { + if (_running) { + refresh(); + _uiHandler.postDelayed(this, KeyInfo.getMillisTillNextRotation(_adapter.getUniformPeriod())); + } + } + }, KeyInfo.getMillisTillNextRotation(_adapter.getUniformPeriod())); + } + + private void stopRefreshLoop() { + refresh(); + _running = false; + } + public void setListener(Listener listener) { _listener = listener; } @@ -71,34 +128,24 @@ public class KeyProfileView extends Fragment implements KeyProfileAdapter.Listen _adapter.notifyDataSetChanged(); } - public boolean allSamePeriod() - { - return _adapter.allSamePeriod(); - } - - public KeyProfile getKeyProfile(int index) - { - return _adapter.getKeys().get(index); - } - public void addKey(KeyProfile profile) { _adapter.addKey(profile); + checkPeriodUniformity(); } public void removeKey(KeyProfile profile) { _adapter.removeKey(profile); + checkPeriodUniformity(); } public void clearKeys() { _adapter.clearKeys(); + checkPeriodUniformity(); } public void replaceKey(KeyProfile profile) { _adapter.replaceKey(profile); - } - - public void refresh() { - _adapter.refresh(); + checkPeriodUniformity(); } public interface Listener { diff --git a/app/src/main/java/me/impy/aegis/ui/views/PeriodProgressBar.java b/app/src/main/java/me/impy/aegis/ui/views/PeriodProgressBar.java new file mode 100644 index 00000000..c4152c2d --- /dev/null +++ b/app/src/main/java/me/impy/aegis/ui/views/PeriodProgressBar.java @@ -0,0 +1,53 @@ +package me.impy.aegis.ui.views; + +import android.animation.ObjectAnimator; +import android.content.Context; +import android.os.Build; +import android.support.annotation.RequiresApi; +import android.util.AttributeSet; +import android.view.animation.LinearInterpolator; +import android.widget.ProgressBar; + +import me.impy.aegis.crypto.KeyInfo; + +public class PeriodProgressBar extends ProgressBar { + private int _period; + + public PeriodProgressBar(Context context) { + super(context); + } + + public PeriodProgressBar(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public PeriodProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public PeriodProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public void setPeriod(int period) { + _period = period; + } + + public void refresh() { + // reset the progress bar + int maxProgress = getMax(); + setProgress(maxProgress); + + // calculate the progress the bar should start at + long millisTillRotation = KeyInfo.getMillisTillNextRotation(_period); + long period = _period * maxProgress; + int currentProgress = maxProgress - (int) ((((double) period - millisTillRotation) / period) * maxProgress); + + // start progress animation + ObjectAnimator animation = ObjectAnimator.ofInt(this, "progress", currentProgress, 0); + animation.setDuration(millisTillRotation); + animation.setInterpolator(new LinearInterpolator()); + animation.start(); + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 830a8c17..3be9deed 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -15,23 +15,6 @@ android:layout_width="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"/> - - - - - - - diff --git a/app/src/main/res/layout/fragment_keyprofile_view.xml b/app/src/main/res/layout/fragment_keyprofile_view.xml index ab6bfa91..39186e44 100644 --- a/app/src/main/res/layout/fragment_keyprofile_view.xml +++ b/app/src/main/res/layout/fragment_keyprofile_view.xml @@ -3,10 +3,20 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="?attr/background"> + android:background="?attr/background" + android:orientation="vertical"> + + + + android:id="@+id/rvKeyProfiles" + android:layout_weight="1"/>