• 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.deskclock;
18 
19 import android.app.AlarmManager;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.res.Configuration;
25 import android.database.ContentObserver;
26 import android.net.Uri;
27 import android.os.Handler;
28 import android.provider.Settings;
29 import android.service.dreams.DreamService;
30 import android.view.View;
31 import android.view.ViewTreeObserver.OnPreDrawListener;
32 import android.widget.TextClock;
33 
34 import com.android.deskclock.data.DataModel;
35 import com.android.deskclock.uidata.UiDataModel;
36 
37 public final class Screensaver extends DreamService {
38 
39     private static final LogUtils.Logger LOGGER = new LogUtils.Logger("Screensaver");
40 
41     private final OnPreDrawListener mStartPositionUpdater = new StartPositionUpdater();
42     private MoveScreensaverRunnable mPositionUpdater;
43 
44     private String mDateFormat;
45     private String mDateFormatForAccessibility;
46 
47     private View mContentView;
48     private View mMainClockView;
49     private TextClock mDigitalClock;
50     private AnalogClock mAnalogClock;
51 
52     /* Register ContentObserver to see alarm changes for pre-L */
53     private final ContentObserver mSettingsContentObserver =
54             Utils.isLOrLater() ? null : new ContentObserver(new Handler()) {
55                 @Override
56                 public void onChange(boolean selfChange) {
57                     Utils.refreshAlarm(Screensaver.this, mContentView);
58                 }
59             };
60 
61     // Runs every midnight or when the time changes and refreshes the date.
62     private final Runnable mMidnightUpdater = new Runnable() {
63         @Override
64         public void run() {
65             Utils.updateDate(mDateFormat, mDateFormatForAccessibility, mContentView);
66         }
67     };
68 
69     /**
70      * Receiver to alarm clock changes.
71      */
72     private final BroadcastReceiver mAlarmChangedReceiver = new BroadcastReceiver() {
73         @Override
74         public void onReceive(Context context, Intent intent) {
75             Utils.refreshAlarm(Screensaver.this, mContentView);
76         }
77     };
78 
79     @Override
onCreate()80     public void onCreate() {
81         LOGGER.v("Screensaver created");
82 
83         setTheme(R.style.Theme_DeskClock);
84         super.onCreate();
85 
86         mDateFormat = getString(R.string.abbrev_wday_month_day_no_year);
87         mDateFormatForAccessibility = getString(R.string.full_wday_month_day_no_year);
88     }
89 
90     @Override
onAttachedToWindow()91     public void onAttachedToWindow() {
92         LOGGER.v("Screensaver attached to window");
93         super.onAttachedToWindow();
94 
95         setContentView(R.layout.desk_clock_saver);
96 
97         mContentView = findViewById(R.id.saver_container);
98         mMainClockView = mContentView.findViewById(R.id.main_clock);
99         mDigitalClock = (TextClock) mMainClockView.findViewById(R.id.digital_clock);
100         mAnalogClock = (AnalogClock) mMainClockView.findViewById(R.id.analog_clock);
101 
102         setClockStyle();
103         Utils.setClockIconTypeface(mContentView);
104         Utils.setTimeFormat(mDigitalClock, false);
105         mAnalogClock.enableSeconds(false);
106 
107         mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
108                 | View.SYSTEM_UI_FLAG_IMMERSIVE
109                 | View.SYSTEM_UI_FLAG_FULLSCREEN
110                 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
111                 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
112 
113         mPositionUpdater = new MoveScreensaverRunnable(mContentView, mMainClockView);
114 
115         // We want the screen saver to exit upon user interaction.
116         setInteractive(false);
117         setFullscreen(true);
118 
119         // Setup handlers for time reference changes and date updates.
120         if (Utils.isLOrLater()) {
121             registerReceiver(mAlarmChangedReceiver,
122                     new IntentFilter(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED));
123         }
124 
125         if (mSettingsContentObserver != null) {
126             @SuppressWarnings("deprecation")
127             final Uri uri = Settings.System.getUriFor(Settings.System.NEXT_ALARM_FORMATTED);
128             getContentResolver().registerContentObserver(uri, false, mSettingsContentObserver);
129         }
130 
131         Utils.updateDate(mDateFormat, mDateFormatForAccessibility, mContentView);
132         Utils.refreshAlarm(this, mContentView);
133 
134         startPositionUpdater();
135         UiDataModel.getUiDataModel().addMidnightCallback(mMidnightUpdater, 100);
136     }
137 
138     @Override
onDetachedFromWindow()139     public void onDetachedFromWindow() {
140         LOGGER.v("Screensaver detached from window");
141         super.onDetachedFromWindow();
142 
143         if (mSettingsContentObserver != null) {
144             getContentResolver().unregisterContentObserver(mSettingsContentObserver);
145         }
146 
147         UiDataModel.getUiDataModel().removePeriodicCallback(mMidnightUpdater);
148         stopPositionUpdater();
149 
150         // Tear down handlers for time reference changes and date updates.
151         if (Utils.isLOrLater()) {
152             unregisterReceiver(mAlarmChangedReceiver);
153         }
154     }
155 
156     @Override
onConfigurationChanged(Configuration newConfig)157     public void onConfigurationChanged(Configuration newConfig) {
158         LOGGER.v("Screensaver configuration changed");
159         super.onConfigurationChanged(newConfig);
160 
161         startPositionUpdater();
162     }
163 
setClockStyle()164     private void setClockStyle() {
165         Utils.setScreensaverClockStyle(mDigitalClock, mAnalogClock);
166         final boolean dimNightMode = DataModel.getDataModel().getScreensaverNightModeOn();
167         Utils.dimClockView(dimNightMode, mMainClockView);
168         setScreenBright(!dimNightMode);
169     }
170 
171     /**
172      * The {@link #mContentView} will be drawn shortly. When that draw occurs, the position updater
173      * callback will also be executed to choose a random position for the time display as well as
174      * schedule future callbacks to move the time display each minute.
175      */
startPositionUpdater()176     private void startPositionUpdater() {
177         if (mContentView != null) {
178             mContentView.getViewTreeObserver().addOnPreDrawListener(mStartPositionUpdater);
179         }
180     }
181 
182     /**
183      * This activity is no longer in the foreground; position callbacks should be removed.
184      */
stopPositionUpdater()185     private void stopPositionUpdater() {
186         if (mContentView != null) {
187             mContentView.getViewTreeObserver().removeOnPreDrawListener(mStartPositionUpdater);
188         }
189         mPositionUpdater.stop();
190     }
191 
192     private final class StartPositionUpdater implements OnPreDrawListener {
193         /**
194          * This callback occurs after initial layout has completed. It is an appropriate place to
195          * select a random position for {@link #mMainClockView} and schedule future callbacks to update
196          * its position.
197          *
198          * @return {@code true} to continue with the drawing pass
199          */
200         @Override
onPreDraw()201         public boolean onPreDraw() {
202             if (mContentView.getViewTreeObserver().isAlive()) {
203                 // (Re)start the periodic position updater.
204                 mPositionUpdater.start();
205 
206                 // This listener must now be removed to avoid starting the position updater again.
207                 mContentView.getViewTreeObserver().removeOnPreDrawListener(mStartPositionUpdater);
208             }
209             return true;
210         }
211     }
212 }
213