1 /*
2  * Copyright (C) 2016 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 package com.example.android.shortcutsample;
17 
18 import android.app.Activity;
19 import android.app.AlertDialog;
20 import android.app.ListActivity;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.ShortcutInfo;
24 import android.content.pm.ShortcutManager;
25 import android.os.AsyncTask;
26 import android.os.Bundle;
27 import androidx.core.content.pm.ShortcutManagerCompat;
28 import android.util.ArraySet;
29 import android.util.Log;
30 import android.view.LayoutInflater;
31 import android.view.View;
32 import android.view.View.OnClickListener;
33 import android.view.ViewGroup;
34 import android.view.inputmethod.EditorInfo;
35 import android.widget.BaseAdapter;
36 import android.widget.Button;
37 import android.widget.EditText;
38 import android.widget.TextView;
39 
40 import java.util.ArrayList;
41 import java.util.List;
42 import java.util.Set;
43 
44 public class Main extends ListActivity implements OnClickListener {
45     static final String TAG = "ShortcutSample";
46 
47     private static final String ID_ADD_WEBSITE = "add_website";
48 
49     private static final String ACTION_ADD_WEBSITE =
50             "com.example.android.shortcutsample.ADD_WEBSITE";
51 
52     private MyAdapter mAdapter;
53 
54     private ShortcutHelper mHelper;
55     private ShortcutManager mShortcutManager;
56 
57     // @GuardedBy("sVisibleInstances")
58     private static final Set<Main> sVisibleInstances = new ArraySet<>();
59 
60     @Override
onCreate(Bundle savedInstanceState)61     protected void onCreate(Bundle savedInstanceState) {
62         super.onCreate(savedInstanceState);
63 
64         setContentView(R.layout.main);
65 
66         mHelper = new ShortcutHelper(this);
67         mShortcutManager = getSystemService(ShortcutManager.class);
68 
69         mHelper.maybeRestoreAllDynamicShortcuts();
70 
71         mHelper.refreshShortcuts(/*force=*/ false);
72 
73         if (ACTION_ADD_WEBSITE.equals(getIntent().getAction())) {
74             // Invoked via the manifest shortcut.
75             addWebSite(/* forPin=*/ false, /* forResult= */ false);
76         } else if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
77             addWebSite(/* forPin=*/ true, /* forResult= */ true);
78         }
79 
80         mAdapter = new MyAdapter(this.getApplicationContext());
81         setListAdapter(mAdapter);
82     }
83 
84     @Override
onStart()85     protected void onStart() {
86         super.onStart();
87         refreshList();
88         synchronized (sVisibleInstances) {
89             sVisibleInstances.add(this);
90         }
91         findViewById(R.id.request_new_pin_shortcut).setVisibility(
92                 ShortcutManagerCompat.isRequestPinShortcutSupported(this)
93                         ? View.VISIBLE : View.GONE);
94     }
95 
96     @Override
onStop()97     protected void onStop() {
98         synchronized (sVisibleInstances) {
99             sVisibleInstances.remove(this);
100         }
101         super.onStop();
102     }
103 
104     /**
105      * Handle the add button.
106      */
onAddPressed(View v)107     public void onAddPressed(View v) {
108         addWebSite(/* forPin=*/ false, /* forResult= */ false);
109     }
110 
111     /**
112      * Handle the add button.
113      */
onRequestNewPinPressed(View v)114     public void onRequestNewPinPressed(View v) {
115         addWebSite(/* forPin=*/ true, /* forResult= */ false);
116     }
117 
addWebSite(boolean forPin, boolean forResult)118     private void addWebSite(boolean forPin, boolean forResult) {
119         Log.i(TAG, "addWebSite forPin=" + forPin);
120 
121         // This is important.  This allows the launcher to build a prediction model.
122         mHelper.reportShortcutUsed(ID_ADD_WEBSITE);
123 
124         final EditText editUri = new EditText(this);
125 
126         editUri.setHint("http://www.android.com/");
127         editUri.setInputType(EditorInfo.TYPE_TEXT_VARIATION_URI);
128 
129         new AlertDialog.Builder(this)
130                 .setTitle(forPin ? "Create pin shortcut for website" : "Add new website")
131                 .setMessage("Type URL of a website")
132                 .setView(editUri)
133                 .setPositiveButton("Add", (dialog, whichButton) -> {
134                     final String url = editUri.getText().toString().trim();
135                     if (url.length() > 0) {
136                         addUriAsync(url, forPin, forResult);
137                     }
138                 })
139                 .setOnCancelListener((dialog) -> {
140                     if (forResult) {
141                         setResult(Activity.RESULT_CANCELED);
142                         finish();
143                     }
144                 })
145                 .show();
146     }
147 
addUriAsync(String uri, boolean forPin, boolean forResult)148     private void addUriAsync(String uri, boolean forPin, boolean forResult) {
149         if (forResult) {
150             new AsyncTask<Void, Void, ShortcutInfo>() {
151                 @Override
152                 protected ShortcutInfo doInBackground(Void... params) {
153                     return mHelper.createShortcutForUrl(uri);
154                 }
155 
156                 @Override
157                 protected void onPostExecute(ShortcutInfo shortcut) {
158                     setResult(Activity.RESULT_OK,
159                             mShortcutManager.createShortcutResultIntent(shortcut));
160                     finish();
161                 }
162             }.execute();
163         } else {
164             new AsyncTask<Void, Void, Void>() {
165                 @Override
166                 protected Void doInBackground(Void... params) {
167                     mHelper.addWebSiteShortcut(uri, forPin);
168                     return null;
169                 }
170 
171                 @Override
172                 protected void onPostExecute(Void aVoid) {
173                     refreshList();
174                 }
175             }.execute();
176         }
177     }
178 
refreshList()179     private void refreshList() {
180         mAdapter.setShortcuts(mHelper.getShortcuts());
181     }
182 
183     @Override
onClick(View v)184     public void onClick(View v) {
185         final ShortcutInfo shortcut = (ShortcutInfo) ((View) v.getParent()).getTag();
186 
187         switch (v.getId()) {
188             case R.id.disable:
189                 if (shortcut.isEnabled()) {
190                     mHelper.disableShortcut(shortcut);
191                 } else {
192                     mHelper.enableShortcut(shortcut);
193                 }
194                 refreshList();
195                 break;
196             case R.id.remove:
197                 mHelper.removeShortcut(shortcut);
198                 refreshList();
199                 break;
200             case R.id.request_pin:
201                 // This is an update case, so just pass the ID.
202                 mHelper.requestPinShortcut(shortcut.getId());
203                 refreshList();
204                 break;
205         }
206     }
207 
208     private static final List<ShortcutInfo> EMPTY_LIST = new ArrayList<>();
209 
getType(ShortcutInfo shortcut)210     private String getType(ShortcutInfo shortcut) {
211         final StringBuilder sb = new StringBuilder();
212         String sep = "";
213         if (shortcut.isDeclaredInManifest()) {
214             sb.append(sep);
215             sb.append("Manifest");
216             sep = ", ";
217         }
218         if (shortcut.isDynamic()) {
219             sb.append(sep);
220             sb.append("Dynamic");
221             sep = ", ";
222         }
223         if (shortcut.isPinned()) {
224             sb.append(sep);
225             sb.append("Pinned");
226             sep = ", ";
227         }
228         if (!shortcut.isEnabled()) {
229             sb.append(sep);
230             sb.append("Disabled");
231             sep = ", ";
232         }
233         return sb.toString();
234     }
235 
236     private class MyAdapter extends BaseAdapter {
237         private final Context mContext;
238         private final LayoutInflater mInflater;
239         private List<ShortcutInfo> mList = EMPTY_LIST;
240 
MyAdapter(Context context)241         public MyAdapter(Context context) {
242             mContext = context;
243             mInflater = mContext.getSystemService(LayoutInflater.class);
244         }
245 
246         @Override
getCount()247         public int getCount() {
248             return mList.size();
249         }
250 
251         @Override
getItem(int position)252         public Object getItem(int position) {
253             return mList.get(position);
254         }
255 
256         @Override
getItemId(int position)257         public long getItemId(int position) {
258             return position;
259         }
260 
261         @Override
hasStableIds()262         public boolean hasStableIds() {
263             return false;
264         }
265 
266         @Override
areAllItemsEnabled()267         public boolean areAllItemsEnabled() {
268             return true;
269         }
270 
271         @Override
isEnabled(int position)272         public boolean isEnabled(int position) {
273             return true;
274         }
275 
setShortcuts(List<ShortcutInfo> list)276         public void setShortcuts(List<ShortcutInfo> list) {
277             mList = list;
278             notifyDataSetChanged();
279         }
280 
281         @Override
getView(int position, View convertView, ViewGroup parent)282         public View getView(int position, View convertView, ViewGroup parent) {
283             final View view;
284             if (convertView != null) {
285                 view = convertView;
286             } else {
287                 view = mInflater.inflate(R.layout.list_item, null);
288             }
289 
290             bindView(view, position, mList.get(position));
291 
292             return view;
293         }
294 
bindView(View view, int position, ShortcutInfo shortcut)295         public void bindView(View view, int position, ShortcutInfo shortcut) {
296             view.setTag(shortcut);
297 
298             final TextView line1 = (TextView) view.findViewById(R.id.line1);
299             final TextView line2 = (TextView) view.findViewById(R.id.line2);
300 
301             line1.setText(shortcut.getLongLabel());
302             line2.setText(getType(shortcut));
303 
304             final Button remove = (Button) view.findViewById(R.id.remove);
305             final Button disable = (Button) view.findViewById(R.id.disable);
306             final Button requestPin = (Button) view.findViewById(R.id.request_pin);
307 
308             disable.setText(
309                     shortcut.isEnabled() ? R.string.disable_shortcut : R.string.enable_shortcut);
310 
311             remove.setVisibility(shortcut.isImmutable() ? View.GONE : View.VISIBLE);
312             disable.setVisibility(shortcut.isImmutable() ? View.GONE : View.VISIBLE);
313             requestPin.setVisibility(
314                     ShortcutManagerCompat.isRequestPinShortcutSupported(mContext)
315                             ? View.VISIBLE : View.GONE);
316 
317             remove.setOnClickListener(Main.this);
318             disable.setOnClickListener(Main.this);
319             requestPin.setOnClickListener(Main.this);
320         }
321     }
322 
refreshAllInstances()323     public static void refreshAllInstances() {
324         synchronized (sVisibleInstances) {
325             for (Main instance : sVisibleInstances) {
326                 instance.refreshList();
327             }
328         }
329     }
330 }
331