1 /*
2  * Copyright (C) 2018 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.homepage.contextualcards;
18 
19 import static com.android.settings.homepage.contextualcards.ContextualCardsAdapter.SPAN_COUNT;
20 
21 import android.app.settings.SettingsEnums;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.os.Build;
27 import android.os.Bundle;
28 import android.util.Log;
29 import android.view.LayoutInflater;
30 import android.view.View;
31 import android.view.ViewGroup;
32 
33 import androidx.annotation.VisibleForTesting;
34 import androidx.loader.app.LoaderManager;
35 import androidx.recyclerview.widget.GridLayoutManager;
36 import androidx.recyclerview.widget.ItemTouchHelper;
37 
38 import com.android.settings.R;
39 import com.android.settings.core.InstrumentedFragment;
40 import com.android.settings.homepage.contextualcards.slices.BluetoothUpdateWorker;
41 import com.android.settings.homepage.contextualcards.slices.SwipeDismissalDelegate;
42 import com.android.settings.overlay.FeatureFactory;
43 
44 public class ContextualCardsFragment extends InstrumentedFragment implements
45         FocusRecyclerView.FocusListener {
46 
47     private static final String TAG = "ContextualCardsFragment";
48     private static final boolean DEBUG = Build.IS_DEBUGGABLE;
49 
50     @VisibleForTesting
51     static boolean sRestartLoaderNeeded;
52 
53     @VisibleForTesting
54     BroadcastReceiver mKeyEventReceiver;
55     @VisibleForTesting
56     BroadcastReceiver mScreenOffReceiver;
57 
58     private FocusRecyclerView mCardsContainer;
59     private GridLayoutManager mLayoutManager;
60     private ContextualCardsAdapter mContextualCardsAdapter;
61     private ContextualCardManager mContextualCardManager;
62     private ItemTouchHelper mItemTouchHelper;
63 
64     @Override
onCreate(Bundle savedInstanceState)65     public void onCreate(Bundle savedInstanceState) {
66         super.onCreate(savedInstanceState);
67         final Context context = getContext();
68         if (savedInstanceState == null) {
69             FeatureFactory.getFeatureFactory().getSlicesFeatureProvider().newUiSession();
70             BluetoothUpdateWorker.initLocalBtManager(getContext());
71         }
72         mContextualCardManager = new ContextualCardManager(context, getSettingsLifecycle(),
73                 savedInstanceState);
74         mKeyEventReceiver = new KeyEventReceiver();
75     }
76 
77     @Override
onStart()78     public void onStart() {
79         super.onStart();
80         registerScreenOffReceiver();
81         registerKeyEventReceiver();
82         mContextualCardManager.loadContextualCards(LoaderManager.getInstance(this),
83                 sRestartLoaderNeeded);
84         sRestartLoaderNeeded = false;
85     }
86 
87     @Override
onStop()88     public void onStop() {
89         unregisterKeyEventReceiver();
90         super.onStop();
91     }
92 
93     @Override
onDestroy()94     public void onDestroy() {
95         unregisterScreenOffReceiver();
96         super.onDestroy();
97     }
98 
99     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)100     public View onCreateView(LayoutInflater inflater, ViewGroup container,
101             Bundle savedInstanceState) {
102         final Context context = getContext();
103         final View rootView = inflater.inflate(R.layout.settings_homepage, container, false);
104         mCardsContainer = rootView.findViewById(R.id.card_container);
105         mLayoutManager = new GridLayoutManager(getActivity(), SPAN_COUNT,
106                 GridLayoutManager.VERTICAL, false /* reverseLayout */);
107         mCardsContainer.setLayoutManager(mLayoutManager);
108         mContextualCardsAdapter = new ContextualCardsAdapter(context, this /* lifecycleOwner */,
109                 mContextualCardManager);
110         mCardsContainer.setItemAnimator(null);
111         mCardsContainer.setAdapter(mContextualCardsAdapter);
112         mContextualCardManager.setListener(mContextualCardsAdapter);
113         mCardsContainer.setListener(this);
114         mItemTouchHelper = new ItemTouchHelper(new SwipeDismissalDelegate(mContextualCardsAdapter));
115         mItemTouchHelper.attachToRecyclerView(mCardsContainer);
116 
117         return rootView;
118     }
119 
120     @Override
onWindowFocusChanged(boolean hasWindowFocus)121     public void onWindowFocusChanged(boolean hasWindowFocus) {
122         mContextualCardManager.onWindowFocusChanged(hasWindowFocus);
123     }
124 
125     @Override
getMetricsCategory()126     public int getMetricsCategory() {
127         return SettingsEnums.SETTINGS_HOMEPAGE;
128     }
129 
registerKeyEventReceiver()130     private void registerKeyEventReceiver() {
131         getActivity().registerReceiver(mKeyEventReceiver,
132                 new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), Context.RECEIVER_EXPORTED);
133     }
134 
unregisterKeyEventReceiver()135     private void unregisterKeyEventReceiver() {
136         getActivity().unregisterReceiver(mKeyEventReceiver);
137     }
138 
registerScreenOffReceiver()139     private void registerScreenOffReceiver() {
140         if (mScreenOffReceiver == null) {
141             mScreenOffReceiver = new ScreenOffReceiver();
142             getActivity().registerReceiver(mScreenOffReceiver,
143                     new IntentFilter(Intent.ACTION_SCREEN_OFF));
144         }
145     }
146 
unregisterScreenOffReceiver()147     private void unregisterScreenOffReceiver() {
148         if (mScreenOffReceiver != null) {
149             getActivity().unregisterReceiver(mScreenOffReceiver);
150             mScreenOffReceiver = null;
151         }
152     }
153 
resetSession(Context context)154     private void resetSession(Context context) {
155         sRestartLoaderNeeded = true;
156         unregisterScreenOffReceiver();
157         FeatureFactory.getFeatureFactory().getSlicesFeatureProvider().newUiSession();
158     }
159 
160     /**
161      * Receiver for updating UI session when home key or recent app key is pressed.
162      */
163     @VisibleForTesting
164     class KeyEventReceiver extends BroadcastReceiver {
165 
166         private static final String KEY_REASON = "reason";
167         private static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
168         private static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
169 
170         @Override
onReceive(Context context, Intent intent)171         public void onReceive(Context context, Intent intent) {
172             if (intent == null || !Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
173                 return;
174             }
175 
176             final String reason = intent.getStringExtra(KEY_REASON);
177             if (!SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)
178                     && !SYSTEM_DIALOG_REASON_HOME_KEY.equals(reason)) {
179                 return;
180             }
181 
182             if (DEBUG) {
183                 Log.d(TAG, "key pressed = " + reason);
184             }
185             resetSession(context);
186         }
187     }
188 
189     /**
190      * Receiver for updating UI session when screen is turned off.
191      */
192     @VisibleForTesting
193     class ScreenOffReceiver extends BroadcastReceiver {
194 
195         @Override
onReceive(Context context, Intent intent)196         public void onReceive(Context context, Intent intent) {
197             if (intent == null || !Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
198                 return;
199             }
200 
201             if (DEBUG) {
202                 Log.d(TAG, "screen off");
203             }
204             resetSession(context);
205         }
206     }
207 }
208