SliceBrowser.java revision 2a7d0fcd09ed39bbeda29e024c47fca617050094
1/*
2 * Copyright 2017 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
17package com.example.androidx.slice.demos;
18
19import android.arch.lifecycle.LiveData;
20import android.content.ContentResolver;
21import android.content.pm.ActivityInfo;
22import android.content.pm.PackageInfo;
23import android.content.pm.PackageManager;
24import android.database.Cursor;
25import android.database.MatrixCursor;
26import android.net.Uri;
27import android.os.Bundle;
28import android.provider.BaseColumns;
29import android.support.annotation.RequiresApi;
30import android.support.v7.app.AppCompatActivity;
31import android.support.v7.widget.Toolbar;
32import android.util.ArrayMap;
33import android.util.Log;
34import android.view.Menu;
35import android.view.MenuItem;
36import android.view.SubMenu;
37import android.view.ViewGroup;
38import android.widget.CursorAdapter;
39import android.widget.SearchView;
40import android.widget.SimpleCursorAdapter;
41
42import java.util.ArrayList;
43import java.util.Comparator;
44import java.util.List;
45
46import androidx.app.slice.Slice;
47import androidx.app.slice.widget.SliceLiveData;
48import androidx.app.slice.widget.SliceView;
49
50/**
51 * Example use of SliceView. Uses a search bar to select/auto-complete a slice uri which is
52 * then displayed in the selected mode with SliceView.
53 */
54@RequiresApi(api = 28)
55public class SliceBrowser extends AppCompatActivity {
56
57    private static final String TAG = "SlicePresenter";
58
59    private static final String SLICE_METADATA_KEY = "android.metadata.SLICE_URI";
60
61    private ArrayList<Uri> mSliceUris = new ArrayList<Uri>();
62    private int mSelectedMode;
63    private ViewGroup mContainer;
64    private SearchView mSearchView;
65    private SimpleCursorAdapter mAdapter;
66    private SubMenu mTypeMenu;
67    private LiveData<Slice> mSliceLiveData;
68
69    @Override
70    public void onCreate(Bundle savedInstanceState) {
71        super.onCreate(savedInstanceState);
72        setContentView(R.layout.activity_layout);
73
74        Toolbar toolbar = findViewById(R.id.search_toolbar);
75        setSupportActionBar(toolbar);
76
77        // Shows the slice
78        mContainer = findViewById(R.id.slice_preview);
79        mSearchView = findViewById(R.id.search_view);
80
81        final String[] from = new String[]{"uri"};
82        final int[] to = new int[]{android.R.id.text1};
83        mAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1,
84                null, from, to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
85        mSearchView.setSuggestionsAdapter(mAdapter);
86        mSearchView.setIconifiedByDefault(false);
87        mSearchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
88            @Override
89            public boolean onSuggestionClick(int position) {
90                mSearchView.setQuery(((Cursor) mAdapter.getItem(position)).getString(1), true);
91                return true;
92            }
93
94            @Override
95            public boolean onSuggestionSelect(int position) {
96                mSearchView.setQuery(((Cursor) mAdapter.getItem(position)).getString(1), true);
97                return true;
98            }
99        });
100        mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
101            @Override
102            public boolean onQueryTextSubmit(String s) {
103                addSlice(Uri.parse(s));
104                mSearchView.clearFocus();
105                return false;
106            }
107
108            @Override
109            public boolean onQueryTextChange(String s) {
110                populateAdapter(s);
111                return false;
112            }
113        });
114
115        mSelectedMode = (savedInstanceState != null)
116                ? savedInstanceState.getInt("SELECTED_MODE", SliceView.MODE_SHORTCUT)
117                : SliceView.MODE_SHORTCUT;
118        if (savedInstanceState != null) {
119            mSearchView.setQuery(savedInstanceState.getString("SELECTED_QUERY"), true);
120        }
121
122        // TODO: Listen for changes.
123        updateAvailableSlices();
124    }
125
126    @Override
127    public boolean onCreateOptionsMenu(Menu menu) {
128        mTypeMenu = menu.addSubMenu("Type");
129        mTypeMenu.setIcon(R.drawable.ic_shortcut);
130        mTypeMenu.getItem().setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
131        mTypeMenu.add("Shortcut");
132        mTypeMenu.add("Small");
133        mTypeMenu.add("Large");
134        super.onCreateOptionsMenu(menu);
135        return true;
136    }
137
138    @Override
139    public boolean onOptionsItemSelected(MenuItem item) {
140        switch (item.getTitle().toString()) {
141            case "Shortcut":
142                mTypeMenu.setIcon(R.drawable.ic_shortcut);
143                mSelectedMode = SliceView.MODE_SHORTCUT;
144                updateSliceModes();
145                return true;
146            case "Small":
147                mTypeMenu.setIcon(R.drawable.ic_small);
148                mSelectedMode = SliceView.MODE_SMALL;
149                updateSliceModes();
150                return true;
151            case "Large":
152                mTypeMenu.setIcon(R.drawable.ic_large);
153                mSelectedMode = SliceView.MODE_LARGE;
154                updateSliceModes();
155                return true;
156        }
157        return super.onOptionsItemSelected(item);
158    }
159
160    @Override
161    protected void onSaveInstanceState(Bundle outState) {
162        super.onSaveInstanceState(outState);
163        outState.putInt("SELECTED_MODE", mSelectedMode);
164        outState.putString("SELECTED_QUERY", mSearchView.getQuery().toString());
165    }
166
167    private void updateAvailableSlices() {
168        mSliceUris.clear();
169        List<PackageInfo> packageInfos = getPackageManager()
170                .getInstalledPackages(PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
171        for (PackageInfo pi : packageInfos) {
172            ActivityInfo[] activityInfos = pi.activities;
173            if (activityInfos != null) {
174                for (ActivityInfo ai : activityInfos) {
175                    if (ai.metaData != null) {
176                        String sliceUri = ai.metaData.getString(SLICE_METADATA_KEY);
177                        if (sliceUri != null) {
178                            mSliceUris.add(Uri.parse(sliceUri));
179                        }
180                    }
181                }
182            }
183        }
184        mSliceUris.add(SampleSliceProvider.MESSAGE);
185        populateAdapter(String.valueOf(mSearchView.getQuery()));
186    }
187
188    private void addSlice(Uri uri) {
189        if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
190            SliceView v = new SliceView(getApplicationContext());
191            v.setTag(uri);
192            if (mSliceLiveData != null) {
193                mSliceLiveData.removeObservers(this);
194            }
195            mContainer.removeAllViews();
196            mContainer.addView(v);
197            mSliceLiveData = SliceLiveData.fromUri(this, uri);
198            v.setMode(mSelectedMode);
199            mSliceLiveData.observe(this, v);
200        } else {
201            Log.w(TAG, "Invalid uri, skipping slice: " + uri);
202        }
203    }
204
205    private void updateSliceModes() {
206        final int count = mContainer.getChildCount();
207        for (int i = 0; i < count; i++) {
208            ((SliceView) mContainer.getChildAt(i)).setMode(mSelectedMode);
209        }
210    }
211
212    private void populateAdapter(String query) {
213        final MatrixCursor c = new MatrixCursor(new String[]{BaseColumns._ID, "uri"});
214        ArrayMap<String, Integer> ranking = new ArrayMap<>();
215        ArrayList<String> suggestions = new ArrayList();
216        mSliceUris.forEach(uri -> {
217            String uriString = uri.toString();
218            if (uriString.contains(query)) {
219                ranking.put(uriString, uriString.indexOf(query));
220                suggestions.add(uriString);
221            }
222        });
223        suggestions.sort(new Comparator<String>() {
224            @Override
225            public int compare(String o1, String o2) {
226                return Integer.compare(ranking.get(o1), ranking.get(o2));
227            }
228        });
229        for (int i = 0; i < suggestions.size(); i++) {
230            c.addRow(new Object[]{i, suggestions.get(i)});
231        }
232        mAdapter.changeCursor(c);
233    }
234}
235