Merge "Use ContentProvider for clockfaces" into ub-launcher3-master
diff --git a/res/values/override.xml b/res/values/override.xml
index de6e908..54bb551 100644
--- a/res/values/override.xml
+++ b/res/values/override.xml
@@ -18,6 +18,8 @@
 <resources>
     <string name="themes_stub_package" translatable="false"/>
     <string name="clocks_stub_package" translatable="false"/>
+    <!-- Authority of a provider in System UI that will provide preview info for available clockfaces. -->
+    <string name="clocks_provider_authority" translatable="false">com.android.keyguard.clock</string>
 
     <!--Name of metadata in the main launcher Activity which values contains the authority
         corresponding to a ContentProvider in launcher to provide available grids and
diff --git a/src/com/android/customization/model/clock/Clockface.java b/src/com/android/customization/model/clock/Clockface.java
index b72ef23..9eba02d 100644
--- a/src/com/android/customization/model/clock/Clockface.java
+++ b/src/com/android/customization/model/clock/Clockface.java
@@ -15,7 +15,8 @@
  */
 package com.android.customization.model.clock;
 
-import android.graphics.drawable.Drawable;
+import android.content.Context;
+import android.provider.Settings.Secure;
 import android.text.TextUtils;
 import android.view.View;
 import android.widget.ImageView;
@@ -23,15 +24,16 @@
 import com.android.customization.model.CustomizationManager;
 import com.android.customization.model.CustomizationOption;
 import com.android.wallpaper.R;
