OSDN Git Service

Automatic translation import
[android-x86/packages-apps-Eleven.git] / src / com / andrew / apollo / utils / ApolloUtils.java
1 /*
2  * Copyright (C) 2012 Andrew Neal Licensed under the Apache License, Version 2.0
3  * (the "License"); you may not use this file except in compliance with the
4  * License. You may obtain a copy of the License at
5  * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
6  * or agreed to in writing, software distributed under the License is
7  * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
8  * KIND, either express or implied. See the License for the specific language
9  * governing permissions and limitations under the License.
10  */
11
12 package com.andrew.apollo.utils;
13
14 import android.annotation.SuppressLint;
15 import android.app.Activity;
16 import android.app.AlertDialog;
17 import android.content.Context;
18 import android.content.DialogInterface;
19 import android.content.DialogInterface.OnClickListener;
20 import android.content.Intent;
21 import android.content.res.Configuration;
22 import android.graphics.Bitmap;
23 import android.graphics.BitmapFactory;
24 import android.graphics.Color;
25 import android.graphics.Rect;
26 import android.net.ConnectivityManager;
27 import android.net.NetworkInfo;
28 import android.os.AsyncTask;
29 import android.os.Build;
30 import android.provider.MediaStore;
31 import android.util.Log;
32 import android.view.Gravity;
33 import android.view.View;
34 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
35 import android.webkit.WebView;
36 import android.widget.Toast;
37
38 import com.andrew.apollo.Config;
39 import com.andrew.apollo.R;
40 import com.andrew.apollo.cache.ImageCache;
41 import com.andrew.apollo.cache.ImageFetcher;
42 import com.andrew.apollo.ui.activities.ShortcutActivity;
43 import com.andrew.apollo.widgets.ColorPickerView;
44 import com.andrew.apollo.widgets.ColorSchemeDialog;
45 import com.devspark.appmsg.AppMsg;
46
47 /**
48  * Mostly general and UI helpers.
49  * 
50  * @author Andrew Neal (andrewdneal@gmail.com)
51  */
52 public final class ApolloUtils {
53
54     /**
55      * The threshold used calculate if a color is light or dark
56      */
57     private static final int BRIGHTNESS_THRESHOLD = 130;
58
59     /* This class is never initiated */
60     public ApolloUtils() {
61     }
62
63     /**
64      * Used to determine if the device is running Jelly Bean or greater
65      * 
66      * @return True if the device is running Jelly Bean or greater, false
67      *         otherwise
68      */
69     public static final boolean hasJellyBean() {
70         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
71     }
72
73     /**
74      * Used to determine if the device is running
75      * Jelly Bean MR2 (Android 4.3) or greater
76      *
77      * @return True if the device is running Jelly Bean MR2 or greater,
78      *         false otherwise
79      */
80     public static final boolean hasJellyBeanMR2() {
81         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2;
82     }
83
84     /**
85      * Used to determine if the device is a tablet or not
86      * 
87      * @param context The {@link Context} to use.
88      * @return True if the device is a tablet, false otherwise.
89      */
90     public static final boolean isTablet(final Context context) {
91         final int layout = context.getResources().getConfiguration().screenLayout;
92         return (layout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
93     }
94
95     /**
96      * Used to determine if the device is currently in landscape mode
97      * 
98      * @param context The {@link Context} to use.
99      * @return True if the device is in landscape mode, false otherwise.
100      */
101     public static final boolean isLandscape(final Context context) {
102         final int orientation = context.getResources().getConfiguration().orientation;
103         return orientation == Configuration.ORIENTATION_LANDSCAPE;
104     }
105
106     /**
107      * Execute an {@link AsyncTask} on a thread pool
108      * 
109      * @param forceSerial True to force the task to run in serial order
110      * @param task Task to execute
111      * @param args Optional arguments to pass to
112      *            {@link AsyncTask#execute(Object[])}
113      * @param <T> Task argument type
114      */
115     @SuppressLint("NewApi")
116     public static <T> void execute(final boolean forceSerial, final AsyncTask<T, ?, ?> task,
117             final T... args) {
118         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.DONUT) {
119             throw new UnsupportedOperationException(
120                     "This class can only be used on API 4 and newer.");
121         }
122         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB || forceSerial) {
123             task.execute(args);
124         } else {
125             task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, args);
126         }
127     }
128
129     /**
130      * Used to determine if there is an active data connection and what type of
131      * connection it is if there is one
132      * 
133      * @param context The {@link Context} to use
134      * @return True if there is an active data connection, false otherwise.
135      *         Also, if the user has checked to only download via Wi-Fi in the
136      *         settings, the mobile data and other network connections aren't
137      *         returned at all
138      */
139     public static final boolean isOnline(final Context context) {
140         /*
141          * This sort of handles a sudden configuration change, but I think it
142          * should be dealt with in a more professional way.
143          */
144         if (context == null) {
145             return false;
146         }
147
148         boolean state = false;
149         final boolean onlyOnWifi = PreferenceUtils.getInstance(context).onlyOnWifi();
150
151         /* Monitor network connections */
152         final ConnectivityManager connectivityManager = (ConnectivityManager)context
153                 .getSystemService(Context.CONNECTIVITY_SERVICE);
154
155         /* Wi-Fi connection */
156         final NetworkInfo wifiNetwork = connectivityManager
157                 .getNetworkInfo(ConnectivityManager.TYPE_WIFI);
158         if (wifiNetwork != null) {
159             state = wifiNetwork.isConnectedOrConnecting();
160         }
161
162         /* Mobile data connection */
163         final NetworkInfo mbobileNetwork = connectivityManager
164                 .getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
165         if (mbobileNetwork != null) {
166             if (!onlyOnWifi) {
167                 state = mbobileNetwork.isConnectedOrConnecting();
168             }
169         }
170
171         /* Other networks */
172         final NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
173         if (activeNetwork != null) {
174             if (!onlyOnWifi) {
175                 state = activeNetwork.isConnectedOrConnecting();
176             }
177         }
178
179         return state;
180     }
181
182     /**
183      * Display a {@link Toast} letting the user know what an item does when long
184      * pressed.
185      * 
186      * @param view The {@link View} to copy the content description from.
187      */
188     public static void showCheatSheet(final View view) {
189
190         final int[] screenPos = new int[2]; // origin is device display
191         final Rect displayFrame = new Rect(); // includes decorations (e.g.
192                                               // status bar)
193         view.getLocationOnScreen(screenPos);
194         view.getWindowVisibleDisplayFrame(displayFrame);
195
196         final Context context = view.getContext();
197         final int viewWidth = view.getWidth();
198         final int viewHeight = view.getHeight();
199         final int viewCenterX = screenPos[0] + viewWidth / 2;
200         final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
201         final int estimatedToastHeight = (int)(48 * context.getResources().getDisplayMetrics().density);
202
203         final Toast cheatSheet = Toast.makeText(context, view.getContentDescription(),
204                 Toast.LENGTH_SHORT);
205         final boolean showBelow = screenPos[1] < estimatedToastHeight;
206         if (showBelow) {
207             // Show below
208             // Offsets are after decorations (e.g. status bar) are factored in
209             cheatSheet.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, viewCenterX
210                     - screenWidth / 2, screenPos[1] - displayFrame.top + viewHeight);
211         } else {
212             // Show above
213             // Offsets are after decorations (e.g. status bar) are factored in
214             cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, viewCenterX
215                     - screenWidth / 2, displayFrame.bottom - screenPos[1]);
216         }
217         cheatSheet.show();
218     }
219
220     /**
221      * @param context The {@link Context} to use.
222      * @return An {@link AlertDialog} used to show the open source licenses used
223      *         in Apollo.
224      */
225     public static final AlertDialog createOpenSourceDialog(final Context context) {
226         final WebView webView = new WebView(context);
227         webView.loadUrl("file:///android_asset/licenses.html");
228         return new AlertDialog.Builder(context)
229                 .setTitle(R.string.settings_open_source_licenses)
230                 .setView(webView)
231                 .setPositiveButton(android.R.string.ok, null)
232                 .create();
233     }
234
235     /**
236      * Calculate whether a color is light or dark, based on a commonly known
237      * brightness formula.
238      * 
239      * @see {@literal http://en.wikipedia.org/wiki/HSV_color_space%23Lightness}
240      */
241     public static final boolean isColorDark(final int color) {
242         return (30 * Color.red(color) + 59 * Color.green(color) + 11 * Color.blue(color)) / 100 <= BRIGHTNESS_THRESHOLD;
243     }
244
245     /**
246      * Runs a piece of code after the next layout run
247      * 
248      * @param view The {@link View} used.
249      * @param runnable The {@link Runnable} used after the next layout run
250      */
251     @SuppressLint("NewApi")
252     public static void doAfterLayout(final View view, final Runnable runnable) {
253         final OnGlobalLayoutListener listener = new OnGlobalLayoutListener() {
254             @SuppressWarnings("deprecation")
255             @Override
256             public void onGlobalLayout() {
257                 /* Layout pass done, unregister for further events */
258                 if (hasJellyBean()) {
259                     view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
260                 } else {
261                     view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
262                 }
263                 runnable.run();
264             }
265         };
266         view.getViewTreeObserver().addOnGlobalLayoutListener(listener);
267     }
268
269     /**
270      * Creates a new instance of the {@link ImageCache} and {@link ImageFetcher}
271      * 
272      * @param activity The {@link Activity} to use.
273      * @return A new {@link ImageFetcher} used to fetch images asynchronously.
274      */
275     public static final ImageFetcher getImageFetcher(final Activity activity) {
276         final ImageFetcher imageFetcher = ImageFetcher.getInstance(activity);
277         imageFetcher.setImageCache(ImageCache.findOrCreateCache(activity));
278         return imageFetcher;
279     }
280
281     /**
282      * Used to create shortcuts for an artist, album, or playlist that is then
283      * placed on the default launcher homescreen
284      * 
285      * @param displayName The shortcut name
286      * @param id The ID of the artist, album, playlist, or genre
287      * @param mimeType The MIME type of the shortcut
288      * @param context The {@link Context} to use to
289      */
290     public static void createShortcutIntent(final String displayName, final String artistName,
291             final Long id, final String mimeType, final Activity context) {
292         try {
293             final ImageFetcher fetcher = getImageFetcher(context);
294             Bitmap bitmap = null;
295             if (mimeType.equals(MediaStore.Audio.Albums.CONTENT_TYPE)) {
296                 bitmap = fetcher.getCachedBitmap(
297                         ImageFetcher.generateAlbumCacheKey(displayName, artistName));
298             } else {
299                 bitmap = fetcher.getCachedBitmap(displayName);
300             }
301             if (bitmap == null) {
302                 bitmap = BitmapFactory.decodeResource(context.getResources(),
303                         R.drawable.default_artwork);
304             }
305
306             // Intent used when the icon is touched
307             final Intent shortcutIntent = new Intent(context, ShortcutActivity.class);
308             shortcutIntent.setAction(Intent.ACTION_VIEW);
309             shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
310             shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
311             shortcutIntent.putExtra(Config.ID, id);
312             shortcutIntent.putExtra(Config.NAME, displayName);
313             shortcutIntent.putExtra(Config.MIME_TYPE, mimeType);
314
315             // Intent that actually sets the shortcut
316             final Intent intent = new Intent();
317             intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, BitmapUtils.resizeAndCropCenter(bitmap, 96));
318             intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
319             intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, displayName);
320             intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
321             context.sendBroadcast(intent);
322             AppMsg.makeText(context,
323                     context.getString(R.string.pinned_to_home_screen, displayName),
324                     AppMsg.STYLE_CONFIRM).show();
325         } catch (final Exception e) {
326             Log.e("ApolloUtils", "createShortcutIntent", e);
327             AppMsg.makeText(
328                     context,
329                     context.getString(R.string.could_not_be_pinned_to_home_screen, displayName),
330                     AppMsg.STYLE_ALERT).show();
331         }
332     }
333
334     /**
335      * Shows the {@link ColorPickerView}
336      * 
337      * @param context The {@link Context} to use.
338      */
339     public static void showColorPicker(final Context context) {
340         final ColorSchemeDialog colorPickerView = new ColorSchemeDialog(context);
341         colorPickerView.setButton(AlertDialog.BUTTON_POSITIVE,
342                 context.getString(android.R.string.ok), new OnClickListener() {
343
344                     @Override
345                     public void onClick(final DialogInterface dialog, final int which) {
346                         PreferenceUtils.getInstance(context).setDefaultThemeColor(
347                                 colorPickerView.getColor());
348                     }
349                 });
350         colorPickerView.setButton(AlertDialog.BUTTON_NEGATIVE,
351                 context.getString(R.string.cancel), (OnClickListener) null);
352         colorPickerView.show();
353     }
354
355     /**
356      * Method that removes the support for HardwareAcceleration from a {@link View}.<br/>
357      * <br/>
358      * Check AOSP notice:<br/>
359      * <pre>
360      * 'ComposeShader can only contain shaders of different types (a BitmapShader and a
361      * LinearGradient for instance, but not two instances of BitmapShader)'. But, 'If your
362      * application is affected by any of these missing features or limitations, you can turn
363      * off hardware acceleration for just the affected portion of your application by calling
364      * setLayerType(View.LAYER_TYPE_SOFTWARE, null).'</pre>
365      *
366      * @param v The view
367      */
368     public static void removeHardwareAccelerationSupport(View v) {
369         if (v.getLayerType() != View.LAYER_TYPE_SOFTWARE) {
370             v.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
371         }
372    }
373 }