| /* |
| * Copyright (C) 2012 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.deskclock; |
| |
| import static android.content.Intent.ACTION_BATTERY_CHANGED; |
| import static android.os.BatteryManager.EXTRA_PLUGGED; |
| |
| import android.app.AlarmManager; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.os.Bundle; |
| import android.view.View; |
| import android.view.ViewTreeObserver.OnPreDrawListener; |
| import android.view.Window; |
| import android.view.WindowInsets; |
| import android.view.WindowManager; |
| import android.widget.TextClock; |
| |
| import androidx.annotation.NonNull; |
| |
| import com.android.deskclock.events.Events; |
| import com.android.deskclock.uidata.UiDataModel; |
| |
| public class ScreensaverActivity extends BaseActivity { |
| |
| private static final LogUtils.Logger LOGGER = new LogUtils.Logger("ScreensaverActivity"); |
| |
| /** These flags keep the screen on if the device is plugged in. */ |
| private static final int WINDOW_FLAGS = WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
| | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
| | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON |
| | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; |
| |
| private final OnPreDrawListener mStartPositionUpdater = new StartPositionUpdater(); |
| |
| private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| LOGGER.v("ScreensaverActivity onReceive, action: " + intent.getAction()); |
| |
| switch (intent.getAction()) { |
| case Intent.ACTION_POWER_CONNECTED: |
| updateWakeLock(true); |
| break; |
| case Intent.ACTION_POWER_DISCONNECTED: |
| updateWakeLock(false); |
| break; |
| case Intent.ACTION_USER_PRESENT: |
| finish(); |
| break; |
| case AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED: |
| Utils.refreshAlarm(ScreensaverActivity.this, mContentView); |
| break; |
| } |
| } |
| }; |
| |
| // Runs every midnight or when the time changes and refreshes the date. |
| private final Runnable mMidnightUpdater = new Runnable() { |
| @Override |
| public void run() { |
| Utils.updateDate(mDateFormat, mDateFormatForAccessibility, mContentView); |
| } |
| }; |
| |
| private String mDateFormat; |
| private String mDateFormatForAccessibility; |
| |
| private View mContentView; |
| private View mMainClockView; |
| |
| private MoveScreensaverRunnable mPositionUpdater; |
| |
| private boolean mAlreadyActive; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| mDateFormat = getString(R.string.abbrev_wday_month_day_no_year); |
| mDateFormatForAccessibility = getString(R.string.full_wday_month_day_no_year); |
| |
| setContentView(R.layout.desk_clock_saver); |
| mContentView = findViewById(R.id.saver_container); |
| mMainClockView = mContentView.findViewById(R.id.main_clock); |
| |
| final View digitalClock = mMainClockView.findViewById(R.id.digital_clock); |
| final AnalogClock analogClock = mMainClockView.findViewById(R.id.analog_clock); |
| |
| Utils.setClockIconTypeface(mMainClockView); |
| Utils.setTimeFormat((TextClock) digitalClock, false); |
| Utils.setClockStyle(digitalClock, analogClock); |
| Utils.dimClockView(true, mMainClockView); |
| analogClock.enableSeconds(false); |
| |
| mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE |
| | View.SYSTEM_UI_FLAG_IMMERSIVE |
| | View.SYSTEM_UI_FLAG_FULLSCREEN |
| | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
| | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); |
| mContentView.setOnApplyWindowInsetsListener(new InteractionListener()); |
| |
| mPositionUpdater = new MoveScreensaverRunnable(mContentView, mMainClockView); |
| |
| final Intent intent = getIntent(); |
| if (intent != null) { |
| final int eventLabel = intent.getIntExtra(Events.EXTRA_EVENT_LABEL, 0); |
| Events.sendScreensaverEvent(R.string.action_show, eventLabel); |
| } |
| } |
| |
| @Override |
| public void onStart() { |
| super.onStart(); |
| |
| final IntentFilter filter = new IntentFilter(); |
| filter.addAction(Intent.ACTION_POWER_CONNECTED); |
| filter.addAction(Intent.ACTION_POWER_DISCONNECTED); |
| filter.addAction(Intent.ACTION_USER_PRESENT); |
| filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); |
| registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED); |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| |
| Utils.updateDate(mDateFormat, mDateFormatForAccessibility, mContentView); |
| Utils.refreshAlarm(ScreensaverActivity.this, mContentView); |
| |
| startPositionUpdater(); |
| UiDataModel.getUiDataModel().addMidnightCallback(mMidnightUpdater); |
| |
| final Intent intent = registerReceiver(null, new IntentFilter(ACTION_BATTERY_CHANGED), |
| Context.RECEIVER_NOT_EXPORTED); |
| final boolean pluggedIn = intent != null && intent.getIntExtra(EXTRA_PLUGGED, 0) != 0; |
| updateWakeLock(pluggedIn); |
| } |
| |
| @Override |
| public void onPause() { |
| super.onPause(); |
| UiDataModel.getUiDataModel().removePeriodicCallback(mMidnightUpdater); |
| stopPositionUpdater(); |
| } |
| |
| @Override |
| public void onStop() { |
| unregisterReceiver(mIntentReceiver); |
| super.onStop(); |
| } |
| |
| @Override |
| public void onUserInteraction() { |
| // We want the screen saver to exit upon user interaction. |
| finish(); |
| } |
| |
| /** |
| * @param pluggedIn {@code true} iff the device is currently plugged in to a charger |
| */ |
| private void updateWakeLock(boolean pluggedIn) { |
| final Window win = getWindow(); |
| final WindowManager.LayoutParams winParams = win.getAttributes(); |
| winParams.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; |
| if (pluggedIn) { |
| winParams.flags |= WINDOW_FLAGS; |
| } else { |
| winParams.flags &= (~WINDOW_FLAGS); |
| } |
| win.setAttributes(winParams); |
| } |
| |
| /** |
| * The {@link #mContentView} will be drawn shortly. When that draw occurs, the position updater |
| * callback will also be executed to choose a random position for the time display as well as |
| * schedule future callbacks to move the time display each minute. |
| */ |
| private void startPositionUpdater() { |
| mContentView.getViewTreeObserver().addOnPreDrawListener(mStartPositionUpdater); |
| } |
| |
| /** |
| * This activity is no longer in the foreground; position callbacks should be removed. |
| */ |
| private void stopPositionUpdater() { |
| mContentView.getViewTreeObserver().removeOnPreDrawListener(mStartPositionUpdater); |
| mPositionUpdater.stop(); |
| } |
| |
| private final class StartPositionUpdater implements OnPreDrawListener { |
| /** |
| * This callback occurs after initial layout has completed. It is an appropriate place to |
| * select a random position for {@link #mMainClockView} and schedule future callbacks to update |
| * its position. |
| * |
| * @return {@code true} to continue with the drawing pass |
| */ |
| @Override |
| public boolean onPreDraw() { |
| if (mContentView.getViewTreeObserver().isAlive()) { |
| // Start the periodic position updater. |
| mPositionUpdater.start(); |
| |
| // This listener must now be removed to avoid starting the position updater again. |
| mContentView.getViewTreeObserver().removeOnPreDrawListener(mStartPositionUpdater); |
| } |
| return true; |
| } |
| } |
| |
| private final class InteractionListener implements View.OnApplyWindowInsetsListener { |
| @NonNull |
| @Override |
| public WindowInsets onApplyWindowInsets(@NonNull View v, @NonNull WindowInsets insets) { |
| if (insets.isVisible(WindowInsets.Type.navigationBars())) { |
| if (mAlreadyActive) { |
| mAlreadyActive = false; |
| finish(); |
| } else { |
| mAlreadyActive = true; |
| } |
| } |
| return insets; |
| } |
| } |
| } |