+import com.android.wallpaper.asset.Asset;
 
 public class Clockface implements CustomizationOption<Clockface> {
 
     private final String mTitle;
     private final String mId;
-    private final Drawable mPreview;
-    private final Drawable mThumbnail;
+    private final Asset mPreview;
+    private final Asset mThumbnail;
 
-    private Clockface(String title, String id, Drawable preview, Drawable thumbnail) {
+    private Clockface(String title, String id, Asset preview, Asset thumbnail) {
         mTitle = title;
         mId = id;
         mPreview = preview;
@@ -45,7 +47,9 @@
 
     @Override
     public void bindThumbnailTile(View view) {
-        ((ImageView) view.findViewById(R.id.clock_option_thumbnail)).setImageDrawable(mThumbnail);
+        ImageView thumbView = view.findViewById(R.id.clock_option_thumbnail);
+        mThumbnail.loadDrawableWithTransition(thumbView.getContext(), thumbView, 50, null,
+                thumbView.getResources().getColor(android.R.color.transparent, null));
     }
 
     @Override
@@ -61,7 +65,7 @@
         return R.layout.clock_option;
     }
 
-    public Drawable getPreviewDrawable() {
+    public Asset getPreviewAsset() {
         return mPreview;
     }
 
@@ -72,8 +76,8 @@
     public static class Builder {
         private String mTitle;
         private String mId;
-        private Drawable mPreview;
-        private Drawable mThumbnail;
+        private Asset mPreview;
+        private Asset mThumbnail;
 
         public Clockface build() {
             return new Clockface(mTitle, mId, mPreview, mThumbnail);
@@ -89,12 +93,12 @@
             return this;
         }
 
-        public Builder setPreview(Drawable preview) {
+        public Builder setPreview(Asset preview) {
             mPreview = preview;
             return this;
         }
 
-        public Builder setThumbnail(Drawable thumbnail) {
+        public Builder setThumbnail(Asset thumbnail) {
             mThumbnail = thumbnail;
             return this;
         }
diff --git a/src/com/android/customization/model/clock/ContentProviderClockProvider.java b/src/com/android/customization/model/clock/ContentProviderClockProvider.java
new file mode 100644
index 0000000..375f3c1
--- /dev/null
+++ b/src/com/android/customization/model/clock/ContentProviderClockProvider.java
@@ -0,0 +1,130 @@
+package com.android.customization.model.clock;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.text.TextUtils;
+
+import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
+import com.android.customization.model.clock.Clockface.Builder;
+import com.android.wallpaper.R;
+import com.android.wallpaper.asset.ContentUriAsset;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ContentProviderClockProvider implements ClockProvider {
+
+    private final Context mContext;
+    private final ProviderInfo mProviderInfo;
+    private List<Clockface> mClocks;
+
+    public ContentProviderClockProvider(Context context) {
+        mContext = context;
+        String providerAuthority = mContext.getString(R.string.clocks_provider_authority);
+        // TODO: check permissions if needed
+        mProviderInfo = TextUtils.isEmpty(providerAuthority) ? null
+                : mContext.getPackageManager().resolveContentProvider(providerAuthority,
+                        PackageManager.MATCH_SYSTEM_ONLY);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mProviderInfo != null;
+    }
+
+    @Override
+    public void fetch(OptionsFetchedListener<Clockface> callback, boolean reload) {
+        if (!isAvailable()) {
+            if (callback != null) {
+                callback.onOptionsLoaded(null);
+            }
+            return;
+        }
+        if (mClocks != null && !reload) {
+            if (callback != null) {
+                callback.onOptionsLoaded(mClocks);
+            }
+            return;
+        }
+        new ClocksFetchTask(mContext, mProviderInfo, options -> {
+            mClocks = options;
+            if (callback != null) {
+                callback.onOptionsLoaded(mClocks);
+            }
+        }).execute();
+    }
+
+    private static class ClocksFetchTask extends AsyncTask<Void, Void, List<Clockface>> {
+
+        private static final String LIST_OPTIONS = "list_options";
+
+        private static final String COL_NAME = "name";
+        private static final String COL_TITLE = "title";
+        private static final String COL_ID = "id";
+        private static final String COL_THUMBNAIL = "thumbnail";
+        private static final String COL_PREVIEW = "preview";
+
+        private final OptionsFetchedListener<Clockface> mCallback;
+        private Context mContext;
+        private final ProviderInfo mProviderInfo;
+
+        public ClocksFetchTask(Context context, ProviderInfo providerInfo,
+                OptionsFetchedListener<Clockface> callback) {
+            super();
+            mContext = context;
+            mProviderInfo = providerInfo;
+            mCallback = callback;
+        }
+
+        @Override
+        protected List<Clockface> doInBackground(Void... voids) {
+            Uri optionsUri = new Uri.Builder()
+                    .scheme(ContentResolver.SCHEME_CONTENT)
+                    .authority(mProviderInfo.authority)
+                    .appendPath(LIST_OPTIONS)
+                    .build();
+
+            ContentResolver resolver = mContext.getContentResolver();
+
+            List<Clockface> clockfaces = new ArrayList<>();
+            try (Cursor c = resolver.query(optionsUri, null, null, null, null)) {
+                while(c.moveToNext()) {
+                    String id = c.getString(c.getColumnIndex(COL_ID));
+                    String title = c.getString(c.getColumnIndex(COL_TITLE));
+                    String thumbnailUri = c.getString(c.getColumnIndex(COL_THUMBNAIL));
+                    String previewUri = c.getString(c.getColumnIndex(COL_PREVIEW));
+                    Uri thumbnail = Uri.parse(thumbnailUri);
+                    Uri preview = Uri.parse(previewUri);
+
+                    Clockface.Builder builder = new Builder();
+                    builder.setId(id).setTitle(title)
+                            .setThumbnail(new ContentUriAsset(mContext, thumbnail,
+                                    RequestOptions.fitCenterTransform()))
+                            .setPreview(new ContentUriAsset(mContext, preview,
+                                    RequestOptions.fitCenterTransform()));
+                    clockfaces.add(builder.build());
+                }
+                Glide.get(mContext).clearDiskCache();
+            } catch (Exception e) {
+                clockfaces = null;
+            } finally {
+                mContext = null;
+            }
+            return clockfaces;
+        }
+
+        @Override
+        protected void onPostExecute(List<Clockface> clockfaces) {
+            super.onPostExecute(clockfaces);
+            mCallback.onOptionsLoaded(clockfaces);
+        }
+    }
+}
diff --git a/src/com/android/customization/model/clock/ResourcesApkClockProvider.java b/src/com/android/customization/model/clock/ResourcesApkClockProvider.java
deleted file mode 100644
index 1533db9..0000000
--- a/src/com/android/customization/model/clock/ResourcesApkClockProvider.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.customization.model.clock;
-
-import android.content.Context;
-import android.content.res.Resources.NotFoundException;
-import android.util.Log;
-
-import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
-import com.android.customization.model.ResourcesApkProvider;
-import com.android.customization.model.clock.Clockface.Builder;
-import com.android.wallpaper.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class ResourcesApkClockProvider extends ResourcesApkProvider implements ClockProvider {
-
-    private static final String TAG = "ResourcesApkClockProvider";
-
-    private static final String CLOCKS_ARRAY = "clocks";
-    private static final String TITLE_PREFIX = "clock_title_";
-    private static final String ID_PREFIX = "clock_id_";
-    private static final String PREVIEW_PREFIX = "clock_preview_";
-    private static final String THUMBNAIL_PREFIX = "clock_thumbnail_";
-
-    private List<Clockface> mClocks;
-
-    public ResourcesApkClockProvider(Context context){
-        super(context, context.getString(R.string.clocks_stub_package));
-    }
-
-    @Override
-    public void fetch(OptionsFetchedListener<Clockface> callback, boolean reload) {
-        if (mClocks == null || reload) {
-            mClocks = new ArrayList<>();
-            loadAll();
-        }
-
-        if(callback != null) {
-            callback.onOptionsLoaded(mClocks);
-        }
-    }
-
-    private void loadAll() {
-        String[] clockNames = getItemsFromStub(CLOCKS_ARRAY);
-
-        for (String clockName : clockNames) {
-            try {
-                Builder builder = new Builder();
-
-                builder.setTitle(getItemStringFromStub(TITLE_PREFIX, clockName))
-                        .setId(getItemStringFromStub(ID_PREFIX, clockName))
-                        .setPreview(getItemDrawableFromStub(PREVIEW_PREFIX, clockName))
-                        .setThumbnail(getItemDrawableFromStub(THUMBNAIL_PREFIX, clockName));
-
-                mClocks.add(builder.build());
-            } catch (NotFoundException e) {
-                Log.i(TAG, "Resource not found, skipping clock", e);
-            }
-        }
-    }
-}
diff --git a/src/com/android/customization/picker/CustomizationPickerActivity.java b/src/com/android/customization/picker/CustomizationPickerActivity.java
index 2b65862..ec4963f 100644
--- a/src/com/android/customization/picker/CustomizationPickerActivity.java
+++ b/src/com/android/customization/picker/CustomizationPickerActivity.java
@@ -34,7 +34,7 @@
 import com.android.customization.model.CustomizationOption;
 import com.android.customization.model.clock.ClockManager;
 import com.android.customization.model.clock.Clockface;
-import com.android.customization.model.clock.ResourcesApkClockProvider;
+import com.android.customization.model.clock.ContentProviderClockProvider;
 import com.android.customization.model.grid.GridOption;
 import com.android.customization.model.grid.GridOptionsManager;
 import com.android.customization.model.grid.LauncherGridOptionsProvider;
@@ -134,7 +134,8 @@
             mSections.put(R.id.nav_theme, new ThemeSection(R.id.nav_theme, themeManager));
         }
         //Clock
-        ClockManager clockManager = new ClockManager(this, new ResourcesApkClockProvider(this));
+        //ClockManager clockManager = new ClockManager(this, new ResourcesApkClockProvider(this));
+        ClockManager clockManager = new ClockManager(this, new ContentProviderClockProvider(this));
         if (clockManager.isAvailable()) {
             mSections.put(R.id.nav_clock, new ClockSection(R.id.nav_clock, clockManager));
         }
diff --git a/src/com/android/customization/picker/clock/ClockFragment.java b/src/com/android/customization/picker/clock/ClockFragment.java
index 2401686..ab38987 100644
--- a/src/com/android/customization/picker/clock/ClockFragment.java
+++ b/src/com/android/customization/picker/clock/ClockFragment.java
@@ -16,7 +16,7 @@
 package com.android.customization.picker.clock;
 
 import android.content.Context;
-import android.graphics.drawable.Drawable;
+import android.content.res.Resources;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -34,6 +34,7 @@
 import com.android.customization.widget.OptionSelectorController;
 import com.android.customization.widget.PreviewPager;
 import com.android.wallpaper.R;
+import com.android.wallpaper.asset.Asset;
 import com.android.wallpaper.picker.ToolbarFragment;
 
 /**
@@ -111,17 +112,22 @@
 
     private static class ClockfacePreviewPage extends PreviewPage {
 
-        private final Drawable mPreview;
+        private final Asset mPreviewAsset;
 
-        public ClockfacePreviewPage(String title, Drawable previewDrawable) {
+        public ClockfacePreviewPage(String title, Asset previewAsset) {
             super(title);
-            mPreview = previewDrawable;
+            mPreviewAsset = previewAsset;
         }
 
         @Override
         public void bindPreviewContent() {
-            ((ImageView) card.findViewById(R.id.clock_preview_image))
-                    .setImageDrawable(mPreview);
+            ImageView previewImage = card.findViewById(R.id.clock_preview_image);
+            Context context = previewImage.getContext();
+            Resources res = previewImage.getResources();
+            mPreviewAsset.loadDrawableWithTransition(context, previewImage,
+                    100 /* transitionDurationMillis */,
+                    null /* drawableLoadedListener */,
+                    res.getColor(android.R.color.transparent, null) /* placeholderColor */);
         }
     }
 
@@ -133,7 +139,7 @@
     private static class ClockPreviewAdapter extends BasePreviewAdapter<ClockfacePreviewPage> {
         ClockPreviewAdapter(Context context, Clockface clockface) {
             super(context, R.layout.clock_preview_card);
-            addPage(new ClockfacePreviewPage(clockface.getTitle(), clockface.getPreviewDrawable()));
+            addPage(new ClockfacePreviewPage(clockface.getTitle(), clockface.getPreviewAsset()));
         }
     }
 }