openEventsPlatformApp/plugins/cordova-plugin-splashscreen/src/android/SplashScreen.java
plugins/cordova-plugin-splashscreen/src/android/SplashScreen.javaRaw
1/*
2 Licensed to the Apache Software Foundation (ASF) under one
3 or more contributor license agreements. See the NOTICE file
4 distributed with this work for additional information
5 regarding copyright ownership. The ASF licenses this file
6 to you under the Apache License, Version 2.0 (the
7 "License"); you may not use this file except in compliance
8 with the License. You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing,
13 software distributed under the License is distributed on an
14 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 KIND, either express or implied. See the License for the
16 specific language governing permissions and limitations
17 under the License.
18*/
19
20package org.apache.cordova.splashscreen;
21
22import android.app.Dialog;
23import android.app.ProgressDialog;
24import android.content.Context;
25import android.content.DialogInterface;
26import android.content.res.Configuration;
27import android.graphics.Color;
28import android.graphics.drawable.ColorDrawable;
29import android.os.Handler;
30import android.view.Display;
31import android.view.Gravity;
32import android.view.View;
33import android.view.ViewGroup.LayoutParams;
34import android.view.WindowManager;
35import android.view.animation.Animation;
36import android.view.animation.AlphaAnimation;
37import android.view.animation.DecelerateInterpolator;
38import android.widget.ImageView;
39import android.widget.LinearLayout;
40import android.widget.ProgressBar;
41import android.widget.RelativeLayout;
42
43import org.apache.cordova.CallbackContext;
44import org.apache.cordova.CordovaPlugin;
45import org.apache.cordova.CordovaWebView;
46import org.json.JSONArray;
47import org.json.JSONException;
48
49public class SplashScreen extends CordovaPlugin {
50 private static final String LOG_TAG = "SplashScreen";
51 // Cordova 3.x.x has a copy of this plugin bundled with it (SplashScreenInternal.java).
52 // Enable functionality only if running on 4.x.x.
53 private static final boolean HAS_BUILT_IN_SPLASH_SCREEN = Integer.valueOf(CordovaWebView.CORDOVA_VERSION.split("\\.")[0]) < 4;
54 private static final int DEFAULT_SPLASHSCREEN_DURATION = 3000;
55 private static final int DEFAULT_FADE_DURATION = 500;
56 private static Dialog splashDialog;
57 private static ProgressDialog spinnerDialog;
58 private static boolean firstShow = true;
59 private static boolean lastHideAfterDelay; // https://issues.apache.org/jira/browse/CB-9094
60
61 /**
62 * Displays the splash drawable.
63 */
64 private ImageView splashImageView;
65
66 /**
67 * Remember last device orientation to detect orientation changes.
68 */
69 private int orientation;
70
71 // Helper to be compile-time compatible with both Cordova 3.x and 4.x.
72 private View getView() {
73 try {
74 return (View)webView.getClass().getMethod("getView").invoke(webView);
75 } catch (Exception e) {
76 return (View)webView;
77 }
78 }
79
80 @Override
81 protected void pluginInitialize() {
82 if (HAS_BUILT_IN_SPLASH_SCREEN) {
83 return;
84 }
85 // Make WebView invisible while loading URL
86 // CB-11326 Ensure we're calling this on UI thread
87 cordova.getActivity().runOnUiThread(new Runnable() {
88 @Override
89 public void run() {
90 getView().setVisibility(View.INVISIBLE);
91 }
92 });
93 int drawableId = preferences.getInteger("SplashDrawableId", 0);
94 if (drawableId == 0) {
95 String splashResource = preferences.getString("SplashScreen", "screen");
96 if (splashResource != null) {
97 drawableId = cordova.getActivity().getResources().getIdentifier(splashResource, "drawable", cordova.getActivity().getClass().getPackage().getName());
98 if (drawableId == 0) {
99 drawableId = cordova.getActivity().getResources().getIdentifier(splashResource, "drawable", cordova.getActivity().getPackageName());
100 }
101 preferences.set("SplashDrawableId", drawableId);
102 }
103 }
104
105 // Save initial orientation.
106 orientation = cordova.getActivity().getResources().getConfiguration().orientation;
107
108 if (firstShow) {
109 boolean autoHide = preferences.getBoolean("AutoHideSplashScreen", true);
110 showSplashScreen(autoHide);
111 }
112
113 if (preferences.getBoolean("SplashShowOnlyFirstTime", true)) {
114 firstShow = false;
115 }
116 }
117
118 /**
119 * Shorter way to check value of "SplashMaintainAspectRatio" preference.
120 */
121 private boolean isMaintainAspectRatio () {
122 return preferences.getBoolean("SplashMaintainAspectRatio", false);
123 }
124
125 private int getFadeDuration () {
126 int fadeSplashScreenDuration = preferences.getBoolean("FadeSplashScreen", true) ?
127 preferences.getInteger("FadeSplashScreenDuration", DEFAULT_FADE_DURATION) : 0;
128
129 if (fadeSplashScreenDuration < 30) {
130 // [CB-9750] This value used to be in decimal seconds, so we will assume that if someone specifies 10
131 // they mean 10 seconds, and not the meaningless 10ms
132 fadeSplashScreenDuration *= 1000;
133 }
134
135 return fadeSplashScreenDuration;
136 }
137
138 @Override
139 public void onPause(boolean multitasking) {
140 if (HAS_BUILT_IN_SPLASH_SCREEN) {
141 return;
142 }
143 // hide the splash screen to avoid leaking a window
144 this.removeSplashScreen(true);
145 }
146
147 @Override
148 public void onDestroy() {
149 if (HAS_BUILT_IN_SPLASH_SCREEN) {
150 return;
151 }
152 // hide the splash screen to avoid leaking a window
153 this.removeSplashScreen(true);
154 // If we set this to true onDestroy, we lose track when we go from page to page!
155 //firstShow = true;
156 }
157
158 @Override
159 public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
160 if (action.equals("hide")) {
161 cordova.getActivity().runOnUiThread(new Runnable() {
162 public void run() {
163 webView.postMessage("splashscreen", "hide");
164 }
165 });
166 } else if (action.equals("show")) {
167 cordova.getActivity().runOnUiThread(new Runnable() {
168 public void run() {
169 webView.postMessage("splashscreen", "show");
170 }
171 });
172 } else {
173 return false;
174 }
175
176 callbackContext.success();
177 return true;
178 }
179
180 @Override
181 public Object onMessage(String id, Object data) {
182 if (HAS_BUILT_IN_SPLASH_SCREEN) {
183 return null;
184 }
185 if ("splashscreen".equals(id)) {
186 if ("hide".equals(data.toString())) {
187 this.removeSplashScreen(false);
188 } else {
189 this.showSplashScreen(false);
190 }
191 } else if ("spinner".equals(id)) {
192 if ("stop".equals(data.toString())) {
193 getView().setVisibility(View.VISIBLE);
194 }
195 } else if ("onReceivedError".equals(id)) {
196 this.spinnerStop();
197 }
198 return null;
199 }
200
201 // Don't add @Override so that plugin still compiles on 3.x.x for a while
202 public void onConfigurationChanged(Configuration newConfig) {
203 if (newConfig.orientation != orientation) {
204 orientation = newConfig.orientation;
205
206 // Splash drawable may change with orientation, so reload it.
207 if (splashImageView != null) {
208 int drawableId = preferences.getInteger("SplashDrawableId", 0);
209 if (drawableId != 0) {
210 splashImageView.setImageDrawable(cordova.getActivity().getResources().getDrawable(drawableId));
211 }
212 }
213 }
214 }
215
216 private void removeSplashScreen(final boolean forceHideImmediately) {
217 cordova.getActivity().runOnUiThread(new Runnable() {
218 public void run() {
219 if (splashDialog != null && splashDialog.isShowing()) {
220 final int fadeSplashScreenDuration = getFadeDuration();
221 // CB-10692 If the plugin is being paused/destroyed, skip the fading and hide it immediately
222 if (fadeSplashScreenDuration > 0 && forceHideImmediately == false) {
223 AlphaAnimation fadeOut = new AlphaAnimation(1, 0);
224 fadeOut.setInterpolator(new DecelerateInterpolator());
225 fadeOut.setDuration(fadeSplashScreenDuration);
226
227 splashImageView.setAnimation(fadeOut);
228 splashImageView.startAnimation(fadeOut);
229
230 fadeOut.setAnimationListener(new Animation.AnimationListener() {
231 @Override
232 public void onAnimationStart(Animation animation) {
233 spinnerStop();
234 }
235
236 @Override
237 public void onAnimationEnd(Animation animation) {
238 if (splashDialog != null && splashDialog.isShowing()) {
239 splashDialog.dismiss();
240 splashDialog = null;
241 splashImageView = null;
242 }
243 }
244
245 @Override
246 public void onAnimationRepeat(Animation animation) {
247 }
248 });
249 } else {
250 spinnerStop();
251 splashDialog.dismiss();
252 splashDialog = null;
253 splashImageView = null;
254 }
255 }
256 }
257 });
258 }
259
260 /**
261 * Shows the splash screen over the full Activity
262 */
263 @SuppressWarnings("deprecation")
264 private void showSplashScreen(final boolean hideAfterDelay) {
265 final int splashscreenTime = preferences.getInteger("SplashScreenDelay", DEFAULT_SPLASHSCREEN_DURATION);
266 final int drawableId = preferences.getInteger("SplashDrawableId", 0);
267
268 final int fadeSplashScreenDuration = getFadeDuration();
269 final int effectiveSplashDuration = Math.max(0, splashscreenTime - fadeSplashScreenDuration);
270
271 lastHideAfterDelay = hideAfterDelay;
272
273 // If the splash dialog is showing don't try to show it again
274 if (splashDialog != null && splashDialog.isShowing()) {
275 return;
276 }
277 if (drawableId == 0 || (splashscreenTime <= 0 && hideAfterDelay)) {
278 return;
279 }
280
281 cordova.getActivity().runOnUiThread(new Runnable() {
282 public void run() {
283 // Get reference to display
284 Display display = cordova.getActivity().getWindowManager().getDefaultDisplay();
285 Context context = webView.getContext();
286
287 // Use an ImageView to render the image because of its flexible scaling options.
288 splashImageView = new ImageView(context);
289 splashImageView.setImageResource(drawableId);
290 LayoutParams layoutParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
291 splashImageView.setLayoutParams(layoutParams);
292
293 splashImageView.setMinimumHeight(display.getHeight());
294 splashImageView.setMinimumWidth(display.getWidth());
295
296 // TODO: Use the background color of the webView's parent instead of using the preference.
297 splashImageView.setBackgroundColor(preferences.getInteger("backgroundColor", Color.BLACK));
298
299 if (isMaintainAspectRatio()) {
300 // CENTER_CROP scale mode is equivalent to CSS "background-size:cover"
301 splashImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
302 }
303 else {
304 // FIT_XY scales image non-uniformly to fit into image view.
305 splashImageView.setScaleType(ImageView.ScaleType.FIT_XY);
306 }
307
308 // Create and show the dialog
309 splashDialog = new Dialog(context, android.R.style.Theme_Translucent_NoTitleBar);
310 // check to see if the splash screen should be full screen
311 if ((cordova.getActivity().getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN)
312 == WindowManager.LayoutParams.FLAG_FULLSCREEN) {
313 splashDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
314 WindowManager.LayoutParams.FLAG_FULLSCREEN);
315 }
316 splashDialog.setContentView(splashImageView);
317 splashDialog.setCancelable(false);
318 splashDialog.show();
319
320 if (preferences.getBoolean("ShowSplashScreenSpinner", true)) {
321 spinnerStart();
322 }
323
324 // Set Runnable to remove splash screen just in case
325 if (hideAfterDelay) {
326 final Handler handler = new Handler();
327 handler.postDelayed(new Runnable() {
328 public void run() {
329 if (lastHideAfterDelay) {
330 removeSplashScreen(false);
331 }
332 }
333 }, effectiveSplashDuration);
334 }
335 }
336 });
337 }
338
339 // Show only spinner in the center of the screen
340 private void spinnerStart() {
341 cordova.getActivity().runOnUiThread(new Runnable() {
342 public void run() {
343 spinnerStop();
344
345 spinnerDialog = new ProgressDialog(webView.getContext());
346 spinnerDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
347 public void onCancel(DialogInterface dialog) {
348 spinnerDialog = null;
349 }
350 });
351
352 spinnerDialog.setCancelable(false);
353 spinnerDialog.setIndeterminate(true);
354
355 RelativeLayout centeredLayout = new RelativeLayout(cordova.getActivity());
356 centeredLayout.setGravity(Gravity.CENTER);
357 centeredLayout.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
358
359 ProgressBar progressBar = new ProgressBar(webView.getContext());
360 RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
361 layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
362 progressBar.setLayoutParams(layoutParams);
363
364 centeredLayout.addView(progressBar);
365
366 spinnerDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
367 spinnerDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
368
369 spinnerDialog.show();
370 spinnerDialog.setContentView(centeredLayout);
371 }
372 });
373 }
374
375 private void spinnerStop() {
376 cordova.getActivity().runOnUiThread(new Runnable() {
377 public void run() {
378 if (spinnerDialog != null && spinnerDialog.isShowing()) {
379 spinnerDialog.dismiss();
380 spinnerDialog = null;
381 }
382 }
383 });
384 }
385}