/* * Copyright (C) 2018 The Android Open Source Project * * 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.android.settings.biometrics.face; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Rect; import com.android.settings.R; import com.android.settings.biometrics.BiometricEnrollSidecar; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Class that's used to create, maintain, and update the state of each animation particle. Particles * should have their colors assigned based on their index. Particles are split into primary and * secondary types - primary types animate twice as fast during the completion effect. The particles * are updated/drawn in a special order so that the overlap is correct during the final completion * effect. */ public class ParticleCollection implements BiometricEnrollSidecar.Listener { private static final String TAG = "AnimationController"; private static final int NUM_PARTICLES = 12; public static final int STATE_STARTED = 1; // dots are rotating public static final int STATE_STOPPED_COLORFUL = 2; // dots are not rotating but colorful public static final int STATE_STOPPED_GRAY = 3; // dots are not rotating and also gray (error) public static final int STATE_COMPLETE = 4; // face is enrolled private final List mParticleList; private final List mPrimariesInProgress; // primary particles not done animating yet private int mState; private Listener mListener; public interface Listener { void onEnrolled(); } private final AnimationParticle.Listener mParticleListener = new AnimationParticle.Listener() { @Override public void onRingCompleted(int index) { final boolean wasEmpty = mPrimariesInProgress.isEmpty(); // We can stop the time animator once the three primary particles have finished for (int i = 0; i < mPrimariesInProgress.size(); i++) { if (mPrimariesInProgress.get(i).intValue() == index) { mPrimariesInProgress.remove(i); break; } } if (mPrimariesInProgress.isEmpty() && !wasEmpty) { mListener.onEnrolled(); } } }; public ParticleCollection(Context context, Listener listener, Rect bounds, int borderWidth) { mParticleList = new ArrayList<>(); mListener = listener; final List colors = new ArrayList<>(); final Resources.Theme theme = context.getTheme(); final Resources resources = context.getResources(); colors.add(resources.getColor(R.color.face_anim_particle_color_1, theme)); colors.add(resources.getColor(R.color.face_anim_particle_color_2, theme)); colors.add(resources.getColor(R.color.face_anim_particle_color_3, theme)); colors.add(resources.getColor(R.color.face_anim_particle_color_4, theme)); // Primary particles expand faster during the completion animation mPrimariesInProgress = new ArrayList<>(Arrays.asList(0, 4, 8)); // Order in which to draw the particles. This is so the final "completion" animation has // the correct behavior. final int[] order = {3, 7, 11, 2, 6, 10, 1, 5, 9, 0, 4, 8}; for (int i = 0; i < NUM_PARTICLES; i++) { AnimationParticle particle = new AnimationParticle(context, mParticleListener, bounds, borderWidth, order[i], NUM_PARTICLES, colors); if (mPrimariesInProgress.contains(order[i])) { particle.setAsPrimary(); } mParticleList.add(particle); } updateState(STATE_STARTED); } public void update(long t, long dt) { for (int i = 0; i < mParticleList.size(); i++) { mParticleList.get(i).update(t, dt); } } public void draw(Canvas canvas) { for (int i = 0; i < mParticleList.size(); i++) { mParticleList.get(i).draw(canvas); } } private void updateState(int state) { if (mState != state) { for (int i = 0; i < mParticleList.size(); i++) { mParticleList.get(i).updateState(state); } mState = state; } } @Override public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { } @Override public void onEnrollmentError(int errMsgId, CharSequence errString) { } @Override public void onEnrollmentProgressChange(int steps, int remaining) { if (remaining == 0) { updateState(STATE_COMPLETE); } } }