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