1547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes/* 2547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes * Copyright (C) 2015 The Android Open Source Project 3547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes * 4547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 5547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes * you may not use this file except in compliance with the License. 6547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes * You may obtain a copy of the License at 7547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes * 8547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes * http://www.apache.org/licenses/LICENSE-2.0 9547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes * 10547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes * Unless required by applicable law or agreed to in writing, software 11547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes * distributed under the License is distributed on an "AS IS" BASIS, 12547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes * See the License for the specific language governing permissions and 14547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes * limitations under the License 15547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes */ 16547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes 17547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banespackage android.support.v7.app; 18547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes 19547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banesimport android.content.Context; 20ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banesimport android.content.res.Configuration; 21ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banesimport android.content.res.Resources; 22ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banesimport android.os.Bundle; 2366698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banesimport android.support.v7.view.SupportActionModeWrapper; 24ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banesimport android.util.Log; 25547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banesimport android.view.ActionMode; 26547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banesimport android.view.Window; 27547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes 28547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banesclass AppCompatDelegateImplV14 extends AppCompatDelegateImplV11 { 29547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes 30ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes private static final String KEY_LOCAL_NIGHT_MODE = "appcompat:local_night_mode"; 31ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes 32ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes private static TwilightManager sTwilightManager; 33ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes 34ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes @NightMode 35ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes private int mLocalNightMode = MODE_NIGHT_UNSPECIFIED; 36ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes private boolean mApplyDayNightCalled; 37ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes 38547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes private boolean mHandleNativeActionModes = true; // defaults to true 39547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes 40547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes AppCompatDelegateImplV14(Context context, Window window, AppCompatCallback callback) { 41547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes super(context, window, callback); 42547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes } 43547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes 44547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes @Override 45ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes public void onCreate(Bundle savedInstanceState) { 46ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes super.onCreate(savedInstanceState); 47ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes 48ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes if (savedInstanceState != null && mLocalNightMode == MODE_NIGHT_UNSPECIFIED) { 49ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes // If we have a icicle and we haven't had a local night mode set yet, try and read 50ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes // it from the icicle 51ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes mLocalNightMode = savedInstanceState.getInt(KEY_LOCAL_NIGHT_MODE, 52ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes MODE_NIGHT_UNSPECIFIED); 53ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes } 54ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes } 55ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes 56ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes @Override 57547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes Window.Callback wrapWindowCallback(Window.Callback callback) { 58547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes // Override the window callback so that we can intercept onWindowStartingActionMode() 59547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes // calls 60547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes return new AppCompatWindowCallbackV14(callback); 61547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes } 62547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes 63547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes @Override 64547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes public void setHandleNativeActionModesEnabled(boolean enabled) { 65547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes mHandleNativeActionModes = enabled; 66547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes } 67547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes 68547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes @Override 69547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes public boolean isHandleNativeActionModesEnabled() { 70547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes return mHandleNativeActionModes; 71547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes } 72547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes 73ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes @Override 74ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes public boolean applyDayNight() { 75ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes mApplyDayNightCalled = true; 76b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes 77b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes final int modeToApply = mapNightMode(mLocalNightMode == MODE_NIGHT_UNSPECIFIED 78b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes ? getDefaultNightMode() 79b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes : mLocalNightMode); 80b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes 81b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes if (modeToApply != MODE_NIGHT_FOLLOW_SYSTEM) { 82b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes // If we're not following the system, we many need to update the configuration 83b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes return updateConfigurationForNightMode(modeToApply); 84b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes } 85b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes return false; 86ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes } 87ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes 88ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes @Override 89ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes public void setLocalNightMode(@NightMode final int mode) { 90ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes switch (mode) { 91ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes case MODE_NIGHT_AUTO: 92ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes case MODE_NIGHT_NO: 93ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes case MODE_NIGHT_YES: 94fe20eed11b68512fcd16039f8f94c1909f2925f8Chris Banes case MODE_NIGHT_FOLLOW_SYSTEM: 95ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes if (mLocalNightMode != mode) { 96ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes mLocalNightMode = mode; 97ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes if (mApplyDayNightCalled) { 98ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes // If we've already applied day night, re-apply since we won't be 99ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes // called again 100ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes applyDayNight(); 101ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes } 102ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes } 103ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes break; 104ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes default: 105ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes Log.d(TAG, "setLocalNightMode() called with an unknown mode"); 106ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes break; 107ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes } 108ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes } 109ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes 110b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes @ApplyableNightMode 111b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes int mapNightMode(@NightMode final int mode) { 112fe20eed11b68512fcd16039f8f94c1909f2925f8Chris Banes switch (mode) { 113fe20eed11b68512fcd16039f8f94c1909f2925f8Chris Banes case MODE_NIGHT_AUTO: 114fe20eed11b68512fcd16039f8f94c1909f2925f8Chris Banes return getTwilightManager().isNight() ? MODE_NIGHT_YES : MODE_NIGHT_NO; 115b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes case MODE_NIGHT_UNSPECIFIED: 116b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes // If we don't have a mode specified, just let the system handle it 117b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes return MODE_NIGHT_FOLLOW_SYSTEM; 118fe20eed11b68512fcd16039f8f94c1909f2925f8Chris Banes default: 119b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes return mode; 120fe20eed11b68512fcd16039f8f94c1909f2925f8Chris Banes } 121fe20eed11b68512fcd16039f8f94c1909f2925f8Chris Banes } 122fe20eed11b68512fcd16039f8f94c1909f2925f8Chris Banes 123ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes @Override 124ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes public void onSaveInstanceState(Bundle outState) { 125ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes super.onSaveInstanceState(outState); 126ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes 127ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes if (mLocalNightMode != MODE_NIGHT_UNSPECIFIED) { 128ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes // If we have a local night mode set, save it 129ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes outState.putInt(KEY_LOCAL_NIGHT_MODE, mLocalNightMode); 130ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes } 131ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes } 132ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes 133ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes /** 134ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes * Updates the {@link Resources} configuration {@code uiMode} with the 135ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes * chosen {@code UI_MODE_NIGHT} value. 136ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes */ 137b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes private boolean updateConfigurationForNightMode(@ApplyableNightMode final int mode) { 138ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes final Resources res = mContext.getResources(); 139ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes final Configuration conf = res.getConfiguration(); 140ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes final int currentNightMode = conf.uiMode & Configuration.UI_MODE_NIGHT_MASK; 141ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes 142b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes final int newNightMode = (mode == MODE_NIGHT_YES) 143b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes ? Configuration.UI_MODE_NIGHT_YES 144b9579b37d2c3240b4bdaf44978b19d87ec9d3e59Chris Banes : Configuration.UI_MODE_NIGHT_NO; 145ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes 146ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes if (currentNightMode != newNightMode) { 1479800bd0698800fa28f9b95e05e2f4f89d31ff655Chris Banes final Configuration newConf = new Configuration(conf); 1489800bd0698800fa28f9b95e05e2f4f89d31ff655Chris Banes newConf.uiMode = (newConf.uiMode & ~Configuration.UI_MODE_NIGHT_MASK) | newNightMode; 1499800bd0698800fa28f9b95e05e2f4f89d31ff655Chris Banes res.updateConfiguration(newConf, null); 150ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes return true; 151ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes } 152ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes return false; 153ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes } 154ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes 155ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes private TwilightManager getTwilightManager() { 156ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes if (sTwilightManager == null) { 157ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes sTwilightManager = new TwilightManager(mContext.getApplicationContext()); 158ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes } 159ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes return sTwilightManager; 160ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes } 161ff7f258a9b0781de5c6325358c7075128bf05a91Chris Banes 162547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes class AppCompatWindowCallbackV14 extends AppCompatWindowCallbackBase { 163547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes AppCompatWindowCallbackV14(Window.Callback callback) { 164547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes super(callback); 165547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes } 166547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes 167547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes @Override 168547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { 169547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes // We wrap in a support action mode on v14+ if enabled 170f5829201b976fc650789accc22f325a2541ed478Chris Banes if (isHandleNativeActionModesEnabled()) { 171547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes return startAsSupportActionMode(callback); 172547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes } 173547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes // Else, let the call fall through to the wrapped callback 174547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes return super.onWindowStartingActionMode(callback); 175547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes } 176547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes 177547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes /** 178547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes * Wrap the framework {@link ActionMode.Callback} in a support action mode and 179547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes * let AppCompat display it. 180547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes */ 181547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes final ActionMode startAsSupportActionMode(ActionMode.Callback callback) { 182515e5121889e182cba12ce560493834470b8a42bChris Banes // Wrap the callback as a v7 ActionMode.Callback 183515e5121889e182cba12ce560493834470b8a42bChris Banes final SupportActionModeWrapper.CallbackWrapper callbackWrapper 184515e5121889e182cba12ce560493834470b8a42bChris Banes = new SupportActionModeWrapper.CallbackWrapper(mContext, callback); 185515e5121889e182cba12ce560493834470b8a42bChris Banes 186515e5121889e182cba12ce560493834470b8a42bChris Banes // Try and start a support action mode using the wrapped callback 187515e5121889e182cba12ce560493834470b8a42bChris Banes final android.support.v7.view.ActionMode supportActionMode 188515e5121889e182cba12ce560493834470b8a42bChris Banes = startSupportActionMode(callbackWrapper); 189547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes 190547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes if (supportActionMode != null) { 191547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes // If we received a support action mode, wrap and return it 19233556e6b972f1aa8126a88605b455afd6d6ee0bdAdam Powell return callbackWrapper.getActionModeWrapper(supportActionMode); 193547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes } 194547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes return null; 195547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes } 196547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes } 197547a80d347e0dc9751bbe3cf91f947477145bdbaChris Banes} 198