| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.retaildemo; |
| |
| import android.app.Activity; |
| import android.content.ComponentName; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.database.ContentObserver; |
| import android.media.MediaPlayer; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Environment; |
| import android.os.Handler; |
| import android.os.PowerManager; |
| import android.os.SystemClock; |
| import android.os.UserManager; |
| import android.provider.Settings; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.WindowManager; |
| import android.widget.VideoView; |
| |
| import java.io.File; |
| |
| /** |
| * This is the activity for playing the retail demo video. This will also try to keep |
| * the screen on. |
| * |
| * This will check for the demo video in {@link Environment#getDataPreloadsDemoDirectory()} or |
| * {@link Context#getObbDir()}. If the demo video is not present, it will run a task to download it |
| * from the specified url. |
| */ |
| public class DemoPlayer extends Activity implements DownloadVideoTask.ResultListener { |
| |
| private static final String TAG = "DemoPlayer"; |
| private static final boolean DEBUG = false; |
| |
| /** |
| * We save the real elapsed time to serve as an indication for downloading the demo video |
| * for the next device boot. The device could boot fast at times and could result in |
| * skipping the download during the next boot sessions. To be safe from cases like this, we |
| * add this offset to the real elapsed time. |
| */ |
| private static final long REAL_ELAPSED_TIME_OFFSET_MS = 60 * 1000; // 1 min |
| |
| /** |
| * Maximum amount of time to wait for demo user to set up. |
| * After it the user can tap the screen to exit |
| */ |
| private static final long READY_TO_TAP_MAX_DELAY_MS = 60 * 1000; // 1 min |
| |
| private PowerManager mPowerManager; |
| |
| private VideoView mVideoView; |
| private String mDownloadPath; |
| private boolean mUsingDownloadedVideo; |
| private Handler mHandler; |
| private boolean mReadyToTap; |
| private SettingsObserver mSettingsObserver; |
| private File mPreloadedVideoFile; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| // Keep screen on |
| getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
| | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
| | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); |
| setContentView(R.layout.retail_video); |
| |
| mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); |
| mHandler = new Handler(); |
| final String preloadedFileName = getString(R.string.retail_demo_video_file_name); |
| mPreloadedVideoFile = new File(Environment.getDataPreloadsDemoDirectory(), |
| preloadedFileName); |
| mDownloadPath = getObbDir().getPath() + File.separator + preloadedFileName; |
| mVideoView = (VideoView) findViewById(R.id.video_content); |
| |
| // Start playing the video when it is ready |
| mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { |
| @Override |
| public void onPrepared(MediaPlayer mediaPlayer) { |
| mediaPlayer.setLooping(true); |
| mVideoView.start(); |
| } |
| }); |
| |
| mVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { |
| @Override |
| public boolean onError(MediaPlayer mp, int what, int extra) { |
| if (mUsingDownloadedVideo && mPreloadedVideoFile.exists()) { |
| if (DEBUG) Log.d(TAG, "Error using the downloaded video, " |
| + "falling back to the preloaded video at " + mPreloadedVideoFile); |
| mUsingDownloadedVideo = false; |
| setVideoPath(mPreloadedVideoFile.getPath()); |
| // And delete the downloaded video so that we don't try to use it |
| // again next time. |
| new File(mDownloadPath).delete(); |
| } else { |
| displayFallbackView(); |
| } |
| return true; |
| } |
| }); |
| |
| mReadyToTap = isUserSetupComplete(); |
| if (!mReadyToTap) { |
| // Wait for setup to finish |
| mSettingsObserver = new SettingsObserver(); |
| mSettingsObserver.register(); |
| // Allow user to exit the demo player if setup takes too long |
| mHandler.postDelayed(() -> { |
| mReadyToTap = true; |
| }, READY_TO_TAP_MAX_DELAY_MS); |
| } |
| |
| loadVideo(); |
| } |
| |
| private void displayFallbackView() { |
| if (DEBUG) Log.d(TAG, "Showing the fallback view"); |
| findViewById(R.id.fallback_layout).setVisibility(View.VISIBLE); |
| findViewById(R.id.video_layout).setVisibility(View.GONE); |
| } |
| |
| private void displayVideoView() { |
| if (DEBUG) Log.d(TAG, "Showing the video view"); |
| findViewById(R.id.video_layout).setVisibility(View.VISIBLE); |
| findViewById(R.id.fallback_layout).setVisibility(View.GONE); |
| } |
| |
| private void loadVideo() { |
| // If the video is already downloaded, then use that and check for an update. |
| // Otherwise check if the video is preloaded, if not download the video from the |
| // specified url. |
| boolean isVideoSet = false; |
| if (new File(mDownloadPath).exists()) { |
| if (DEBUG) Log.d(TAG, "Using the already existing video at " + mDownloadPath); |
| setVideoPath(mDownloadPath); |
| isVideoSet = true; |
| } else if (mPreloadedVideoFile.exists()) { |
| if (DEBUG) Log.d(TAG, "Using the preloaded video at " + mPreloadedVideoFile); |
| setVideoPath(mPreloadedVideoFile.getPath()); |
| isVideoSet = true; |
| } |
| |
| final String downloadUrl = getString(R.string.retail_demo_video_download_url); |
| // If the download url is empty, then no need to start the download task. |
| if (TextUtils.isEmpty(downloadUrl)) { |
| if (!isVideoSet) { |
| displayFallbackView(); |
| } |
| return; |
| } |
| if (!checkIfDownloadingAllowed()) { |
| if (DEBUG) Log.d(TAG, "Downloading not allowed, neither starting download nor checking" |
| + " for an update."); |
| if (!isVideoSet) { |
| displayFallbackView(); |
| } |
| return; |
| } |
| new DownloadVideoTask(this, mDownloadPath, mPreloadedVideoFile, this).run(); |
| } |
| |
| private boolean checkIfDownloadingAllowed() { |
| final long lastRealElapsedTime = DataReaderWriter.getElapsedRealTime(this); |
| final long realElapsedTime = SystemClock.elapsedRealtime(); |
| // We need to download the video atmost once after every boot. |
| if (lastRealElapsedTime == 0 || realElapsedTime < lastRealElapsedTime) { |
| DataReaderWriter.setElapsedRealTime(this, |
| realElapsedTime + REAL_ELAPSED_TIME_OFFSET_MS); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public void onFileDownloaded(final String filePath) { |
| mUsingDownloadedVideo = true; |
| runOnUiThread(new Runnable() { |
| @Override |
| public void run() { |
| setVideoPath(filePath); |
| } |
| }); |
| } |
| |
| @Override |
| public void onError() { |
| displayFallbackView(); |
| } |
| |
| @Override |
| public boolean dispatchTouchEvent(MotionEvent ev) { |
| if (mReadyToTap && getSystemService(UserManager.class).isDemoUser()) { |
| disableSelf(); |
| } |
| return true; |
| } |
| |
| private void disableSelf() { |
| final String componentName = getString(R.string.demo_overlay_app_component); |
| if (!TextUtils.isEmpty(componentName)) { |
| ComponentName component = ComponentName.unflattenFromString(componentName); |
| if (component != null) { |
| Intent intent = new Intent(); |
| intent.setComponent(component); |
| ResolveInfo resolveInfo = getPackageManager().resolveService(intent, 0); |
| if (resolveInfo != null) { |
| startService(intent); |
| } else { |
| resolveInfo = getPackageManager().resolveActivity(intent, |
| PackageManager.MATCH_DEFAULT_ONLY); |
| if (resolveInfo != null) { |
| startActivity(intent); |
| } else { |
| Log.w(TAG, "Component " + componentName + " cannot be resolved"); |
| } |
| } |
| } |
| } |
| getPackageManager().setComponentEnabledSetting(getComponentName(), |
| PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0); |
| } |
| |
| @Override |
| public void onPause() { |
| if (mVideoView != null) { |
| mVideoView.pause(); |
| } |
| // If power key is pressed to turn screen off, turn screen back on |
| if (!mPowerManager.isInteractive()) { |
| forceTurnOnScreen(); |
| } |
| super.onPause(); |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| // Resume video playing |
| if (mVideoView != null) { |
| mVideoView.start(); |
| } |
| } |
| |
| @Override |
| protected void onDestroy() { |
| if (mSettingsObserver != null) { |
| mSettingsObserver.unregister(); |
| mSettingsObserver = null; |
| } |
| super.onDestroy(); |
| } |
| |
| @Override |
| public void onWindowFocusChanged(boolean hasFocus) { |
| if (hasFocus) { |
| // Make view fullscreen. |
| // And since flags SYSTEM_UI_FLAG_HIDE_NAVIGATION and SYSTEM_UI_FLAG_HIDE_NAVIGATION |
| // might get cleared on user interaction, we do this here. |
| getWindow().getDecorView().setSystemUiVisibility( |
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
| | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
| | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
| | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
| | View.SYSTEM_UI_FLAG_FULLSCREEN |
| | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
| | View.STATUS_BAR_DISABLE_BACK); |
| } |
| } |
| |
| private void setVideoPath(String videoPath) { |
| // Load the video from resource |
| try { |
| mVideoView.setVideoPath(videoPath); |
| displayVideoView(); |
| } catch (Exception e) { |
| Log.e(TAG, "Exception setting video uri! " + e.getMessage()); |
| displayFallbackView(); |
| } |
| } |
| |
| private void forceTurnOnScreen() { |
| final PowerManager.WakeLock wakeLock = mPowerManager.newWakeLock( |
| PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG); |
| wakeLock.acquire(); |
| // Device woken up, release the wake-lock |
| wakeLock.release(); |
| } |
| |
| private class SettingsObserver extends ContentObserver { |
| private final Uri mDemoModeSetupComplete = Settings.Secure.getUriFor( |
| Settings.Secure.DEMO_USER_SETUP_COMPLETE); |
| |
| SettingsObserver() { |
| super(mHandler); |
| } |
| |
| void register() { |
| ContentResolver cr = getContentResolver(); |
| cr.registerContentObserver(mDemoModeSetupComplete, false, this); |
| } |
| |
| void unregister() { |
| getContentResolver().unregisterContentObserver(this); |
| } |
| |
| @Override |
| public void onChange(boolean selfChange, Uri uri) { |
| if (mDemoModeSetupComplete.equals(uri)) { |
| mReadyToTap = true; |
| } |
| } |
| } |
| |
| private boolean isUserSetupComplete() { |
| return "1".equals(Settings.Secure.getString(getContentResolver(), |
| Settings.Secure.DEMO_USER_SETUP_COMPLETE)); |
| } |
| } |