• 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      * Maximum amount of time to wait for demo user to set up.
60      * After it the user can tap the screen to exit
61      */
62     private static final long READY_TO_TAP_MAX_DELAY_MS = 60 * 1000; // 1 min
63 
64     private PowerManager mPowerManager;
65 
66     private VideoView mVideoView;
67     private String mDownloadPath;
68     private boolean mUsingDownloadedVideo;
69     private Handler mHandler;
70     private boolean mReadyToTap;
71     private SettingsObserver mSettingsObserver;
72     private File mPreloadedVideoFile;
73 
74     @Override
onCreate(Bundle savedInstanceState)75     protected void onCreate(Bundle savedInstanceState) {
76         super.onCreate(savedInstanceState);
77 
78         // Keep screen on
79         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
80                 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
81                 | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
82         setContentView(R.layout.retail_video);
83 
84         mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
85         mHandler = new Handler();
86         final String preloadedFileName = getString(R.string.retail_demo_video_file_name);
87         mPreloadedVideoFile = new File(Environment.getDataPreloadsDemoDirectory(),
88                 preloadedFileName);
89         mDownloadPath = getObbDir().getPath() + File.separator + preloadedFileName;
90         mVideoView = (VideoView) findViewById(R.id.video_content);
91 
92         // Start playing the video when it is ready
93         mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
94             @Override
95             public void onPrepared(MediaPlayer mediaPlayer) {
96                 mediaPlayer.setLooping(true);
97                 mVideoView.start();
98             }
99         });
100 
101         mVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
102             @Override
103             public boolean onError(MediaPlayer mp, int what, int extra) {
104                 if (mUsingDownloadedVideo && mPreloadedVideoFile.exists()) {
105                     if (DEBUG) Log.d(TAG, "Error using the downloaded video, "
106                             + "falling back to the preloaded video at " + mPreloadedVideoFile);
107                     mUsingDownloadedVideo = false;
108                     setVideoPath(mPreloadedVideoFile.getPath());
109                     // And delete the downloaded video so that we don't try to use it
110                     // again next time.
111                     new File(mDownloadPath).delete();
112                 } else {
113                     displayFallbackView();
114                 }
115                 return true;
116             }
117         });
118 
119         mReadyToTap = isUserSetupComplete();
120         if (!mReadyToTap) {
121             // Wait for setup to finish
122             mSettingsObserver = new SettingsObserver();
123             mSettingsObserver.register();
124             // Allow user to exit the demo player if setup takes too long
125             mHandler.postDelayed(() -> {
126                 mReadyToTap = true;
127             }, READY_TO_TAP_MAX_DELAY_MS);
128         }
129 
130         loadVideo();
131     }
132 
displayFallbackView()133     private void displayFallbackView() {
134         if (DEBUG) Log.d(TAG, "Showing the fallback view");
135         findViewById(R.id.fallback_layout).setVisibility(View.VISIBLE);
136         findViewById(R.id.video_layout).setVisibility(View.GONE);
137     }
138 
displayVideoView()139     private void displayVideoView() {
140         if (DEBUG) Log.d(TAG, "Showing the video view");
141         findViewById(R.id.video_layout).setVisibility(View.VISIBLE);
142         findViewById(R.id.fallback_layout).setVisibility(View.GONE);
143     }
144 
loadVideo()145     private void loadVideo() {
146         // If the video is already downloaded, then use that and check for an update.
147         // Otherwise check if the video is preloaded, if not download the video from the
148         // specified url.
149         boolean isVideoSet = false;
150         if (new File(mDownloadPath).exists()) {
151             if (DEBUG) Log.d(TAG, "Using the already existing video at " + mDownloadPath);
152             setVideoPath(mDownloadPath);
153             isVideoSet = true;
154         } else if (mPreloadedVideoFile.exists()) {
155             if (DEBUG) Log.d(TAG, "Using the preloaded video at " + mPreloadedVideoFile);
156             setVideoPath(mPreloadedVideoFile.getPath());
157             isVideoSet = true;
158         }
159 
160         final String downloadUrl = getString(R.string.retail_demo_video_download_url);
161         // If the download url is empty, then no need to start the download task.
162         if (TextUtils.isEmpty(downloadUrl)) {
163             if (!isVideoSet) {
164                 displayFallbackView();
165             }
166             return;
167         }
168         if (!checkIfDownloadingAllowed()) {
169             if (DEBUG) Log.d(TAG, "Downloading not allowed, neither starting download nor checking"
170                     + " for an update.");
171             if (!isVideoSet) {
172                 displayFallbackView();
173             }
174             return;
175         }
176         new DownloadVideoTask(this, mDownloadPath, mPreloadedVideoFile, this).run();
177     }
178 
checkIfDownloadingAllowed()179     private boolean checkIfDownloadingAllowed() {
180         final int lastBootCount = DataReaderWriter.readLastBootCount(this);
181         final int bootCount = Settings.Global.getInt(getContentResolver(),
182                 Settings.Global.BOOT_COUNT, -1);
183         // Something went wrong, don't do anything.
184         if (bootCount == -1) {
185             return false;
186         }
187         // Error reading the last boot count, just write the current boot count.
188         if (lastBootCount == -1) {
189             DataReaderWriter.writeLastBootCount(this, bootCount);
190             return false;
191         }
192         // We need to download the video atmost once after every boot.
193         if (lastBootCount != bootCount) {
194             DataReaderWriter.writeLastBootCount(this, bootCount);
195             return true;
196         }
197         return false;
198     }
199 
200     @Override
onFileDownloaded(final String filePath)201     public void onFileDownloaded(final String filePath) {
202         mUsingDownloadedVideo = true;
203         runOnUiThread(new Runnable() {
204             @Override
205             public void run() {
206                 setVideoPath(filePath);
207             }
208         });
209     }
210 
211     @Override
onError()212     public void onError() {
213         displayFallbackView();
214     }
215 
216     @Override
dispatchTouchEvent(MotionEvent ev)217     public boolean dispatchTouchEvent(MotionEvent ev) {
218         if (mReadyToTap && getSystemService(UserManager.class).isDemoUser()) {
219             disableSelf();
220         }
221         return true;
222     }
223 
disableSelf()224     private void disableSelf() {
225         final String componentName = getString(R.string.demo_overlay_app_component);
226         if (!TextUtils.isEmpty(componentName)) {
227             ComponentName component = ComponentName.unflattenFromString(componentName);
228             if (component != null) {
229                 Intent intent = new Intent();
230                 intent.setComponent(component);
231                 ResolveInfo resolveInfo = getPackageManager().resolveService(intent, 0);
232                 if (resolveInfo != null) {
233                     startService(intent);
234                 } else {
235                     resolveInfo = getPackageManager().resolveActivity(intent,
236                             PackageManager.MATCH_DEFAULT_ONLY);
237                     if (resolveInfo != null) {
238                         startActivity(intent);
239                     } else {
240                         Log.w(TAG, "Component " + componentName + " cannot be resolved");
241                     }
242                 }
243             }
244         }
245         getPackageManager().setComponentEnabledSetting(getComponentName(),
246                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
247     }
248 
249     @Override
onPause()250     public void onPause() {
251         if (mVideoView != null) {
252             mVideoView.pause();
253         }
254         // If power key is pressed to turn screen off, turn screen back on
255         if (!mPowerManager.isInteractive()) {
256             forceTurnOnScreen();
257         }
258         super.onPause();
259     }
260 
261     @Override
onResume()262     public void onResume() {
263         super.onResume();
264         // Resume video playing
265         if (mVideoView != null) {
266             mVideoView.start();
267         }
268     }
269 
270     @Override
onDestroy()271     protected void onDestroy() {
272         if (mSettingsObserver != null) {
273             mSettingsObserver.unregister();
274             mSettingsObserver = null;
275         }
276         super.onDestroy();
277     }
278 
279     @Override
onWindowFocusChanged(boolean hasFocus)280     public void onWindowFocusChanged(boolean hasFocus) {
281         if (hasFocus) {
282             // Make view fullscreen.
283             // And since flags SYSTEM_UI_FLAG_HIDE_NAVIGATION and SYSTEM_UI_FLAG_HIDE_NAVIGATION
284             // might get cleared on user interaction, we do this here.
285             getWindow().getDecorView().setSystemUiVisibility(
286                     View.SYSTEM_UI_FLAG_LAYOUT_STABLE
287                             | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
288                             | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
289                             | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
290                             | View.SYSTEM_UI_FLAG_FULLSCREEN
291                             | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
292                             | View.STATUS_BAR_DISABLE_BACK);
293         }
294     }
295 
setVideoPath(String videoPath)296     private void setVideoPath(String videoPath) {
297         // Load the video from resource
298         try {
299             mVideoView.setVideoPath(videoPath);
300             displayVideoView();
301         } catch (Exception e) {
302             Log.e(TAG, "Exception setting video uri! " + e.getMessage());
303             displayFallbackView();
304         }
305     }
306 
forceTurnOnScreen()307     private void forceTurnOnScreen() {
308         final PowerManager.WakeLock wakeLock = mPowerManager.newWakeLock(
309                 PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG);
310         wakeLock.acquire();
311         // Device woken up, release the wake-lock
312         wakeLock.release();
313     }
314 
315     private class SettingsObserver extends ContentObserver {
316         private final Uri mDemoModeSetupComplete = Settings.Secure.getUriFor(
317                 Settings.Secure.DEMO_USER_SETUP_COMPLETE);
318 
SettingsObserver()319         SettingsObserver() {
320             super(mHandler);
321         }
322 
register()323         void register() {
324             ContentResolver cr = getContentResolver();
325             cr.registerContentObserver(mDemoModeSetupComplete, false, this);
326         }
327 
unregister()328         void unregister() {
329             getContentResolver().unregisterContentObserver(this);
330         }
331 
332         @Override
onChange(boolean selfChange, Uri uri)333         public void onChange(boolean selfChange, Uri uri) {
334             if (mDemoModeSetupComplete.equals(uri)) {
335                 mReadyToTap = true;
336             }
337         }
338     }
339 
isUserSetupComplete()340     private boolean isUserSetupComplete() {
341         return "1".equals(Settings.Secure.getString(getContentResolver(),
342                 Settings.Secure.DEMO_USER_SETUP_COMPLETE));
343     }
344 }
345