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 android.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.content.res.Configuration; 24 import android.graphics.Point; 25 import android.hardware.display.DisplayManager; 26 import android.hardware.display.DisplayManager.DisplayListener; 27 import android.os.Handler; 28 import android.util.Log; 29 import android.view.Display; 30 import android.view.WindowManager; 31 32 import com.android.launcher3.MainThreadExecutor; 33 34 import java.util.function.Consumer; 35 36 /** 37 * {@link BroadcastReceiver} which watches configuration changes and 38 * notifies the callback in case changes which affect the device profile occur. 39 */ 40 public class ConfigMonitor extends BroadcastReceiver implements DisplayListener { 41 42 private static final String TAG = "ConfigMonitor"; 43 44 private final Point mTmpPoint1 = new Point(); 45 private final Point mTmpPoint2 = new Point(); 46 47 private final Context mContext; 48 private final float mFontScale; 49 private final int mDensity; 50 51 private final int mDisplayId; 52 private final Point mRealSize; 53 private final Point mSmallestSize, mLargestSize; 54 55 private Consumer<Context> mCallback; 56 ConfigMonitor(Context context, Consumer<Context> callback)57 public ConfigMonitor(Context context, Consumer<Context> callback) { 58 mContext = context; 59 60 Configuration config = context.getResources().getConfiguration(); 61 mFontScale = config.fontScale; 62 mDensity = config.densityDpi; 63 64 Display display = getDefaultDisplay(context); 65 mDisplayId = display.getDisplayId(); 66 67 mRealSize = new Point(); 68 display.getRealSize(mRealSize); 69 70 mSmallestSize = new Point(); 71 mLargestSize = new Point(); 72 display.getCurrentSizeRange(mSmallestSize, mLargestSize); 73 74 mCallback = callback; 75 76 // Listen for configuration change 77 mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED)); 78 79 // Listen for display manager change 80 mContext.getSystemService(DisplayManager.class) 81 .registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper())); 82 } 83 84 @Override onReceive(Context context, Intent intent)85 public void onReceive(Context context, Intent intent) { 86 Configuration config = context.getResources().getConfiguration(); 87 if (mFontScale != config.fontScale || mDensity != config.densityDpi) { 88 Log.d(TAG, "Configuration changed."); 89 notifyChange(); 90 } 91 } 92 93 @Override onDisplayAdded(int displayId)94 public void onDisplayAdded(int displayId) { } 95 96 @Override onDisplayRemoved(int displayId)97 public void onDisplayRemoved(int displayId) { } 98 99 @Override onDisplayChanged(int displayId)100 public void onDisplayChanged(int displayId) { 101 if (displayId != mDisplayId) { 102 return; 103 } 104 Display display = getDefaultDisplay(mContext); 105 display.getRealSize(mTmpPoint1); 106 107 if (!mRealSize.equals(mTmpPoint1) && !mRealSize.equals(mTmpPoint1.y, mTmpPoint1.x)) { 108 Log.d(TAG, String.format("Display size changed from %s to %s", mRealSize, mTmpPoint1)); 109 notifyChange(); 110 return; 111 } 112 113 display.getCurrentSizeRange(mTmpPoint1, mTmpPoint2); 114 if (!mSmallestSize.equals(mTmpPoint1) || !mLargestSize.equals(mTmpPoint2)) { 115 Log.d(TAG, String.format("Available size changed from [%s, %s] to [%s, %s]", 116 mSmallestSize, mLargestSize, mTmpPoint1, mTmpPoint2)); 117 notifyChange(); 118 } 119 } 120 notifyChange()121 private synchronized void notifyChange() { 122 if (mCallback != null) { 123 Consumer<Context> callback = mCallback; 124 mCallback = null; 125 new MainThreadExecutor().execute(() -> callback.accept(mContext)); 126 } 127 } 128 getDefaultDisplay(Context context)129 private Display getDefaultDisplay(Context context) { 130 return context.getSystemService(WindowManager.class).getDefaultDisplay(); 131 } 132 unregister()133 public void unregister() { 134 try { 135 mContext.unregisterReceiver(this); 136 mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this); 137 } catch (Exception e) { 138 Log.e(TAG, "Failed to unregister config monitor", e); 139 } 140 } 141 } 142