1 /* 2 * Copyright 2018 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 package org.skia.skottie; 9 10 import android.app.Activity; 11 import android.content.Intent; 12 import android.graphics.Color; 13 import android.graphics.Point; 14 import android.net.Uri; 15 import android.os.Bundle; 16 import android.view.View; 17 import android.view.ViewGroup; 18 import android.widget.Button; 19 import android.widget.GridLayout; 20 21 import java.io.FileNotFoundException; 22 import java.util.ArrayList; 23 import java.util.List; 24 import java.util.concurrent.CountDownLatch; 25 import java.util.concurrent.TimeUnit; 26 import java.util.concurrent.TimeoutException; 27 28 import static java.lang.Math.ceil; 29 import static java.lang.Math.sqrt; 30 31 public class SkottieActivity extends Activity implements View.OnClickListener { 32 33 private final static long TIME_OUT_MS = 10000; 34 35 private CountDownLatch mEnterAnimationFence = new CountDownLatch(1); 36 37 private GridLayout mGrid; 38 private int mRowCount = 0; 39 private int mColumnCount = 0; 40 private int mCellWidth = 0; 41 private int mCellHeight = 0; 42 43 private List<SkottieView> mAnimations; 44 static private List<Uri> mAnimationFiles = new ArrayList<Uri>(); 45 populateGrid()46 private void populateGrid() { 47 mRowCount = 0; 48 mColumnCount = 0; 49 mAnimations = new ArrayList<SkottieView>(); 50 mCellWidth = 0; 51 mCellHeight = 0; 52 53 int rawAssets[] = { 54 R.raw.star, R.raw.movie_loading, R.raw.uk, R.raw.white_material_wave_loading 55 }; 56 57 for (int resId : rawAssets) { 58 SkottieView view = new SkottieView(this); 59 view.setSource(resId); 60 mAnimations.add(view); 61 } 62 63 for (Uri uri : mAnimationFiles) { 64 try { 65 SkottieView view = new SkottieView(this); 66 view.setSource(this, uri); 67 mAnimations.add(view); 68 } catch (FileNotFoundException e) { 69 e.printStackTrace(); 70 } 71 } 72 73 Point size = new Point(); 74 getWindowManager().getDefaultDisplay().getSize(size); 75 int screenWidth = size.x; 76 int screenHeight = (int)(size.y / 1.3f); 77 78 double unit = sqrt(mAnimations.size() / 6.0f); 79 mRowCount = (int)ceil(3 * unit); 80 mColumnCount = (int)ceil(2 * unit); 81 mGrid.setColumnCount(mColumnCount); 82 mGrid.setRowCount(mRowCount); 83 mCellWidth = screenWidth / mColumnCount; 84 mCellHeight = screenHeight / mRowCount; 85 86 refreshGrid(); 87 88 startAnimation(); 89 90 for (SkottieView view : mAnimations) { 91 view.setOnClickListener(new View.OnClickListener(){ 92 public void onClick(View view){ 93 inflateView((SkottieView)view); 94 } 95 }); 96 } 97 98 if (mInflatedIndex >= 0) { 99 SkottieView view = mAnimations.get(mInflatedIndex); 100 mInflatedIndex = -1; 101 inflateView(view); 102 } 103 } 104 105 static int mInflatedIndex = -1; 106 inflateView(SkottieView view)107 private void inflateView(SkottieView view) { 108 if (mInflatedIndex >= 0) { 109 //deflate active view 110 SkottieView oldView = mAnimations.get(mInflatedIndex); 111 if (oldView != null) { 112 int row = mInflatedIndex / mColumnCount, column = mInflatedIndex % mColumnCount; 113 addView(oldView, row, column, false); 114 } 115 mInflatedIndex = -1; 116 //start and show animations that were in the background 117 for (SkottieView anyView : mAnimations) { 118 if (anyView != oldView) { 119 anyView.start(); 120 anyView.setVisibility(View.VISIBLE); 121 } 122 } 123 return; 124 } 125 126 //stop and hide animations in the background 127 for (SkottieView anyView : mAnimations) { 128 if (anyView != view) { 129 anyView.stop(); 130 anyView.setVisibility(View.INVISIBLE); 131 } 132 } 133 134 mInflatedIndex = mAnimations.indexOf(view); 135 136 GridLayout.Spec rowSpec = GridLayout.spec(0, mRowCount, GridLayout.CENTER); 137 GridLayout.Spec colSpec = GridLayout.spec(0, mColumnCount, GridLayout.CENTER); 138 GridLayout.LayoutParams params = new GridLayout.LayoutParams(rowSpec, colSpec); 139 params.width = ViewGroup.LayoutParams.MATCH_PARENT; 140 params.height = ViewGroup.LayoutParams.MATCH_PARENT; 141 142 mGrid.updateViewLayout(view, params); 143 } 144 refreshGrid()145 private void refreshGrid() { 146 mGrid.removeAllViews(); 147 int currentRaw = 0; 148 int row = 0, column = 0; 149 for (SkottieView view : mAnimations) { 150 addView(view, row, column, true); 151 column++; 152 if (column >= mColumnCount) { 153 column = 0; 154 row++; 155 } 156 } 157 } 158 addView(SkottieView view, int row , int column, boolean addView)159 private void addView(SkottieView view, int row , int column, boolean addView) { 160 GridLayout.Spec rowSpec = GridLayout.spec(row, 1, GridLayout.CENTER); 161 GridLayout.Spec colSpec = GridLayout.spec(column, 1, GridLayout.CENTER); 162 GridLayout.LayoutParams params = new GridLayout.LayoutParams(rowSpec, colSpec); 163 params.width = mCellWidth; 164 params.height = mCellHeight; 165 if (addView) { 166 mGrid.addView(view, params); 167 } else { 168 mGrid.updateViewLayout(view, params); 169 } 170 } 171 startAnimation()172 private void startAnimation() { 173 for (SkottieView view : mAnimations) { 174 view.start(); 175 } 176 } 177 stopAnimation()178 private void stopAnimation() { 179 for (SkottieView view : mAnimations) { 180 view.stop(); 181 } 182 } 183 addLottie(Uri uri)184 private void addLottie(Uri uri) throws FileNotFoundException { 185 int animations = mAnimations.size(); 186 if (animations < mRowCount * mColumnCount) { 187 SkottieView view = new SkottieView(this); 188 view.setSource(this, uri); 189 int row = animations / mColumnCount, column = animations % mColumnCount; 190 mAnimations.add(view); 191 mAnimationFiles.add(uri); 192 view.setOnClickListener(new View.OnClickListener(){ 193 public void onClick(View view){ 194 inflateView((SkottieView)view); 195 } 196 }); 197 addView(view, row, column, true); 198 view.start(); 199 } else { 200 stopAnimation(); 201 mAnimationFiles.add(uri); 202 populateGrid(); 203 startAnimation(); 204 } 205 } 206 207 208 @Override onEnterAnimationComplete()209 public void onEnterAnimationComplete() { 210 super.onEnterAnimationComplete(); 211 mEnterAnimationFence.countDown(); 212 } 213 waitForEnterAnimationComplete()214 public void waitForEnterAnimationComplete() throws TimeoutException, InterruptedException { 215 if (!mEnterAnimationFence.await(TIME_OUT_MS, TimeUnit.MILLISECONDS)) { 216 throw new TimeoutException(); 217 } 218 } 219 createLayout()220 private void createLayout() { 221 setContentView(R.layout.main_layout); 222 Button open = (Button)findViewById(R.id.open_lottie); 223 open.setOnClickListener(this); 224 225 Button play = (Button)findViewById(R.id.play); 226 play.setOnClickListener(this); 227 Button pause = (Button)findViewById(R.id.pause); 228 pause.setOnClickListener(this); 229 Button reset = (Button)findViewById(R.id.reset); 230 reset.setOnClickListener(this); 231 232 mGrid = (GridLayout)findViewById(R.id.grid_lotties); 233 mGrid.setBackgroundColor(Color.LTGRAY); 234 235 populateGrid(); 236 } 237 238 @Override onCreate(Bundle savedInstanceState)239 protected void onCreate(Bundle savedInstanceState) { 240 super.onCreate(savedInstanceState); 241 242 createLayout(); 243 } 244 245 @Override onDestroy()246 protected void onDestroy() { 247 super.onDestroy(); 248 } 249 250 static final int PICK_FILE_REQUEST = 2; 251 252 @Override onClick(View view)253 public void onClick(View view) { 254 switch(view.getId()) { 255 case R.id.open_lottie: 256 Intent intent = new Intent(); 257 intent.setType("application/json"); 258 Intent i = Intent.createChooser(intent, "View Default File Manager"); 259 startActivityForResult(i, PICK_FILE_REQUEST); 260 break; 261 case R.id.play: 262 for (SkottieView anim : mAnimations) { 263 anim.play(); 264 } 265 break; 266 case R.id.pause: 267 for (SkottieView anim : mAnimations) { 268 anim.pause(); 269 } 270 break; 271 case R.id.reset: 272 for (SkottieView anim : mAnimations) { 273 anim.seek(0f); 274 } 275 break; 276 } 277 278 } 279 280 @Override onActivityResult(int requestCode, int resultCode, Intent data)281 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 282 super.onActivityResult(requestCode, resultCode, data); 283 if (resultCode == Activity.RESULT_OK) { 284 if (requestCode == PICK_FILE_REQUEST) if (data != null) { 285 //no data present 286 Uri uri = data.getData(); 287 288 try { 289 addLottie(uri); 290 } catch (FileNotFoundException e) { 291 e.printStackTrace(); 292 } 293 } 294 } 295 } 296 } 297