1 /*
2  * Copyright (C) 2013 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.documentsui;
18 
19 import android.app.ActivityManager;
20 import android.app.Application;
21 import android.content.BroadcastReceiver;
22 import android.content.ContentProviderClient;
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.om.OverlayManager;
28 import android.net.Uri;
29 import android.os.RemoteException;
30 import android.text.format.DateUtils;
31 import android.util.Log;
32 
33 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
34 
35 import com.android.documentsui.base.Lookup;
36 import com.android.documentsui.base.UserId;
37 import com.android.documentsui.clipping.ClipStorage;
38 import com.android.documentsui.clipping.ClipStore;
39 import com.android.documentsui.clipping.DocumentClipper;
40 import com.android.documentsui.queries.SearchHistoryManager;
41 import com.android.documentsui.roots.ProvidersCache;
42 import com.android.documentsui.theme.ThemeOverlayManager;
43 
44 import com.google.common.collect.Lists;
45 
46 import java.util.List;
47 
48 public class DocumentsApplication extends Application {
49     private static final String TAG = "DocumentsApplication";
50     private static final long PROVIDER_ANR_TIMEOUT = 20 * DateUtils.SECOND_IN_MILLIS;
51 
52     private static final List<String> PACKAGE_FILTER_ACTIONS = Lists.newArrayList(
53             Intent.ACTION_PACKAGE_ADDED,
54             Intent.ACTION_PACKAGE_CHANGED,
55             Intent.ACTION_PACKAGE_REMOVED,
56             Intent.ACTION_PACKAGE_DATA_CLEARED
57     );
58 
59     private static final List<String> MANAGED_PROFILE_FILTER_ACTIONS = Lists.newArrayList(
60             Intent.ACTION_MANAGED_PROFILE_ADDED,
61             Intent.ACTION_MANAGED_PROFILE_REMOVED,
62             Intent.ACTION_MANAGED_PROFILE_UNLOCKED,
63             Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE
64     );
65 
66     private ProvidersCache mProviders;
67     private ThumbnailCache mThumbnailCache;
68     private ClipStorage mClipStore;
69     private DocumentClipper mClipper;
70     private DragAndDropManager mDragAndDropManager;
71     private UserIdManager mUserIdManager;
72     private Lookup<String, String> mFileTypeLookup;
73 
getProvidersCache(Context context)74     public static ProvidersCache getProvidersCache(Context context) {
75         return ((DocumentsApplication) context.getApplicationContext()).mProviders;
76     }
77 
getThumbnailCache(Context context)78     public static ThumbnailCache getThumbnailCache(Context context) {
79         final DocumentsApplication app = (DocumentsApplication) context.getApplicationContext();
80         return app.mThumbnailCache;
81     }
82 
acquireUnstableProviderOrThrow( ContentResolver resolver, String authority)83     public static ContentProviderClient acquireUnstableProviderOrThrow(
84             ContentResolver resolver, String authority) throws RemoteException {
85         final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
86                 authority);
87         if (client == null) {
88             throw new RemoteException("Failed to acquire provider for " + authority);
89         }
90         client.setDetectNotResponding(PROVIDER_ANR_TIMEOUT);
91         return client;
92     }
93 
getDocumentClipper(Context context)94     public static DocumentClipper getDocumentClipper(Context context) {
95         return ((DocumentsApplication) context.getApplicationContext()).mClipper;
96     }
97 
getClipStore(Context context)98     public static ClipStore getClipStore(Context context) {
99         return ((DocumentsApplication) context.getApplicationContext()).mClipStore;
100     }
101 
getUserIdManager(Context context)102     public static UserIdManager getUserIdManager(Context context) {
103         return ((DocumentsApplication) context.getApplicationContext()).mUserIdManager;
104     }
105 
getDragAndDropManager(Context context)106     public static DragAndDropManager getDragAndDropManager(Context context) {
107         return ((DocumentsApplication) context.getApplicationContext()).mDragAndDropManager;
108     }
109 
getFileTypeLookup(Context context)110     public static Lookup<String, String> getFileTypeLookup(Context context) {
111         return ((DocumentsApplication) context.getApplicationContext()).mFileTypeLookup;
112     }
113 
onApplyOverlayFinish(boolean result)114     private void onApplyOverlayFinish(boolean result) {
115         Log.d(TAG, "OverlayManager.setEnabled() result: " + result);
116     }
117 
118     @Override
onCreate()119     public void onCreate() {
120         super.onCreate();
121 
122         final ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
123         final OverlayManager om = getSystemService(OverlayManager.class);
124         final int memoryClassBytes = am.getMemoryClass() * 1024 * 1024;
125 
126         if (om != null) {
127             new ThemeOverlayManager(om, getPackageName()).applyOverlays(this, true,
128                     this::onApplyOverlayFinish);
129         } else {
130             Log.w(TAG, "Can't obtain OverlayManager from System Service!");
131         }
132 
133         mUserIdManager = UserIdManager.create(this);
134 
135         mProviders = new ProvidersCache(this, mUserIdManager);
136         mProviders.updateAsync(/* forceRefreshAll= */ false, /* callback= */  null);
137 
138         mThumbnailCache = new ThumbnailCache(memoryClassBytes / 4);
139 
140         mClipStore = new ClipStorage(
141                 ClipStorage.prepareStorage(getCacheDir()),
142                 getSharedPreferences(ClipStorage.PREF_NAME, 0));
143         mClipper = DocumentClipper.create(this, mClipStore);
144 
145         mDragAndDropManager = DragAndDropManager.create(this, mClipper);
146 
147         mFileTypeLookup = new FileTypeMap(this);
148 
149         final IntentFilter packageFilter = new IntentFilter();
150         for (String packageAction : PACKAGE_FILTER_ACTIONS) {
151             packageFilter.addAction(packageAction);
152         }
153         packageFilter.addDataScheme("package");
154         registerReceiver(mCacheReceiver, packageFilter);
155 
156         final IntentFilter localeFilter = new IntentFilter();
157         localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
158         registerReceiver(mCacheReceiver, localeFilter);
159 
160         final IntentFilter managedProfileFilter = new IntentFilter();
161         for (String managedProfileAction : MANAGED_PROFILE_FILTER_ACTIONS) {
162             managedProfileFilter.addAction(managedProfileAction);
163         }
164         registerReceiver(mCacheReceiver, managedProfileFilter);
165 
166         SearchHistoryManager.getInstance(getApplicationContext());
167     }
168 
169     @Override
onTrimMemory(int level)170     public void onTrimMemory(int level) {
171         super.onTrimMemory(level);
172 
173         mThumbnailCache.onTrimMemory(level);
174     }
175 
176     private BroadcastReceiver mCacheReceiver = new BroadcastReceiver() {
177         @Override
178         public void onReceive(Context context, Intent intent) {
179             final Uri data = intent.getData();
180             final String action = intent.getAction();
181             if (PACKAGE_FILTER_ACTIONS.contains(action) && data != null) {
182                 final String packageName = data.getSchemeSpecificPart();
183                 mProviders.updatePackageAsync(UserId.DEFAULT_USER, packageName);
184             } else if (MANAGED_PROFILE_FILTER_ACTIONS.contains(action)) {
185                 // After we have reloaded roots. Resend the broadcast locally so the other
186                 // components can reload properly after roots are updated.
187                 mProviders.updateAsync(/* forceRefreshAll= */ true,
188                         () -> LocalBroadcastManager.getInstance(context).sendBroadcast(intent));
189             } else {
190                 mProviders.updateAsync(/* forceRefreshAll= */ true, /* callback= */ null);
191             }
192         }
193     };
194 }
195