• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.customization.model.clock;
2 
3 import android.content.ContentResolver;
4 import android.content.Context;
5 import android.content.pm.ApplicationInfo;
6 import android.content.pm.PackageManager;
7 import android.content.pm.PackageManager.NameNotFoundException;
8 import android.content.pm.ProviderInfo;
9 import android.database.Cursor;
10 import android.net.Uri;
11 import android.os.Handler;
12 import android.os.Looper;
13 import android.text.TextUtils;
14 import android.util.Log;
15 
16 import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
17 import com.android.customization.model.clock.Clockface.Builder;
18 import com.android.wallpaper.R;
19 import com.android.wallpaper.asset.ContentUriAsset;
20 
21 import com.bumptech.glide.Glide;
22 import com.bumptech.glide.request.RequestOptions;
23 
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.concurrent.ExecutorService;
27 import java.util.concurrent.Executors;
28 
29 public class ContentProviderClockProvider implements ClockProvider {
30 
31     private static final String TAG = "ContentProviderClockProvider";
32     private static final ExecutorService sExecutorService = Executors.newSingleThreadExecutor();
33     private static final String LIST_OPTIONS = "list_options";
34     private static final String COL_TITLE = "title";
35     private static final String COL_ID = "id";
36     private static final String COL_THUMBNAIL = "thumbnail";
37     private static final String COL_PREVIEW = "preview";
38 
39     private final Context mContext;
40     private final ProviderInfo mProviderInfo;
41     private List<Clockface> mClocks;
42     private boolean mClockContentAvailable;
43 
ContentProviderClockProvider(Context context)44     public ContentProviderClockProvider(Context context) {
45         mContext = context;
46         String providerAuthority = mContext.getString(R.string.clocks_provider_authority);
47         // TODO: check permissions if needed
48         mProviderInfo = TextUtils.isEmpty(providerAuthority) ? null
49                 : mContext.getPackageManager().resolveContentProvider(providerAuthority,
50                         PackageManager.MATCH_SYSTEM_ONLY);
51 
52         if (TextUtils.isEmpty(mContext.getString(R.string.clocks_stub_package))) {
53             mClockContentAvailable = false;
54         } else {
55             try {
56                 ApplicationInfo applicationInfo = mContext.getPackageManager().getApplicationInfo(
57                         mContext.getString(R.string.clocks_stub_package),
58                         PackageManager.MATCH_SYSTEM_ONLY);
59                 mClockContentAvailable = applicationInfo != null;
60             } catch (NameNotFoundException e) {
61                 mClockContentAvailable = false;
62             }
63         }
64     }
65 
66     @Override
isAvailable()67     public boolean isAvailable() {
68         return mProviderInfo != null && mClockContentAvailable
69                 && (mClocks == null || !mClocks.isEmpty());
70     }
71 
72     @Override
fetch(OptionsFetchedListener<Clockface> callback, boolean reload)73     public void fetch(OptionsFetchedListener<Clockface> callback, boolean reload) {
74         if (!isAvailable()) {
75             if (callback != null) {
76                 callback.onError(null);
77             }
78             return;
79         }
80         if (mClocks != null && !reload) {
81             if (callback != null) {
82                 if (!mClocks.isEmpty()) {
83                     callback.onOptionsLoaded(mClocks);
84                 } else {
85                     callback.onError(null);
86                 }
87             }
88             return;
89         }
90         sExecutorService.submit(() -> {
91             Uri optionsUri = new Uri.Builder()
92                     .scheme(ContentResolver.SCHEME_CONTENT)
93                     .authority(mProviderInfo.authority)
94                     .appendPath(LIST_OPTIONS)
95                     .build();
96 
97             ContentResolver resolver = mContext.getContentResolver();
98 
99             List<Clockface> clockfaces = new ArrayList<>();
100             try (Cursor c = resolver.query(optionsUri, null, null, null, null)) {
101                 while (c != null && c.moveToNext()) {
102                     String id = c.getString(c.getColumnIndex(COL_ID));
103                     String title = c.getString(c.getColumnIndex(COL_TITLE));
104                     String thumbnailUri = c.getString(c.getColumnIndex(COL_THUMBNAIL));
105                     String previewUri = c.getString(c.getColumnIndex(COL_PREVIEW));
106                     Uri thumbnail = Uri.parse(thumbnailUri);
107                     Uri preview = Uri.parse(previewUri);
108 
109                     Clockface.Builder builder = new Builder();
110                     builder.setId(id).setTitle(title)
111                             .setThumbnail(new ContentUriAsset(mContext, thumbnail,
112                                     RequestOptions.fitCenterTransform()))
113                             .setPreview(new ContentUriAsset(mContext, preview,
114                                     RequestOptions.fitCenterTransform()));
115                     clockfaces.add(builder.build());
116                 }
117                 Glide.get(mContext).clearDiskCache();
118             } catch (Exception e) {
119                 clockfaces = null;
120                 Log.e(TAG, "Failed to query clock face options.", e);
121             }
122             final List<Clockface> clockfaceList = clockfaces;
123             new Handler(Looper.getMainLooper()).post(() -> {
124                 mClocks = clockfaceList;
125                 if (callback != null) {
126                     if (!mClocks.isEmpty()) {
127                         callback.onOptionsLoaded(mClocks);
128                     } else {
129                         callback.onError(null);
130                     }
131                 }
132             });
133         });
134     }
135 }
136