/*
* Copyright 2013 serso aka se.solovyev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Contact details
*
* Email: se.solovyev@gmail.com
* Site: http://se.solovyev.org
*/
package org.solovyev.android.calculator;
import android.app.Activity;
import android.app.Application;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Build;
import android.os.IBinder;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.view.ContextThemeWrapper;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import org.solovyev.android.Check;
import org.solovyev.android.calculator.floating.FloatingCalculatorService;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public final class App {
public static final String TAG = "C++";
@Nonnull
private static Application application;
@Nonnull
private static SharedPreferences preferences;
private App() {
throw new AssertionError();
}
public static void init(@Nonnull CalculatorApplication application) {
App.application = application;
App.preferences = PreferenceManager.getDefaultSharedPreferences(application);
}
@Nonnull
public static Preferences.Gui.Theme getTheme() {
return Preferences.Gui.getTheme(preferences);
}
@Nonnull
public static Preferences.SimpleTheme getWidgetTheme() {
return Preferences.Widget.getTheme(preferences);
}
@Nonnull
public static Preferences.Gui.Theme getThemeFor(@Nonnull Context context) {
if (isFloatingCalculator(context)) {
final SharedPreferences p = preferences;
final Preferences.SimpleTheme onscreenTheme = Preferences.Onscreen.getTheme(p);
final Preferences.Gui.Theme appTheme = Preferences.Gui.getTheme(p);
return onscreenTheme.resolveThemeFor(appTheme).getAppTheme();
} else {
return App.getTheme();
}
}
@NonNull
private static Context unwrap(@NonNull Context context) {
if (context instanceof ContextThemeWrapper) {
return unwrap(((ContextThemeWrapper) context).getBaseContext());
}
return context;
}
public static void showDialog(@Nonnull DialogFragment dialogFragment,
@Nonnull String fragmentTag,
@Nonnull FragmentManager fm) {
final FragmentTransaction ft = fm.beginTransaction();
Fragment prev = fm.findFragmentByTag(fragmentTag);
if (prev != null) {
ft.remove(prev);
}
// Create and show the dialog.
dialogFragment.show(ft, fragmentTag);
}
@NonNull
public static SpannableString colorString(@Nonnull String s, int color) {
final SpannableString spannable = new SpannableString(s);
spannable.setSpan(new ForegroundColorSpan(color), 0, s.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannable;
}
@NonNull
public static String unspan(@Nonnull CharSequence spannable) {
return spannable.toString();
}
private static final AtomicInteger sNextViewId = new AtomicInteger(1);
public static int generateViewId() {
Check.isMainThread();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return View.generateViewId();
} else {
// Backwards compatible version, as given by fantouchx@gmail.com in
// http://stackoverflow.com/questions/6790623/#21000252
while (true) {
final int result = sNextViewId.get();
// aapt-generated IDs have the high byte non-zero. Clamp to the
// range below that.
int newValue = result + 1;
if (newValue > 0x00FFFFFF) {
newValue = 1;
}
if (sNextViewId.compareAndSet(result, newValue)) {
return result;
}
}
}
}
@Nullable
public static String find(@Nonnull List<String> tokens, @Nonnull String text, int position) {
for (int i = 0; i < tokens.size(); i++) {
final String token = tokens.get(i);
if (text.startsWith(token, position)) {
return token;
}
}
return null;
}
@NonNull
public static CalculatorApplication cast(@NonNull Fragment fragment) {
return cast(fragment.getActivity());
}
@NonNull
public static CalculatorApplication cast(@NonNull Context context) {
if (context instanceof CalculatorApplication) {
return (CalculatorApplication) context;
}
final Context appContext = context.getApplicationContext();
if (appContext instanceof CalculatorApplication) {
return (CalculatorApplication) appContext;
}
Check.shouldNotHappen();
return (CalculatorApplication) application;
}
public static void hideIme(@NonNull DialogFragment fragment) {
final Dialog dialog = fragment.getDialog();
if (dialog == null) {
return;
}
final View focusedView = dialog.getCurrentFocus();
if (focusedView == null) {
return;
}
hideIme(focusedView);
}
public static void hideIme(@NonNull View view) {
final IBinder token = view.getWindowToken();
if (token != null) {
InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(token, 0);
}
}
public static void showSystemPermissionSettings(@NonNull Activity activity,
@NonNull String action) {
try {
final Intent intent = new Intent(action);
intent.setData(Uri.parse("package:" + activity.getPackageName()));
activity.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.e(TAG,
"Failed to show permission settings for " + action, e);
}
}
public static boolean isTablet(@NonNull Context context) {
return context.getResources().getBoolean(R.bool.cpp_tablet);
}
static boolean isFloatingCalculator(@NonNull Context context) {
return unwrap(context) instanceof FloatingCalculatorService;
}
public static int getAppVersionCode(@Nonnull Context context) {
try {
return getAppVersionCode(context, context.getPackageName());
} catch (PackageManager.NameNotFoundException e) {
Check.shouldNotHappen();
}
return 0;
}
public static int getAppVersionCode(@Nonnull Context context, @Nonnull String appPackageName) throws PackageManager.NameNotFoundException {
return context.getPackageManager().getPackageInfo(appPackageName, 0).versionCode;
}
public static boolean isUiThread() {
return Looper.myLooper() == Looper.getMainLooper();
}
public static void restartActivity(@Nonnull Activity activity) {
final Intent intent = activity.getIntent();
activity.finish();
activity.startActivity(intent);
}
/**
* Method runs through view and all it's children recursively and process them via viewProcessor
*
* @param view parent view to be processed, if view is ViewGroup then all it's children will be processed
* @param viewProcessor object which processes views
*/
public static void processViews(@Nonnull View view, @Nonnull ViewProcessor<View> viewProcessor) {
processViewsOfType0(view, null, viewProcessor);
}
static <T> void processViewsOfType0(@Nonnull View view, @Nullable Class<T> viewClass, @Nonnull ViewProcessor<T> viewProcessor) {
if (view instanceof ViewGroup) {
final ViewGroup viewGroup = (ViewGroup) view;
if (viewClass == null || viewClass.isAssignableFrom(ViewGroup.class)) {
//noinspection unchecked
viewProcessor.process((T) viewGroup);
}
for (int index = 0; index < viewGroup.getChildCount(); index++) {
processViewsOfType0(viewGroup.getChildAt(index), viewClass, viewProcessor);
}
} else if (viewClass == null || viewClass.isAssignableFrom(view.getClass())) {
//noinspection unchecked
viewProcessor.process((T) view);
}
}
public static <T> void processViewsOfType(@Nonnull View view, @Nonnull Class<T> viewClass, @Nonnull ViewProcessor<T> viewProcessor) {
processViewsOfType0(view, viewClass, viewProcessor);
}
@Nonnull
public static <T> ArrayAdapter<T> makeSimpleSpinnerAdapter(@NonNull Context context) {
return new ArrayAdapter<>(context, R.layout.support_simple_spinner_dropdown_item);
}
public interface ViewProcessor<V> {
void process(@Nonnull V view);
}
@SuppressWarnings("deprecation")
public static int getScreenOrientation(@Nonnull Activity activity) {
final android.view.Display display = activity.getWindowManager().getDefaultDisplay();
if (display.getWidth() <= display.getHeight()) {
return Configuration.ORIENTATION_PORTRAIT;
} else {
return Configuration.ORIENTATION_LANDSCAPE;
}
}
public static void addIntentFlags(@Nonnull Intent intent, boolean detached, @Nonnull Context context) {
int flags = 0;
if (!(context instanceof Activity)) {
flags = flags | Intent.FLAG_ACTIVITY_NEW_TASK;
}
if (detached) {
flags = flags | Intent.FLAG_ACTIVITY_NO_HISTORY;
}
intent.setFlags(flags);
}
public static void enableComponent(@Nonnull Context context,
@Nonnull Class<?> componentClass,
boolean enable) {
final PackageManager pm = context.getPackageManager();
final int componentState;
if (enable) {
componentState = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
} else {
componentState = PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
}
pm.setComponentEnabledSetting(new ComponentName(context, componentClass), componentState, PackageManager.DONT_KILL_APP);
}
public static int toPixels(@Nonnull DisplayMetrics dm, float dps) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dps, dm);
}
public static int toPixels(@Nonnull Context context, float dps) {
return toPixels(context.getResources().getDisplayMetrics(), dps);
}
public static int toPixels(@Nonnull View view, float dps) {
return toPixels(view.getContext(), dps);
}
}