1 package com.android.launcher3.util;
2 
3 /**
4  * Copyright (C) 2015 The Android Open Source Project
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
20 
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.content.res.Configuration;
26 import android.graphics.Point;
27 import android.util.Log;
28 
29 import java.util.function.Consumer;
30 
31 /**
32  * {@link BroadcastReceiver} which watches configuration changes and
33  * notifies the callback in case changes which affect the device profile occur.
34  */
35 public class ConfigMonitor extends BroadcastReceiver implements
36         DefaultDisplay.DisplayInfoChangeListener {
37 
38     private static final String TAG = "ConfigMonitor";
39 
40     private final Point mTmpPoint1 = new Point();
41     private final Point mTmpPoint2 = new Point();
42 
43     private final Context mContext;
44     private final float mFontScale;
45     private final int mDensity;
46 
47     private final int mDisplayId;
48     private final Point mRealSize;
49     private final Point mSmallestSize, mLargestSize;
50 
51     private Consumer<Context> mCallback;
52 
ConfigMonitor(Context context, Consumer<Context> callback)53     public ConfigMonitor(Context context, Consumer<Context> callback) {
54         mContext = context;
55 
56         Configuration config = context.getResources().getConfiguration();
57         mFontScale = config.fontScale;
58         mDensity = config.densityDpi;
59 
60         DefaultDisplay display = DefaultDisplay.INSTANCE.get(context);
61         display.addChangeListener(this);
62         DefaultDisplay.Info displayInfo = display.getInfo();
63         mDisplayId = displayInfo.id;
64 
65         mRealSize = new Point(displayInfo.realSize);
66         mSmallestSize = new Point(displayInfo.smallestSize);
67         mLargestSize = new Point(displayInfo.largestSize);
68 
69         mCallback = callback;
70 
71         // Listen for configuration change
72         mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
73     }
74 
75     @Override
onReceive(Context context, Intent intent)76     public void onReceive(Context context, Intent intent) {
77         Configuration config = context.getResources().getConfiguration();
78         if (mFontScale != config.fontScale || mDensity != config.densityDpi) {
79             Log.d(TAG, "Configuration changed.");
80             notifyChange();
81         }
82     }
83 
84     @Override
onDisplayInfoChanged(DefaultDisplay.Info info, int flags)85     public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
86         if (info.id != mDisplayId) {
87             return;
88         }
89         mTmpPoint1.set(info.realSize.x, info.realSize.y);
90         if (!mRealSize.equals(mTmpPoint1) && !mRealSize.equals(mTmpPoint1.y, mTmpPoint1.x)) {
91             Log.d(TAG, String.format("Display size changed from %s to %s", mRealSize, mTmpPoint1));
92             notifyChange();
93             return;
94         }
95 
96         mTmpPoint1.set(info.smallestSize.x, info.smallestSize.y);
97         mTmpPoint2.set(info.largestSize.x, info.largestSize.y);
98         if (!mSmallestSize.equals(mTmpPoint1) || !mLargestSize.equals(mTmpPoint2)) {
99             Log.d(TAG, String.format("Available size changed from [%s, %s] to [%s, %s]",
100                     mSmallestSize, mLargestSize, mTmpPoint1, mTmpPoint2));
101             notifyChange();
102         }
103     }
104 
notifyChange()105     private synchronized void notifyChange() {
106         if (mCallback != null) {
107             Consumer<Context> callback = mCallback;
108             mCallback = null;
109             MAIN_EXECUTOR.execute(() -> callback.accept(mContext));
110         }
111     }
112 
unregister()113     public void unregister() {
114         try {
115             mContext.unregisterReceiver(this);
116             DefaultDisplay display = DefaultDisplay.INSTANCE.get(mContext);
117             display.removeChangeListener(this);
118         } catch (Exception e) {
119             Log.e(TAG, "Failed to unregister config monitor", e);
120         }
121     }
122 }
123