OSDN Git Service

Implement scale up transition for app launches + lots of cleanup
[android-x86/packages-apps-Taskbar.git] / app / src / main / java / com / farmerbb / taskbar / adapter / StartMenuAdapter.java
1 /* Copyright 2016 Braden Farmer
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 package com.farmerbb.taskbar.adapter;
17
18 import android.content.ComponentName;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.SharedPreferences;
22 import android.content.pm.ActivityInfo;
23 import android.graphics.Color;
24 import android.graphics.Typeface;
25 import android.os.Build;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.support.annotation.NonNull;
29 import android.support.v4.content.ContextCompat;
30 import android.support.v4.content.LocalBroadcastManager;
31 import android.support.v4.graphics.ColorUtils;
32 import android.util.SparseIntArray;
33 import android.view.LayoutInflater;
34 import android.view.MotionEvent;
35 import android.view.PointerIcon;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.widget.ArrayAdapter;
39 import android.widget.ImageView;
40 import android.widget.LinearLayout;
41 import android.widget.SectionIndexer;
42 import android.widget.TextView;
43
44 import com.farmerbb.taskbar.R;
45 import com.farmerbb.taskbar.util.AppEntry;
46 import com.farmerbb.taskbar.util.FreeformHackHelper;
47 import com.farmerbb.taskbar.util.TopApps;
48 import com.farmerbb.taskbar.util.U;
49
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.HashMap;
53 import java.util.List;
54 import java.util.Map;
55
56 public class StartMenuAdapter extends ArrayAdapter<AppEntry> implements SectionIndexer {
57
58     private boolean isGrid = false;
59
60     private final List<Character> sections = new ArrayList<>();
61     private final SparseIntArray gpfsCache = new SparseIntArray();
62     private final SparseIntArray gsfpCache = new SparseIntArray();
63     private final Map<AppEntry, Boolean> topAppsCache = new HashMap<>();
64
65     private final List<Character> lowercase = Arrays.asList(
66             'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
67             'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
68     );
69
70     private final List<Character> uppercase = Arrays.asList(
71             'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
72             'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
73     );
74
75     public StartMenuAdapter(Context context, int layout, List<AppEntry> list) {
76         super(context, layout, list);
77         isGrid = layout == R.layout.row_alt;
78
79         updateList(list, true);
80     }
81
82     @Override
83     public @NonNull View getView(int position, View convertView, final @NonNull ViewGroup parent) {
84         // Check if an existing view is being reused, otherwise inflate the view
85         if(convertView == null)
86             convertView = LayoutInflater.from(getContext()).inflate(isGrid ? R.layout.row_alt : R.layout.row, parent, false);
87
88         final AppEntry entry = getItem(position);
89         assert entry != null;
90
91         final SharedPreferences pref = U.getSharedPreferences(getContext());
92
93         TextView textView = convertView.findViewById(R.id.name);
94         textView.setText(entry.getLabel());
95         textView.setTypeface(null, isTopApp(entry) ? Typeface.BOLD : Typeface.NORMAL);
96
97         switch(pref.getString("theme", "light")) {
98             case "light":
99                 textView.setTextColor(ContextCompat.getColor(getContext(), R.color.text_color));
100                 break;
101             case "dark":
102                 textView.setTextColor(ContextCompat.getColor(getContext(), R.color.text_color_dark));
103                 break;
104         }
105
106         ImageView imageView = convertView.findViewById(R.id.icon);
107         imageView.setImageDrawable(entry.getIcon(getContext()));
108
109         LinearLayout layout = convertView.findViewById(R.id.entry);
110         layout.setOnClickListener(view -> {
111             LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU"));
112             U.launchApp(getContext(), entry, null, false, false, view);
113         });
114
115         layout.setOnLongClickListener(view -> {
116             int[] location = new int[2];
117             view.getLocationOnScreen(location);
118             openContextMenu(entry, location);
119             return true;
120         });
121
122         layout.setOnGenericMotionListener((view, motionEvent) -> {
123             int action = motionEvent.getAction();
124
125             if(action == MotionEvent.ACTION_BUTTON_PRESS
126                     && motionEvent.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
127                 int[] location = new int[2];
128                 view.getLocationOnScreen(location);
129                 openContextMenu(entry, location);
130             }
131
132             if(action == MotionEvent.ACTION_SCROLL && U.visualFeedbackEnabled(getContext()))
133                 view.setBackgroundColor(0);
134
135             return false;
136         });
137
138         if(U.visualFeedbackEnabled(getContext())) {
139             layout.setOnHoverListener((v, event) -> {
140                 if(event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
141                     int backgroundTint = pref.getBoolean("transparent_start_menu", false)
142                             ? U.getAccentColor(getContext())
143                             : U.getBackgroundTint(getContext());
144
145                     //noinspection ResourceAsColor
146                     backgroundTint = ColorUtils.setAlphaComponent(backgroundTint, Color.alpha(backgroundTint) / 2);
147                     v.setBackgroundColor(backgroundTint);
148                 }
149
150                 if(event.getAction() == MotionEvent.ACTION_HOVER_EXIT)
151                     v.setBackgroundColor(0);
152
153                 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
154                     v.setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT));
155
156                 return false;
157             });
158         }
159
160         if(pref.getBoolean("visual_feedback", true)) {
161             layout.setOnTouchListener((v, event) -> {
162                 v.setAlpha(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE ? 0.5f : 1);
163                 return false;
164             });
165         }
166
167         return convertView;
168     }
169
170     private boolean isTopApp(AppEntry entry) {
171         if(topAppsCache.containsKey(entry))
172             return topAppsCache.get(entry);
173
174         Intent intent = new Intent();
175         intent.setComponent(ComponentName.unflattenFromString(entry.getComponentName()));
176         ActivityInfo activityInfo = intent.resolveActivityInfo(getContext().getPackageManager(), 0);
177
178         if(activityInfo != null) {
179             TopApps topApps = TopApps.getInstance(getContext());
180             boolean isTopApp = topApps.isTopApp(activityInfo.packageName + "/" + activityInfo.name + ":" + entry.getUserId(getContext()))
181                     || topApps.isTopApp(activityInfo.packageName + "/" + activityInfo.name)
182                     || topApps.isTopApp(activityInfo.name);
183
184             topAppsCache.put(entry, isTopApp);
185             return isTopApp;
186         }
187
188         topAppsCache.put(entry, false);
189         return false;
190     }
191
192     private void openContextMenu(final AppEntry entry, final int[] location) {
193         LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent("com.farmerbb.taskbar.HIDE_START_MENU_NO_RESET"));
194
195         Bundle args = new Bundle();
196         args.putSerializable("app_entry", entry);
197         args.putBoolean("launched_from_start_menu", true);
198         args.putInt("x", location[0]);
199         args.putInt("y", location[1]);
200
201         new Handler().postDelayed(() -> U.startContextMenuActivity(getContext(), args), shouldDelay() ? 100 : 0);
202     }
203
204     private boolean shouldDelay() {
205         SharedPreferences pref = U.getSharedPreferences(getContext());
206         return U.hasFreeformSupport(getContext())
207                 && pref.getBoolean("freeform_hack", false)
208                 && !FreeformHackHelper.getInstance().isFreeformHackActive();
209     }
210
211     public void updateList(List<AppEntry> list) {
212         updateList(list, false);
213     }
214
215     private void updateList(List<AppEntry> list, boolean firstUpdate) {
216         if(!firstUpdate) {
217             clear();
218
219             sections.clear();
220             gsfpCache.clear();
221             gpfsCache.clear();
222             topAppsCache.clear();
223
224             addAll(list);
225         }
226
227         SharedPreferences pref = U.getSharedPreferences(getContext());
228         if(pref.getBoolean("scrollbar", false)) {
229             for(AppEntry entry : list) {
230                 char firstLetter = getSectionForAppEntry(entry);
231                 if(!sections.contains(firstLetter))
232                     sections.add(firstLetter);
233             }
234         }
235     }
236
237     private char getSectionForAppEntry(AppEntry entry) {
238         if(isTopApp(entry))
239             return '\u2605';
240
241         char origChar = entry.getLabel().charAt(0);
242         if(uppercase.contains(origChar))
243             return origChar;
244
245         if(lowercase.contains(origChar))
246             return uppercase.get(lowercase.indexOf(origChar));
247
248         return '#';
249     }
250
251     @Override
252     public int getPositionForSection(int section) {
253         int cachedPos = gpfsCache.get(section, -1);
254         if(cachedPos != -1)
255             return cachedPos;
256
257         for(int i = 0; i < getCount(); i++) {
258             if(sections.get(section) == getSectionForAppEntry(getItem(i))) {
259                 gpfsCache.put(section, i);
260                 return i;
261             }
262         }
263
264         gpfsCache.put(section, 0);
265         return 0;
266     }
267
268     @Override
269     public int getSectionForPosition(int position) {
270         int cachedSection = gsfpCache.get(position, -1);
271         if(cachedSection != -1)
272             return cachedSection;
273
274         for(int i = 0; i < sections.size(); i++) {
275             if(sections.get(i) == getSectionForAppEntry(getItem(position))) {
276                 gsfpCache.put(position, i);
277                 return i;
278             }
279         }
280
281         gsfpCache.put(position, 0);
282         return 0;
283     }
284
285     @Override
286     public Object[] getSections() {
287         return sections.toArray();
288     }
289 }