1 /* 2 * Copyright (C) 2019 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.settings.network; 18 19 import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY; 20 import static androidx.lifecycle.Lifecycle.Event.ON_START; 21 import static androidx.lifecycle.Lifecycle.Event.ON_STOP; 22 23 import android.content.Context; 24 import android.database.ContentObserver; 25 import android.net.Uri; 26 import android.os.Handler; 27 import android.os.Looper; 28 import android.provider.Settings; 29 30 import androidx.lifecycle.Lifecycle; 31 import androidx.lifecycle.LifecycleObserver; 32 import androidx.lifecycle.OnLifecycleEvent; 33 34 import java.util.concurrent.atomic.AtomicBoolean; 35 36 /** 37 * A listener for Settings.Global configuration change, with support of Lifecycle 38 */ 39 public abstract class GlobalSettingsChangeListener extends ContentObserver 40 implements LifecycleObserver, AutoCloseable { 41 42 /** 43 * Constructor 44 * 45 * @param context {@code Context} of this listener 46 * @param field field of Global Settings 47 */ GlobalSettingsChangeListener(Context context, String field)48 public GlobalSettingsChangeListener(Context context, String field) { 49 this(Looper.getMainLooper(), context, field); 50 } 51 52 /** 53 * Constructor 54 * 55 * @param looper {@code Looper} for processing callback 56 * @param context {@code Context} of this listener 57 * @param field field of Global Settings 58 */ GlobalSettingsChangeListener(Looper looper, Context context, String field)59 public GlobalSettingsChangeListener(Looper looper, Context context, String field) { 60 super(new Handler(looper)); 61 mContext = context; 62 mField = field; 63 mUri = Settings.Global.getUriFor(field); 64 mListening = new AtomicBoolean(false); 65 monitorUri(true); 66 } 67 68 private Context mContext; 69 private String mField; 70 private Uri mUri; 71 private AtomicBoolean mListening; 72 private Lifecycle mLifecycle; 73 74 /** 75 * Observed Settings got changed 76 */ onChanged(String field)77 public abstract void onChanged(String field); 78 79 /** 80 * Notify change of Globals.Setting based on given Lifecycle 81 * 82 * @param lifecycle life cycle to reference 83 */ notifyChangeBasedOn(Lifecycle lifecycle)84 public void notifyChangeBasedOn(Lifecycle lifecycle) { 85 if (mLifecycle != null) { 86 mLifecycle.removeObserver(this); 87 } 88 if (lifecycle != null) { 89 lifecycle.addObserver(this); 90 } 91 mLifecycle = lifecycle; 92 } 93 onChange(boolean selfChange)94 public void onChange(boolean selfChange) { 95 if (!mListening.get()) { 96 return; 97 } 98 onChanged(mField); 99 } 100 101 @OnLifecycleEvent(ON_START) onStart()102 void onStart() { 103 monitorUri(true); 104 } 105 106 @OnLifecycleEvent(ON_STOP) onStop()107 void onStop() { 108 monitorUri(false); 109 } 110 111 @OnLifecycleEvent(ON_DESTROY) onDestroy()112 void onDestroy() { 113 close(); 114 } 115 116 /** 117 * Implementation of AutoCloseable 118 */ close()119 public void close() { 120 monitorUri(false); 121 notifyChangeBasedOn(null); 122 } 123 monitorUri(boolean on)124 private void monitorUri(boolean on) { 125 if (!mListening.compareAndSet(!on, on)) { 126 return; 127 } 128 129 if (on) { 130 mContext.getContentResolver().registerContentObserver(mUri, false, this); 131 return; 132 } 133 134 mContext.getContentResolver().unregisterContentObserver(this); 135 } 136 } 137