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