Home | History | Annotate | Download | only in impl
      1 /*
      2  * Copyright 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 androidx.slice.builders.impl;
     18 
     19 import static android.app.slice.Slice.HINT_ACTIONS;
     20 import static android.app.slice.Slice.HINT_LARGE;
     21 import static android.app.slice.Slice.HINT_LIST_ITEM;
     22 import static android.app.slice.Slice.HINT_NO_TINT;
     23 import static android.app.slice.Slice.HINT_PARTIAL;
     24 import static android.app.slice.Slice.HINT_SEE_MORE;
     25 import static android.app.slice.Slice.HINT_SHORTCUT;
     26 import static android.app.slice.Slice.HINT_SUMMARY;
     27 import static android.app.slice.Slice.HINT_TITLE;
     28 import static android.app.slice.Slice.SUBTYPE_COLOR;
     29 import static android.app.slice.Slice.SUBTYPE_CONTENT_DESCRIPTION;
     30 import static android.app.slice.Slice.SUBTYPE_MAX;
     31 import static android.app.slice.Slice.SUBTYPE_RANGE;
     32 import static android.app.slice.Slice.SUBTYPE_VALUE;
     33 import static android.app.slice.SliceItem.FORMAT_TEXT;
     34 
     35 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
     36 import static androidx.slice.builders.ListBuilder.ICON_IMAGE;
     37 import static androidx.slice.builders.ListBuilder.INFINITY;
     38 import static androidx.slice.builders.ListBuilder.LARGE_IMAGE;
     39 import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
     40 import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
     41 import static androidx.slice.core.SliceHints.HINT_TTL;
     42 import static androidx.slice.core.SliceHints.SUBTYPE_MILLIS;
     43 import static androidx.slice.core.SliceHints.SUBTYPE_MIN;
     44 
     45 import android.app.PendingIntent;
     46 import android.net.Uri;
     47 
     48 import androidx.annotation.ColorInt;
     49 import androidx.annotation.NonNull;
     50 import androidx.annotation.RestrictTo;
     51 import androidx.core.graphics.drawable.IconCompat;
     52 import androidx.slice.Slice;
     53 import androidx.slice.SliceItem;
     54 import androidx.slice.SliceSpec;
     55 import androidx.slice.builders.SliceAction;
     56 
     57 import java.util.ArrayList;
     58 import java.util.List;
     59 
     60 /**
     61  * @hide
     62  */
     63 @RestrictTo(LIBRARY)
     64 public class ListBuilderV1Impl extends TemplateBuilderImpl implements ListBuilder {
     65 
     66     private List<Slice> mSliceActions;
     67     private Slice mSliceHeader;
     68 
     69     /**
     70      */
     71     public ListBuilderV1Impl(Slice.Builder b, SliceSpec spec) {
     72         super(b, spec);
     73     }
     74 
     75     /**
     76      */
     77     @Override
     78     public void apply(Slice.Builder builder) {
     79         builder.addLong(System.currentTimeMillis(), SUBTYPE_MILLIS, HINT_LAST_UPDATED);
     80         if (mSliceHeader != null) {
     81             builder.addSubSlice(mSliceHeader);
     82         }
     83         if (mSliceActions != null) {
     84             Slice.Builder sb = new Slice.Builder(builder);
     85             for (int i = 0; i < mSliceActions.size(); i++) {
     86                 sb.addSubSlice(mSliceActions.get(i));
     87             }
     88             builder.addSubSlice(sb.addHints(HINT_ACTIONS).build());
     89         }
     90     }
     91 
     92     /**
     93      * Add a row to list builder.
     94      */
     95     @NonNull
     96     @Override
     97     public void addRow(@NonNull TemplateBuilderImpl builder) {
     98         builder.getBuilder().addHints(HINT_LIST_ITEM);
     99         getBuilder().addSubSlice(builder.build());
    100     }
    101 
    102     /**
    103      */
    104     @NonNull
    105     @Override
    106     public void addGridRow(@NonNull TemplateBuilderImpl builder) {
    107         builder.getBuilder().addHints(HINT_LIST_ITEM);
    108         getBuilder().addSubSlice(builder.build());
    109     }
    110 
    111     /**
    112      */
    113     @Override
    114     public void setHeader(@NonNull TemplateBuilderImpl builder) {
    115         mSliceHeader = builder.build();
    116     }
    117 
    118     /**
    119      */
    120     @Override
    121     public void addAction(@NonNull SliceAction action) {
    122         if (mSliceActions == null) {
    123             mSliceActions = new ArrayList<>();
    124         }
    125         Slice.Builder b = new Slice.Builder(getBuilder()).addHints(HINT_ACTIONS);
    126         mSliceActions.add(action.buildSlice(b));
    127     }
    128 
    129     @Override
    130     public void addInputRange(TemplateBuilderImpl builder) {
    131         getBuilder().addSubSlice(builder.build(), SUBTYPE_RANGE);
    132     }
    133 
    134     @Override
    135     public void addRange(TemplateBuilderImpl builder) {
    136         getBuilder().addSubSlice(builder.build(), SUBTYPE_RANGE);
    137     }
    138 
    139     /**
    140      */
    141     @Override
    142     public void setSeeMoreRow(TemplateBuilderImpl builder) {
    143         builder.getBuilder().addHints(HINT_SEE_MORE);
    144         getBuilder().addSubSlice(builder.build());
    145     }
    146 
    147     /**
    148      */
    149     @Override
    150     public void setSeeMoreAction(PendingIntent intent) {
    151         getBuilder().addSubSlice(
    152                 new Slice.Builder(getBuilder())
    153                         .addHints(HINT_SEE_MORE)
    154                         .addAction(intent, new Slice.Builder(getBuilder())
    155                                 .addHints(HINT_SEE_MORE).build(), null)
    156                         .build());
    157     }
    158 
    159 
    160     /**
    161      * Builder to construct an input row.
    162      */
    163     public static class RangeBuilderImpl extends TemplateBuilderImpl implements RangeBuilder {
    164         private int mMin = 0;
    165         private int mMax = 100;
    166         private int mValue = 0;
    167         private CharSequence mTitle;
    168         private CharSequence mSubtitle;
    169         private CharSequence mContentDescr;
    170         private SliceAction mPrimaryAction;
    171 
    172         public RangeBuilderImpl(Slice.Builder sb) {
    173             super(sb, null);
    174         }
    175 
    176         @Override
    177         public void setMin(int min) {
    178             mMin = min;
    179         }
    180 
    181         @Override
    182         public void setMax(int max) {
    183             mMax = max;
    184         }
    185 
    186         @Override
    187         public void setValue(int value) {
    188             mValue = value;
    189         }
    190 
    191         @Override
    192         public void setTitle(@NonNull CharSequence title) {
    193             mTitle = title;
    194         }
    195 
    196         @Override
    197         public void setSubtitle(@NonNull CharSequence title) {
    198             mSubtitle = title;
    199         }
    200 
    201         @Override
    202         public void setPrimaryAction(@NonNull SliceAction action) {
    203             mPrimaryAction = action;
    204         }
    205 
    206         @Override
    207         public void setContentDescription(@NonNull CharSequence description) {
    208             mContentDescr = description;
    209         }
    210 
    211         @Override
    212         public void apply(Slice.Builder builder) {
    213             if (mTitle != null) {
    214                 builder.addText(mTitle, null, HINT_TITLE);
    215             }
    216             if (mSubtitle != null) {
    217                 builder.addText(mSubtitle, null);
    218             }
    219             if (mContentDescr != null) {
    220                 builder.addText(mContentDescr, SUBTYPE_CONTENT_DESCRIPTION);
    221             }
    222             if (mPrimaryAction != null) {
    223                 Slice.Builder sb = new Slice.Builder(
    224                         getBuilder()).addHints(HINT_TITLE, HINT_SHORTCUT);
    225                 builder.addSubSlice(mPrimaryAction.buildSlice(sb), null /* subtype */);
    226             }
    227             builder.addHints(HINT_LIST_ITEM)
    228                     .addInt(mMin, SUBTYPE_MIN)
    229                     .addInt(mMax, SUBTYPE_MAX)
    230                     .addInt(mValue, SUBTYPE_VALUE);
    231         }
    232     }
    233 
    234     /**
    235      * Builder to construct an input range row.
    236      */
    237     public static class InputRangeBuilderImpl
    238             extends RangeBuilderImpl implements InputRangeBuilder {
    239         private PendingIntent mAction;
    240         private IconCompat mThumb;
    241 
    242         public InputRangeBuilderImpl(Slice.Builder sb) {
    243             super(sb);
    244         }
    245 
    246         @Override
    247         public void setInputAction(@NonNull PendingIntent action) {
    248             mAction = action;
    249         }
    250 
    251         @Override
    252         public void setThumb(@NonNull IconCompat thumb) {
    253             mThumb = thumb;
    254         }
    255 
    256         @Override
    257         public void apply(Slice.Builder builder) {
    258             if (mAction == null) {
    259                 throw new IllegalStateException("Input ranges must have an associated action.");
    260             }
    261             Slice.Builder sb = new Slice.Builder(builder);
    262             super.apply(sb);
    263             if (mThumb != null) {
    264                 sb.addIcon(mThumb, null);
    265             }
    266             builder.addAction(mAction, sb.build(), SUBTYPE_RANGE).addHints(HINT_LIST_ITEM);
    267         }
    268     }
    269 
    270     /**
    271      */
    272     @NonNull
    273     @Override
    274     public void setColor(@ColorInt int color) {
    275         getBuilder().addInt(color, SUBTYPE_COLOR);
    276     }
    277 
    278     /**
    279      */
    280     @Override
    281     public void setKeywords(@NonNull List<String> keywords) {
    282         Slice.Builder sb = new Slice.Builder(getBuilder());
    283         for (int i = 0; i < keywords.size(); i++) {
    284             sb.addText(keywords.get(i), null);
    285         }
    286         getBuilder().addSubSlice(sb.addHints(HINT_KEYWORDS).build());
    287     }
    288 
    289     /**
    290      */
    291     @Override
    292     public void setTtl(long ttl) {
    293         long expiry = ttl == INFINITY ? INFINITY : System.currentTimeMillis() + ttl;
    294         getBuilder().addTimestamp(expiry, SUBTYPE_MILLIS, HINT_TTL);
    295     }
    296 
    297     /**
    298      */
    299     @Override
    300     public TemplateBuilderImpl createRowBuilder() {
    301         return new RowBuilderImpl(this);
    302     }
    303 
    304     /**
    305      */
    306     @Override
    307     public TemplateBuilderImpl createRowBuilder(Uri uri) {
    308         return new RowBuilderImpl(uri);
    309     }
    310 
    311 
    312     @Override
    313     public TemplateBuilderImpl createInputRangeBuilder() {
    314         return new InputRangeBuilderImpl(createChildBuilder());
    315     }
    316 
    317     @Override
    318     public TemplateBuilderImpl createRangeBuilder() {
    319         return new RangeBuilderImpl(createChildBuilder());
    320     }
    321 
    322     /**
    323      */
    324     @Override
    325     public TemplateBuilderImpl createGridBuilder() {
    326         return new GridRowBuilderListV1Impl(this);
    327     }
    328 
    329     /**
    330      */
    331     @Override
    332     public TemplateBuilderImpl createHeaderBuilder() {
    333         return new HeaderBuilderImpl(this);
    334     }
    335 
    336     @Override
    337     public TemplateBuilderImpl createHeaderBuilder(Uri uri) {
    338         return new HeaderBuilderImpl(uri);
    339     }
    340 
    341     /**
    342      */
    343     public static class RowBuilderImpl extends TemplateBuilderImpl
    344             implements ListBuilder.RowBuilder {
    345 
    346         private SliceAction mPrimaryAction;
    347         private SliceItem mTitleItem;
    348         private SliceItem mSubtitleItem;
    349         private Slice mStartItem;
    350         private ArrayList<Slice> mEndItems = new ArrayList<>();
    351         private CharSequence mContentDescr;
    352 
    353         /**
    354          */
    355         public RowBuilderImpl(@NonNull ListBuilderV1Impl parent) {
    356             super(parent.createChildBuilder(), null);
    357         }
    358 
    359         /**
    360          */
    361         public RowBuilderImpl(@NonNull Uri uri) {
    362             super(new Slice.Builder(uri), null);
    363         }
    364 
    365         /**
    366          */
    367         public RowBuilderImpl(Slice.Builder builder) {
    368             super(builder, null);
    369         }
    370 
    371         /**
    372          */
    373         @NonNull
    374         @Override
    375         public void setTitleItem(long timeStamp) {
    376             mStartItem = new Slice.Builder(getBuilder())
    377                     .addTimestamp(timeStamp, null).addHints(HINT_TITLE).build();
    378         }
    379 
    380         /**
    381          */
    382         @NonNull
    383         @Override
    384         public void setTitleItem(IconCompat icon, int imageMode) {
    385             setTitleItem(icon, imageMode, false /* isLoading */);
    386         }
    387 
    388         /**
    389          */
    390         @NonNull
    391         @Override
    392         public void setTitleItem(IconCompat icon, int imageMode, boolean isLoading) {
    393             ArrayList<String> hints = new ArrayList<>();
    394             if (imageMode != ICON_IMAGE) {
    395                 hints.add(HINT_NO_TINT);
    396             }
    397             if (imageMode == LARGE_IMAGE) {
    398                 hints.add(HINT_LARGE);
    399             }
    400             if (isLoading) {
    401                 hints.add(HINT_PARTIAL);
    402             }
    403             Slice.Builder sb = new Slice.Builder(getBuilder())
    404                     .addIcon(icon, null /* subType */, hints);
    405             if (isLoading) {
    406                 sb.addHints(HINT_PARTIAL);
    407             }
    408             mStartItem = sb.addHints(HINT_TITLE).build();
    409         }
    410 
    411         /**
    412          */
    413         @NonNull
    414         @Override
    415         public void setTitleItem(@NonNull SliceAction action) {
    416             setTitleItem(action, false /* isLoading */);
    417         }
    418 
    419         /**
    420          */
    421         @Override
    422         public void setTitleItem(SliceAction action, boolean isLoading) {
    423             Slice.Builder sb = new Slice.Builder(getBuilder()).addHints(HINT_TITLE);
    424             if (isLoading) {
    425                 sb.addHints(HINT_PARTIAL);
    426             }
    427             mStartItem = action.buildSlice(sb);
    428         }
    429 
    430         /**
    431          */
    432         @NonNull
    433         @Override
    434         public void setPrimaryAction(@NonNull SliceAction action) {
    435             mPrimaryAction = action;
    436         }
    437 
    438         /**
    439          */
    440         @NonNull
    441         @Override
    442         public void setTitle(CharSequence title) {
    443             setTitle(title, false /* isLoading */);
    444         }
    445 
    446         /**
    447          */
    448         @Override
    449         public void setTitle(CharSequence title, boolean isLoading) {
    450             mTitleItem = new SliceItem(title, FORMAT_TEXT, null, new String[] {HINT_TITLE});
    451             if (isLoading) {
    452                 mTitleItem.addHint(HINT_PARTIAL);
    453             }
    454         }
    455 
    456         /**
    457          */
    458         @NonNull
    459         @Override
    460         public void setSubtitle(CharSequence subtitle) {
    461             setSubtitle(subtitle, false /* isLoading */);
    462         }
    463 
    464         /**
    465          */
    466         @Override
    467         public void setSubtitle(CharSequence subtitle, boolean isLoading) {
    468             mSubtitleItem = new SliceItem(subtitle, FORMAT_TEXT, null, new String[0]);
    469             if (isLoading) {
    470                 mSubtitleItem.addHint(HINT_PARTIAL);
    471             }
    472         }
    473 
    474         /**
    475          */
    476         @NonNull
    477         @Override
    478         public void addEndItem(long timeStamp) {
    479             mEndItems.add(new Slice.Builder(getBuilder()).addTimestamp(timeStamp,
    480                     null, new String[0]).build());
    481         }
    482 
    483         /**
    484          */
    485         @NonNull
    486         @Override
    487         public void addEndItem(IconCompat icon, int imageMode) {
    488             addEndItem(icon, imageMode, false /* isLoading */);
    489         }
    490 
    491         /**
    492          */
    493         @NonNull
    494         @Override
    495         public void addEndItem(IconCompat icon, int imageMode, boolean isLoading) {
    496             ArrayList<String> hints = new ArrayList<>();
    497             if (imageMode != ICON_IMAGE) {
    498                 hints.add(HINT_NO_TINT);
    499             }
    500             if (imageMode == LARGE_IMAGE) {
    501                 hints.add(HINT_LARGE);
    502             }
    503             if (isLoading) {
    504                 hints.add(HINT_PARTIAL);
    505             }
    506             Slice.Builder sb = new Slice.Builder(getBuilder())
    507                     .addIcon(icon, null /* subType */, hints);
    508             if (isLoading) {
    509                 sb.addHints(HINT_PARTIAL);
    510             }
    511             mEndItems.add(sb.build());
    512         }
    513 
    514         /**
    515          */
    516         @NonNull
    517         @Override
    518         public void addEndItem(@NonNull SliceAction action) {
    519             addEndItem(action, false /* isLoading */);
    520         }
    521 
    522         /**
    523          */
    524         @Override
    525         public void addEndItem(@NonNull SliceAction action, boolean isLoading) {
    526             Slice.Builder sb = new Slice.Builder(getBuilder());
    527             if (isLoading) {
    528                 sb.addHints(HINT_PARTIAL);
    529             }
    530             mEndItems.add(action.buildSlice(sb));
    531         }
    532 
    533         @Override
    534         public void setContentDescription(CharSequence description) {
    535             mContentDescr = description;
    536         }
    537 
    538         /**
    539          */
    540         @Override
    541         public void apply(Slice.Builder b) {
    542             if (mStartItem != null) {
    543                 b.addSubSlice(mStartItem);
    544             }
    545             if (mTitleItem != null) {
    546                 b.addItem(mTitleItem);
    547             }
    548             if (mSubtitleItem != null) {
    549                 b.addItem(mSubtitleItem);
    550             }
    551             for (int i = 0; i < mEndItems.size(); i++) {
    552                 Slice item = mEndItems.get(i);
    553                 b.addSubSlice(item);
    554             }
    555             if (mContentDescr != null) {
    556                 b.addText(mContentDescr, SUBTYPE_CONTENT_DESCRIPTION);
    557             }
    558             if (mPrimaryAction != null) {
    559                 Slice.Builder sb = new Slice.Builder(
    560                         getBuilder()).addHints(HINT_TITLE, HINT_SHORTCUT);
    561                 b.addSubSlice(mPrimaryAction.buildSlice(sb), null);
    562             }
    563         }
    564     }
    565 
    566     /**
    567      */
    568     public static class HeaderBuilderImpl extends TemplateBuilderImpl
    569             implements ListBuilder.HeaderBuilder {
    570 
    571         private SliceItem mTitleItem;
    572         private SliceItem mSubtitleItem;
    573         private SliceItem mSummaryItem;
    574         private SliceAction mPrimaryAction;
    575         private CharSequence mContentDescr;
    576 
    577         /**
    578          */
    579         public HeaderBuilderImpl(@NonNull ListBuilderV1Impl parent) {
    580             super(parent.createChildBuilder(), null);
    581         }
    582 
    583         /**
    584          */
    585         public HeaderBuilderImpl(@NonNull Uri uri) {
    586             super(new Slice.Builder(uri), null);
    587         }
    588 
    589         /**
    590          */
    591         @Override
    592         public void apply(Slice.Builder b) {
    593             if (mTitleItem != null) {
    594                 b.addItem(mTitleItem);
    595             }
    596             if (mSubtitleItem != null) {
    597                 b.addItem(mSubtitleItem);
    598             }
    599             if (mSummaryItem != null) {
    600                 b.addItem(mSummaryItem);
    601             }
    602             if (mContentDescr != null) {
    603                 b.addText(mContentDescr, SUBTYPE_CONTENT_DESCRIPTION);
    604             }
    605             if (mPrimaryAction != null) {
    606                 Slice.Builder sb = new Slice.Builder(
    607                         getBuilder()).addHints(HINT_TITLE, HINT_SHORTCUT);
    608                 b.addSubSlice(mPrimaryAction.buildSlice(sb), null /* subtype */);
    609             }
    610         }
    611 
    612         /**
    613          */
    614         @Override
    615         public void setTitle(CharSequence title, boolean isLoading) {
    616             mTitleItem = new SliceItem(title, FORMAT_TEXT, null, new String[] {HINT_TITLE});
    617             if (isLoading) {
    618                 mTitleItem.addHint(HINT_PARTIAL);
    619             }
    620         }
    621 
    622         /**
    623          */
    624         @Override
    625         public void setSubtitle(CharSequence subtitle, boolean isLoading) {
    626             mSubtitleItem = new SliceItem(subtitle, FORMAT_TEXT, null, new String[0]);
    627             if (isLoading) {
    628                 mSubtitleItem.addHint(HINT_PARTIAL);
    629             }
    630         }
    631 
    632         /**
    633          */
    634         @Override
    635         public void setSummary(CharSequence summarySubtitle, boolean isLoading) {
    636             mSummaryItem = new SliceItem(summarySubtitle, FORMAT_TEXT, null,
    637                     new String[] {HINT_SUMMARY});
    638             if (isLoading) {
    639                 mSummaryItem.addHint(HINT_PARTIAL);
    640             }
    641         }
    642 
    643         /**
    644          */
    645         @Override
    646         public void setPrimaryAction(SliceAction action) {
    647             mPrimaryAction = action;
    648         }
    649 
    650         /**
    651          */
    652         @Override
    653         public void setContentDescription(CharSequence description) {
    654             mContentDescr = description;
    655         }
    656     }
    657 }
    658