• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.retaildemo;
18 
19 import android.app.Activity;
20 import android.content.ComponentName;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.PackageManager;
25 import android.content.pm.ResolveInfo;
26 import android.database.ContentObserver;
27 import android.media.MediaPlayer;
28 import android.net.Uri;
29 import android.os.Bundle;
30 import android.os.Environment;
31 import android.os.Handler;
32 import android.os.PowerManager;
33 import android.os.SystemClock;
34 import android.os.UserManager;
35 import android.provider.Settings;
36 import android.text.TextUtils;
37 import android.util.Log;
38 import android.view.MotionEvent;
39 import android.view.View;
40 import android.view.WindowManager;
41 import android.widget.VideoView;
42 
43 import java.io.File;
44 
45 /**
46  * This is the activity for playing the retail demo video. This will also try to keep
47  * the screen on.
48  *
49  * This will check for the demo video in {@link Environment#getDataPreloadsDemoDirectory()} or
50  * {@link Context#getObbDir()}. If the demo video is not present, it will run a task to download it
51  * from the specified url.
52  */
53 public class DemoPlayer extends Activity implements DownloadVideoTask.ResultListener {
54 
55     private static final String TAG = "DemoPlayer";
56     private static final boolean DEBUG = false;
57 
58     /**
59      * We save the real elapsed time to serve as an indication for downloading the demo video
60      * for the next device boot. The device could boot fast at times and could result in
61      * skipping the download during the next boot sessions. To be safe from cases like this, we
62      * add this offset to the real elapsed time.
63      */
64     private static final long REAL_ELAPSED_TIME_OFFSET_MS = 60 * 1000; // 1 min
65 
66     /**
67      * Maximum amount of time to wait for demo user to set up.
68      * After it the user can tap the screen to exit
69      */
70     private static final long READY_TO_TAP_MAX_DELAY_MS = 60 * 1000; // 1 min
71 
72     private PowerManager mPowerManager;
73 
74     private VideoView mVideoView;
75     private String mDownloadPath;
76     private boolean mUsingDownloadedVideo;
77     private Handler mHandler;
78     private boolean mReadyToTap;
79     private SettingsObserver mSettingsObserver;
80     private File mPreloadedVideoFile;
81 
82     @Override
onCreate(Bundle savedInstanceState)83     protected void onCreate(Bundle savedInstanceState) {
84         super.onCreate(savedInstanceState);
85 
86         // Keep screen on
87         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
88                 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
89                 | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
90         setContentView(R.layout.retail_video);
91 
92         mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
93         mHandler = new Handler();
94         final String preloadedFileName = getString(R.string.retail_demo_video_file_name);
95         mPreloadedVideoFile = new File(Environment.getDataPreloadsDemoDirectory(),
96                 preloadedFileName);
97         mDownloadPath = getObbDir().getPath() + File.separator + preloadedFileName;
98         mVideoView = (VideoView) findViewById(R.id.video_content);
99 
100         // Start playing the video when it is ready
101         mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
102             @Override
103             public void onPrepared(MediaPlayer mediaPlayer) {
104                 mediaPlayer.setLooping(true);
105                 mVideoView.start();
106             }
107         });
108 
109         mVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
110             @Override
111             public boolean onError(MediaPlayer mp, int what, int extra) {
112                 if (mUsingDownloadedVideo && mPreloadedVideoFile.exists()) {
113                     if (DEBUG) Log.d(TAG, "Error using the downloaded video, "
114                             + "falling back to the preloaded video at " + mPreloadedVideoFile);
115                     mUsingDownloadedVideo = false;
116                     setVideoPath(mPreloadedVideoFile.getPath());
117                     // And delete the downloaded video so that we don't try to use it
118                     // again next time.
119                     new File(mDownloadPath).delete();
120                 } else {
121                     displayFallbackView();
122                 }
123                 return true;
124             }
125         });
126 
127         mReadyToTap = isUserSetupComplete();
128         if (!mReadyToTap) {
129             // Wait for setup to finish
130             mSettingsObserver = new SettingsObserver();
131             mSettingsObserver.register();
132             // Allow user to exit the demo player if setup takes too long
133             mHandler.postDelayed(() -> {
134                 mReadyToTap = true;
135             }, READY_TO_TAP_MAX_DELAY_MS);
136         }
137 
138         loadVideo();
139     }
140 
displayFallbackView()141     private void displayFallbackView() {
142         if (DEBUG) Log.d(TAG, "Showing the fallback view");
143         findViewById(R.id.fallback_layout).setVisibility(View.VISIBLE);
144         findViewById(R.id.video_layout).setVisibility(View.GONE);
145     }
146 
displayVideoView()147     private void displayVideoView() {
148         if (DEBUG) Log.d(TAG, "Showing the video view");
149         findViewById(R.id.video_layout).setVisibility(View.VISIBLE);
150         findViewById(R.id.fallback_layout).setVisibility(View.GONE);
151     }
152 
loadVideo()153     private void loadVideo() {
154         // If the video is already downloaded, then use that and check for an update.
155         // Otherwise check if the video is preloaded, if not download the video from the
156         // specified url.
157         boolean isVideoSet = false;
158         if (new File(mDownloadPath).exists()) {
159             if (DEBUG) Log.d(TAG, "Using the already existing video at " + mDownloadPath);
160             setVideoPath(mDownloadPath);
161             isVideoSet = true;
162         } else if (mPreloadedVideoFile.exists()) {
163             if (DEBUG) Log.d(TAG, "Using the preloaded video at " + mPreloadedVideoFile);
164             setVideoPath(mPreloadedVideoFile.getPath());
165             isVideoSet = true;
166         }
167 
168         final String downloadUrl = getString(R.string.retail_demo_video_download_url);
169         // If the download url is empty, then no need to start the download task.
170         if (TextUtils.isEmpty(downloadUrl)) {
171             if (!isVideoSet) {
172                 displayFallbackView();
173             }
174             return;
175         }
176         if (!checkIfDownloadingAllowed()) {
177             if (DEBUG) Log.d(TAG, "Downloading not allowed, neither starting download nor checking"
178                     + " for an update.");
179             if (!isVideoSet) {
180                 displayFallbackView();
181             }
182             return;
183         }
184         new DownloadVideoTask(this, mDownloadPath, mPreloadedVideoFile, this).run();
185     }
186 
checkIfDownloadingAllowed()187     private boolean checkIfDownloadingAllowed() {
188         final long lastRealElapsedTime = DataReaderWriter.getElapsedRealTime(this);
189         final long realElapsedTime = SystemClock.elapsedRealtime();
190         // We need to download the video atmost once after every boot.
191         if (lastRealElapsedTime == 0 || realElapsedTime < lastRealElapsedTime) {
192             DataReaderWriter.setElapsedRealTime(this,
193                     realElapsedTime + REAL_ELAPSED_TIME_OFFSET_MS);
194             return true;
195         }
196         return false;
197     }
198 
199     @Override
onFileDownloaded(final String filePath)200     public void onFileDownloaded(final String filePath) {
201         mUsingDownloadedVideo = true;
202         runOnUiThread(new Runnable() {
203             @Override
204             public void run() {
205                 setVideoPath(filePath);
206             }
207         });
208     }
209 
210     @Override
onError()211     public void onError() {
212         displayFallbackView();
213     }
214 
215     @Override
dispatchTouchEvent(MotionEvent ev)216     public boolean dispatchTouchEvent(MotionEvent ev) {
217         if (mReadyToTap && getSystemService(UserManager.class).isDemoUser()) {
218             disableSelf();
219         }
220         return true;
221     }
222 
disableSelf()223     private void disableSelf() {
224         final String componentName = getString(R.string.demo_overlay_app_component);
225         if (!TextUtils.isEmpty(componentName)) {
226             ComponentName component = ComponentName.unflattenFromString(componentName);
227             if (component != null) {
228                 Intent intent = new Intent();
229                 intent.setComponent(component);
230                 ResolveInfo resolveInfo = getPackageManager().resolveService(intent, 0);
231                 if (resolveInfo != null) {
232                     startService(intent);
233                 } else {
234                     resolveInfo = getPackageManager().resolveActivity(intent,
235                             PackageManager.MATCH_DEFAULT_ONLY);
236                     if (resolveInfo != null) {
237                         startActivity(intent);
238                     } else {
239                         Log.w(TAG, "Component " + componentName + " cannot be resolved");
240                     }
241                 }
242             }
243         }
244         getPackageManager().setComponentEnabledSetting(getComponentName(),
245                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
246     }
247 
248     @Override
onPause()249     public void onPause() {
250         if (mVideoView != null) {
251             mVideoView.pause();
252         }
253         // If power key is pressed to turn screen off, turn screen back on
254         if (!mPowerManager.isInteractive()) {
255             forceTurnOnScreen();
256         }
257         super.onPause();
258     }
259 
260     @Override
onResume()261     public void onResume() {
262         super.onResume();
263         // Resume video playing
264         if (mVideoView != null) {
265             mVideoView.start();
266         }
267     }
268 
269     @Override
onDestroy()270     protected void onDestroy() {
271         if (mSettingsObserver != null) {
272             mSettingsObserver.unregister();
273             mSettingsObserver = null;
274         }
275         super.onDestroy();
276     }
277 
278     @Override
onWindowFocusChanged(boolean hasFocus)279     public void onWindowFocusChanged(boolean hasFocus) {
280         if (hasFocus) {
281             // Make view fullscreen.
282             // And since flags SYSTEM_UI_FLAG_HIDE_NAVIGATION and SYSTEM_UI_FLAG_HIDE_NAVIGATION
283             // might get cleared on user interaction, we do this here.
284             getWindow().getDecorView().setSystemUiVisibility(
285                     View.SYSTEM_UI_FLAG_LAYOUT_STABLE
286                             | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
287                             | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
288                             | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
289                             | View.SYSTEM_UI_FLAG_FULLSCREEN
290                             | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
291                             | View.STATUS_BAR_DISABLE_BACK);
292         }
293     }
294 
setVideoPath(String videoPath)295     private void setVideoPath(String videoPath) {
296         // Load the video from resource
297         try {
298             mVideoView.setVideoPath(videoPath);
299             displayVideoView();
300         } catch (Exception e) {
301             Log.e(TAG, "Exception setting video uri! " + e.getMessage());
302             displayFallbackView();
303         }
304     }
305 
forceTurnOnScreen()306     private void forceTurnOnScreen() {
307         final PowerManager.WakeLock wakeLock = mPowerManager.newWakeLock(
308                 PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG);
309         wakeLock.acquire();
310         // Device woken up, release the wake-lock
311         wakeLock.release();
312     }
313 
314     private class SettingsObserver extends ContentObserver {
315         private final Uri mDemoModeSetupComplete = Settings.Secure.getUriFor(
316                 Settings.Secure.DEMO_USER_SETUP_COMPLETE);
317 
SettingsObserver()318         SettingsObserver() {
319             super(mHandler);
320         }
321 
register()322         void register() {
323             ContentResolver cr = getContentResolver();
324             cr.registerContentObserver(mDemoModeSetupComplete, false, this);
325         }
326 
unregister()327         void unregister() {
328             getContentResolver().unregisterContentObserver(this);
329         }
330 
331         @Override
onChange(boolean selfChange, Uri uri)332         public void onChange(boolean selfChange, Uri uri) {
333             if (mDemoModeSetupComplete.equals(uri)) {
334                 mReadyToTap = true;
335             }
336         }
337     }
338 
isUserSetupComplete()339     private boolean isUserSetupComplete() {
340         return "1".equals(Settings.Secure.getString(getContentResolver(),
341                 Settings.Secure.DEMO_USER_SETUP_COMPLETE));
342     }
343 }
344