extracting common classes to another project

This commit is contained in:
Sergey Solovyev 2012-01-15 01:50:37 +04:00
parent 461126bee8
commit c46872b978
96 changed files with 22 additions and 5458 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 493 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 933 B

View File

@ -1,50 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2009-2011. Created by serso aka se.solovyev.
~ For more information, please, contact se.solovyev@gmail.com
~ or visit http://se.solovyev.org
-->
<!-- Copyright (C) 2008 The Android Open Source Project
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.
-->
<selector xmlns:a="http://schemas.android.com/apk/res/android">
<item a:state_pressed="false"
a:state_enabled="true"
a:state_focused="false"
a:drawable="@drawable/timepicker_down_normal" />
<item a:state_pressed="true"
a:state_enabled="true"
a:drawable="@drawable/timepicker_down_pressed" />
<item a:state_pressed="false"
a:state_enabled="true"
a:state_focused="true"
a:drawable="@drawable/timepicker_down_selected" />
<item a:state_pressed="false"
a:state_enabled="false"
a:state_focused="false"
a:drawable="@drawable/timepicker_down_disabled" />
<item a:state_pressed="false"
a:state_enabled="false"
a:state_focused="true"
a:drawable="@drawable/timepicker_down_disabled_focused" />
</selector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 580 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 795 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2009-2011. Created by serso aka se.solovyev.
~ For more information, please, contact se.solovyev@gmail.com
~ or visit http://se.solovyev.org
-->
<!-- Copyright (C) 2008 The Android Open Source Project
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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false" android:state_enabled="true"
android:state_focused="false" android:drawable="@drawable/timepicker_input_normal" />
<item android:state_pressed="true" android:state_enabled="true"
android:drawable="@drawable/timepicker_input_pressed" />
<item android:state_pressed="false" android:state_enabled="true"
android:state_focused="true" android:drawable="@drawable/timepicker_input_selected" />
<item android:state_pressed="false" android:state_enabled="false"
android:state_focused="false" android:drawable="@drawable/timepicker_input_disabled" />
<item android:state_pressed="false" android:state_enabled="false"
android:state_focused="true" android:drawable="@drawable/timepicker_input_normal" />
</selector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 517 B

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2009-2011. Created by serso aka se.solovyev.
~ For more information, please, contact se.solovyev@gmail.com
~ or visit http://se.solovyev.org
-->
<!-- Copyright (C) 2008 The Android Open Source Project
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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false" android:state_enabled="true"
android:state_focused="false" android:drawable="@drawable/timepicker_up_normal"/>
<item android:state_pressed="true" android:state_enabled="true"
android:drawable="@drawable/timepicker_up_pressed"/>
<item android:state_pressed="false" android:state_enabled="true"
android:state_focused="true" android:drawable="@drawable/timepicker_up_selected"/>
<item android:state_pressed="false" android:state_enabled="false"
android:state_focused="false" android:drawable="@drawable/timepicker_up_disabled"/>
<item android:state_pressed="false" android:state_enabled="false"
android:state_focused="true" android:drawable="@drawable/timepicker_up_disabled_focused"/>
</selector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 728 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -1,22 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<declare-styleable name="DirectionDragButton">
<attr name="textUp" format="string"/>
<attr name="textDown" format="string"/>
<attr name="textRight" format="string"/>
<attr name="textLeft" format="string"/>
<attr name="directionTextScale" format="string"/>
<attr name="directionTextAlpha" format="integer"/>
</declare-styleable>
<declare-styleable name="DragButton">
<attr name="hTextPosition" format="float"/>
</declare-styleable>
<declare-styleable name="NumberRangeSeekBar">
<attr name="boundaries" format="string"/>
<attr name="steps" format="integer"/>
</declare-styleable>
<attr name="digitButtonStyle" format="reference" /> <attr name="digitButtonStyle" format="reference" />
<attr name="controlButtonStyle" format="reference" /> <attr name="controlButtonStyle" format="reference" />

View File

@ -1,207 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TabHost;
import android.widget.TextView;
import com.google.ads.AdRequest;
import com.google.ads.AdSize;
import com.google.ads.AdView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* User: serso
* Date: 12/21/11
* Time: 11:54 PM
*/
public final class AndroidUtils {
// not intended for instantiation
private AndroidUtils() {
throw new AssertionError();
}
public static void centerAndWrapTabsFor(@NotNull TabHost tabHost) {
if (allowCenterAndWrappingTabs()) {
int tabCount = tabHost.getTabWidget().getTabCount();
for (int i = 0; i < tabCount; i++) {
final View view = tabHost.getTabWidget().getChildTabViewAt(i);
if (view != null) {
if (view.getLayoutParams().height > 0) {
// reduce height of the tab
view.getLayoutParams().height *= 0.8;
}
// get title text view
final View textView = view.findViewById(android.R.id.title);
if (textView instanceof TextView) {
// just in case check the type
// center text
((TextView) textView).setGravity(Gravity.CENTER);
// wrap text
((TextView) textView).setSingleLine(false);
// explicitly set layout parameters
textView.getLayoutParams().height = ViewGroup.LayoutParams.FILL_PARENT;
textView.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
}
}
}
}
}
private static boolean allowCenterAndWrappingTabs() {
boolean result = true;
String deviceModel = Build.MODEL;
if (deviceModel != null) {
deviceModel = deviceModel.toUpperCase();
if (deviceModel.contains("M1") || deviceModel.contains("MIONE") || deviceModel.contains("MI-ONE")) {
// Xiaomi Phone MiOne => do not allow to center and wrap tabs
result = false;
Log.i(AndroidUtils.class.getName(), "Device model doesn't support center and wrap of tabs: " + Build.MODEL);
}
}
if (result) {
String buildId = Build.DISPLAY;
if (buildId != null) {
buildId = buildId.toUpperCase();
if (buildId.contains("MIUI")) {
// fix for MIUI ROM
result = false;
Log.i(AndroidUtils.class.getName(), "Device build doesn't support center and wrap of tabs: " + Build.DISPLAY);
}
}
}
return result;
}
public static void addTab(@NotNull Context context,
@NotNull TabHost tabHost,
@NotNull String tabId,
int tabCaptionId,
@NotNull Class<? extends Activity> activityClass) {
TabHost.TabSpec spec;
final Intent intent = new Intent().setClass(context, activityClass);
// Initialize a TabSpec for each tab and add it to the TabHost
spec = tabHost.newTabSpec(tabId).setIndicator(context.getString(tabCaptionId)).setContent(intent);
tabHost.addTab(spec);
}
/**
* @param context context
* @param appPackageName - full name of the package of an app, 'com.example.app' for example.
* @return version number we are currently in
*/
public static int getAppVersionCode(@NotNull Context context, @NotNull String appPackageName) {
try {
return context.getPackageManager().getPackageInfo(appPackageName, 0).versionCode;
} catch (PackageManager.NameNotFoundException e) {
// App not installed!
}
return -1;
}
@NotNull
public static AdView createAndInflateAdView(@NotNull Activity activity,
@NotNull String admobAccountId,
@Nullable ViewGroup parentView,
int layoutId,
@NotNull List<String> keywords) {
final ViewGroup layout = parentView != null ? parentView : (ViewGroup) activity.findViewById(layoutId);
// Create the adView
final AdView adView = new AdView(activity, AdSize.BANNER, admobAccountId);
// Add the adView to it
layout.addView(adView);
// Initiate a generic request to load it with an ad
final AdRequest adRequest = new AdRequest();
// todo serso: revert - only for tests
//adRequest.addTestDevice(AdRequest.TEST_EMULATOR);
//adRequest.addTestDevice("DB3C2F605A1296971898F0E60224A927");
for (String keyword : keywords) {
adRequest.addKeyword(keyword);
}
adView.loadAd(adRequest);
return adView;
}
public static <T> void processViewsOfType(@NotNull View view, @NotNull Class<T> clazz, @NotNull ViewProcessor<T> viewProcessor) {
processViewsOfType0(view, clazz, viewProcessor);
}
public static void processViews(@NotNull View view, @NotNull ViewProcessor<View> viewProcessor) {
processViewsOfType0(view, null, viewProcessor);
}
private static <T> void processViewsOfType0(@NotNull View view, @Nullable Class<T> clazz, @NotNull ViewProcessor<T> viewProcessor) {
if (view instanceof ViewGroup) {
final ViewGroup viewGroup = (ViewGroup) view;
if (clazz == null || ViewGroup.class.isAssignableFrom(clazz)) {
//noinspection unchecked
viewProcessor.process((T) viewGroup);
}
for (int index = 0; index < viewGroup.getChildCount(); index++) {
processViewsOfType0(viewGroup.getChildAt(index), clazz, viewProcessor);
}
} else if (clazz == null || view.getClass().isAssignableFrom(clazz)) {
//noinspection unchecked
viewProcessor.process((T) view);
}
}
public static interface ViewProcessor<V> {
void process(@NotNull V view);
}
public static void restartActivity(@NotNull Activity activity) {
final Intent intent = activity.getIntent();
/*
for compatibility with android_1.6_compatibility
overridePendingTransition(0, 0);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);*/
Log.d(activity.getClass().getName(), "Finishing current activity!");
activity.finish();
/*
for compatibility with android_1.6_compatibility
overridePendingTransition(0, 0);*/
Log.d(activity.getClass().getName(), "Starting new activity!");
activity.startActivity(intent);
}
}

View File

@ -1,233 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.view.widgets.DragButton;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* User: serso
* Date: 11/25/11
* Time: 1:52 PM
*/
public enum ResourceCache {
instance;
@NotNull
private static final Locale DEFAULT_LOCALE = Locale.ENGLISH;
// ids of drag buttons in R.class
private List<Integer> dragButtonIds = null;
// ids of buttons in R.class
private List<Integer> buttonIds = null;
// first map: key: language id, value: map of captions and translations
// second mal: key: caption id, value: translation
private final Map<String, Map<String, String>> captions = new HashMap<String, Map<String, String>>();
private Class<?> resourceClass;
private Context context;
@NotNull
private final NameToIdCache nameToIdCache = new NameToIdCache();
public List<Integer> getDragButtonIds() {
return dragButtonIds;
}
public List<Integer> getButtonIds() {
return buttonIds;
}
/**
* Method load captions for default locale using android R class
*
* @param context STATIC CONTEXT
* @param resourceClass class of captions in android (SUBCLASS of R class)
*/
public void initCaptions(@NotNull Context context, @NotNull Class<?> resourceClass) {
initCaptions(context, resourceClass, Locale.getDefault());
}
/**
* Method load captions for specified locale using android R class
*
* @param context STATIC CONTEXT
* @param resourceClass class of captions in android (SUBCLASS of R class)
* @param locale language to be used for translation
*/
public void initCaptions(@NotNull Context context, @NotNull Class<?> resourceClass, @NotNull Locale locale) {
assert this.resourceClass == null || this.resourceClass.equals(resourceClass);
this.context = context;
this.resourceClass = resourceClass;
if (!initialized(locale)) {
final Map<String, String> captionsByLanguage = new HashMap<String, String>();
final Locale defaultLocale = Locale.getDefault();
try {
if (!defaultLocale.getLanguage().equals(locale.getLanguage())) {
updateConfiguration(context, locale);
}
for (Field field : resourceClass.getDeclaredFields()) {
int modifiers = field.getModifiers();
if (Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers)) {
try {
int captionId = field.getInt(resourceClass);
captionsByLanguage.put(field.getName(), context.getString(captionId));
} catch (IllegalAccessException e) {
Log.e(ResourceCache.class.getName(), e.getMessage());
} catch (Resources.NotFoundException e) {
Log.e(ResourceCache.class.getName(), "Caption with name " + field.getName() + " was not found for " + locale.getLanguage() + " language: " + e.getMessage());
}
}
}
} finally {
if (!defaultLocale.getLanguage().equals(locale.getLanguage())) {
updateConfiguration(context, defaultLocale);
}
}
captions.put(locale.getLanguage(), captionsByLanguage);
}
}
private static void updateConfiguration(@NotNull Context context, @NotNull Locale locale) {
Locale.setDefault(locale);
final Configuration config = new Configuration();
config.locale = locale;
context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
}
private boolean initialized(@NotNull Locale locale) {
return captions.containsKey(locale.getLanguage());
}
/**
* @param captionId id of caption to be translated
* @return translation by caption id in default language, null if no translation in default language present
*/
@Nullable
public String getCaption(@NotNull String captionId) {
return getCaption(captionId, Locale.getDefault());
}
/**
* @param captionId id of caption to be translated
* @param locale language to be used for translation
* @return translation by caption id in specified language, null if no translation in specified language present
*/
@Nullable
public String getCaption(@NotNull String captionId, @NotNull final Locale locale) {
Map<String, String> captionsByLanguage = captions.get(locale.getLanguage());
if (captionsByLanguage != null) {
return getCaption(captionsByLanguage, captionId, locale);
} else {
assert resourceClass != null && context != null;
initCaptions(context, resourceClass, locale);
captionsByLanguage = captions.get(locale.getLanguage());
if (captionsByLanguage != null) {
return getCaption(captionsByLanguage, captionId, locale);
}
}
return null;
}
@Nullable
private String getCaption(@NotNull Map<String, String> captionsByLanguage, @NotNull String captionId, @NotNull Locale locale) {
String result = captionsByLanguage.get(captionId);
if (result == null && !locale.getLanguage().equals(DEFAULT_LOCALE.getLanguage())) {
result = getCaption(captionId, DEFAULT_LOCALE);
}
return result;
}
public void init(@NotNull Class<?> resourceClass, @NotNull Activity activity) {
dragButtonIds = new ArrayList<Integer>();
buttonIds = new ArrayList<Integer>();
for (Field field : resourceClass.getDeclaredFields()) {
int modifiers = field.getModifiers();
if (Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers)) {
try {
int viewId = field.getInt(resourceClass);
final View view = activity.findViewById(viewId);
if (view instanceof DragButton) {
dragButtonIds.add(viewId);
}
if (view instanceof Button) {
buttonIds.add(viewId);
}
} catch (IllegalAccessException e) {
Log.e(ResourceCache.class.getName(), e.getMessage());
}
}
}
}
@NotNull
public Map<String, Integer> getNameToIdCache(@NotNull Class<?> clazz) {
return this.nameToIdCache.getCache(clazz);
}
private static class NameToIdCache {
@NotNull
private final Map<Class<?>, Map<String, Integer>> caches = new HashMap<Class<?>, Map<String, Integer>>(3);
// not intended for instantiation
private NameToIdCache() {
}
@NotNull
public Map<String, Integer> getCache(@NotNull Class<?> clazz) {
Map<String, Integer> result = caches.get(clazz);
if (result == null) {
result = new HashMap<String, Integer>();
for (Field field : clazz.getDeclaredFields()) {
int modifiers = field.getModifiers();
if (Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers)) {
try {
result.put(field.getName(), field.getInt(clazz));
} catch (IllegalAccessException e) {
Log.e(ResourceCache.class.getName(), e.getMessage());
}
}
}
caches.put(clazz, result);
}
return result;
}
}
}

View File

@ -1,17 +1,8 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.ViewGroup;
import com.google.ads.AdView;
import net.robotmedia.billing.BillingController; import net.robotmedia.billing.BillingController;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.solovyev.android.ads.AdsController;
import org.solovyev.android.AndroidUtils;
import java.util.Collections;
import java.util.List;
/** /**
* User: serso * User: serso
@ -37,53 +28,11 @@ public class CalculatorApplication extends android.app.Application {
return instance; return instance;
} }
private static boolean isAdFreePurchased(@NotNull Context context) {
return BillingController.isPurchased(context.getApplicationContext(), AD_FREE_PRODUCT_ID);
}
private static boolean transactionsRestored = false;
public static boolean isAdFree(@NotNull Context context) {
// check if user already bought this product
boolean purchased = isAdFreePurchased(context);
if (!purchased && !transactionsRestored) {
// we must to restore all transactions done by user to guarantee that product was purchased or not
BillingController.restoreTransactions(context);
transactionsRestored = true;
// todo serso: may be call net.robotmedia.billing.BillingController.restoreTransactions() always before first check and get rid of second check
// check the billing one more time
purchased = isAdFreePurchased(context);
}
return purchased;
}
@Nullable
public static AdView inflateAd(@NotNull Activity activity) {
return inflateAd(activity, null, R.id.ad_parent_view);
}
@Nullable
public static AdView inflateAd(@NotNull Activity activity, @Nullable ViewGroup parentView, int parentViewId) {
AdView result = null;
if ( !isAdFree(activity) ) {
Log.d(activity.getClass().getName(), "Application is not ad free - inflating ad!");
final List<String> keywords = Collections.emptyList();
result = AndroidUtils.createAndInflateAdView(activity, ADMOB_USER_ID, parentView, parentViewId, keywords);
} else {
Log.d(activity.getClass().getName(), "Application is ad free - no ads!");
}
return result;
}
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
//BillingController.setDebug(true); AdsController.getInstance().init(ADMOB_USER_ID, AD_FREE_PRODUCT_ID, new BillingController.IConfiguration() {
BillingController.setConfiguration(new BillingController.IConfiguration() {
@Override @Override
public byte[] getObfuscationSalt() { public byte[] getObfuscationSalt() {

View File

@ -19,7 +19,6 @@ import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.model.CalculatorEngine; import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.model.CalculatorParseException; import org.solovyev.android.calculator.model.CalculatorParseException;
import org.solovyev.android.calculator.model.TextProcessor; import org.solovyev.android.calculator.model.TextProcessor;
import org.solovyev.android.view.AMenuBuilder;
import org.solovyev.android.view.AMenuItem; import org.solovyev.android.view.AMenuItem;
import org.solovyev.android.view.AutoResizeTextView; import org.solovyev.android.view.AutoResizeTextView;

View File

@ -18,6 +18,7 @@ import net.robotmedia.billing.BillingRequest;
import net.robotmedia.billing.IBillingObserver; import net.robotmedia.billing.IBillingObserver;
import net.robotmedia.billing.model.Transaction; import net.robotmedia.billing.model.Transaction;
import org.solovyev.android.AndroidUtils; import org.solovyev.android.AndroidUtils;
import org.solovyev.android.ads.AdsController;
import org.solovyev.android.calculator.model.CalculatorEngine; import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.view.widgets.VibratorContainer; import org.solovyev.android.view.widgets.VibratorContainer;
@ -63,7 +64,7 @@ public class CalculatorPreferencesActivity extends PreferenceActivity implements
private void setAdFreeAction() { private void setAdFreeAction() {
final Preference adFreePreference = findPreference(CalculatorApplication.AD_FREE_P_KEY); final Preference adFreePreference = findPreference(CalculatorApplication.AD_FREE_P_KEY);
if (!CalculatorApplication.isAdFree(this)) { if (!AdsController.getInstance().isAdFree(this)) {
Log.d(CalculatorPreferencesActivity.class.getName(), "Ad free is not purchased - enable preference!"); Log.d(CalculatorPreferencesActivity.class.getName(), "Ad free is not purchased - enable preference!");
adFreePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { adFreePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@ -76,7 +77,7 @@ public class CalculatorPreferencesActivity extends PreferenceActivity implements
new AlertDialog.Builder(CalculatorPreferencesActivity.this).setTitle(R.string.c_error).setMessage(R.string.c_billing_error).create().show(); new AlertDialog.Builder(CalculatorPreferencesActivity.this).setTitle(R.string.c_error).setMessage(R.string.c_billing_error).create().show();
} else { } else {
Log.d(CalculatorPreferencesActivity.class.getName(), "Billing is supported - continue!"); Log.d(CalculatorPreferencesActivity.class.getName(), "Billing is supported - continue!");
if (!CalculatorApplication.isAdFree(CalculatorPreferencesActivity.this)) { if (!AdsController.getInstance().isAdFree(CalculatorPreferencesActivity.this)) {
Log.d(CalculatorPreferencesActivity.class.getName(), "Item not purchased - try to purchase!"); Log.d(CalculatorPreferencesActivity.class.getName(), "Item not purchased - try to purchase!");
// not purchased => purchasing // not purchased => purchasing

View File

@ -20,6 +20,16 @@ public final class CalculatorSecurity {
@NotNull @NotNull
public static String getPK() { public static String getPK() {
return "org.solovyev.android.calculator"; final StringBuilder result = new StringBuilder();
result.append("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A");
result.append("MIIBCgKCAQEAquP2a7dEhTaJEQeXtSyreH5dCmTDOd");
result.append("dElCfg0ijOeB8JTxBiJTXLWnLA0kMaT/sRXswUaYI61YCQOoik82");
result.append("qrFH7W4+OFtiLb8WGX+YPEpQQ/IBZu9qm3xzS9Nolu79EBff0/CLa1FuT9RtjO");
result.append("iTW8Q0VP9meQdJEkfqJEyVCgHain+MGoQaRXI45EzkYmkz8TBx6X6aJF5NBAXnAWeyD0wPX1");
result.append("uedHH7+LgLcjnPVw82YjyJSzYnaaD2GX0Y7PGoFe6J5K4yJGGX5mih45pe2HWcG5lAkQhu1uX2hCcCBdF3");
result.append("W7paRq9mJvCsbn+BNTh9gq8QKui0ltmiWpa5U+/9L+FQIDAQAB");
return result.toString();
} }
} }

View File

@ -10,7 +10,7 @@ import android.app.Activity;
import android.os.Bundle; import android.os.Bundle;
import com.google.ads.AdView; import com.google.ads.AdView;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorApplication; import org.solovyev.android.ads.AdsController;
/** /**
* User: serso * User: serso
@ -35,7 +35,7 @@ public class AbstractHelpActivity extends Activity {
setContentView(layoutId); setContentView(layoutId);
// do not inflate ad in help (as some problems were encountered dut to ScrollView - no space for ad banner) // do not inflate ad in help (as some problems were encountered dut to ScrollView - no space for ad banner)
adView = CalculatorApplication.inflateAd(this); adView = AdsController.getInstance().inflateAd(this);
} }
@Override @Override

View File

@ -19,7 +19,7 @@ import android.widget.ListView;
import com.google.ads.AdView; import com.google.ads.AdView;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorApplication; import org.solovyev.android.ads.AdsController;
import org.solovyev.android.calculator.CalculatorModel; import org.solovyev.android.calculator.CalculatorModel;
import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
@ -67,7 +67,7 @@ public abstract class AbstractHistoryActivity extends ListActivity {
setContentView(R.layout.history_activity); setContentView(R.layout.history_activity);
adView = CalculatorApplication.inflateAd(this); adView = AdsController.getInstance().inflateAd(this);
adapter = new HistoryArrayAdapter(this, getLayoutId(), R.id.history_item, new ArrayList<CalculatorHistoryState>()); adapter = new HistoryArrayAdapter(this, getLayoutId(), R.id.history_item, new ArrayList<CalculatorHistoryState>());
setListAdapter(adapter); setListAdapter(adapter);

View File

@ -18,7 +18,7 @@ import android.widget.*;
import com.google.ads.AdView; import com.google.ads.AdView;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorApplication; import org.solovyev.android.ads.AdsController;
import org.solovyev.android.calculator.CalculatorModel; import org.solovyev.android.calculator.CalculatorModel;
import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.model.AndroidMathRegistry; import org.solovyev.android.calculator.model.AndroidMathRegistry;
@ -90,7 +90,7 @@ public abstract class AbstractMathEntityListActivity<T extends MathEntity> exten
setContentView(getLayoutId()); setContentView(getLayoutId());
adView = CalculatorApplication.inflateAd(this); adView = AdsController.getInstance().inflateAd(this);
final Intent intent = getIntent(); final Intent intent = getIntent();
if ( intent != null ) { if ( intent != null ) {

View File

@ -1,40 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.msg;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.ResourceCache;
import org.solovyev.common.msg.AbstractMessage;
import org.solovyev.common.msg.MessageType;
import java.util.List;
import java.util.Locale;
/**
* User: serso
* Date: 10/18/11
* Time: 11:57 PM
*/
public class AndroidMessage extends AbstractMessage {
public AndroidMessage(@NotNull String messageCode,
@NotNull MessageType messageType,
@org.jetbrains.annotations.Nullable Object... arguments) {
super(messageCode, messageType, arguments);
}
public AndroidMessage(@NotNull String messageCode,
@NotNull MessageType messageType,
@NotNull List<?> arguments) {
super(messageCode, messageType, arguments);
}
@Override
protected String getMessagePattern(@NotNull Locale locale) {
return ResourceCache.instance.getCaption(getMessageCode());
}
}

View File

@ -1,24 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view;
import android.content.Context;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 12/18/11
* Time: 1:30 PM
*/
public interface AMenu<T extends AMenuItem<D>, D> {
@Nullable
T itemAt(int i);
@NotNull
CharSequence[] getMenuCaptions(@NotNull final Context context);
}

View File

@ -1,60 +0,0 @@
package org.solovyev.android.view;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 12/19/11
* Time: 10:54 AM
*/
public class AMenuBuilder<T extends AMenuItem<D>, D>{
@NotNull
private final Context context;
@NotNull
private final AlertDialog.Builder menuBuilder;
@NotNull
private final AMenu<T, D> menu;
@NotNull
public static <T extends Enum & AMenuItem<D>, D> AMenuBuilder<T, D> newInstance(@NotNull Context context, @NotNull Class<T> enumClass) {
return new AMenuBuilder<T, D>(context, EnumMenu.<T, D>newInstance(enumClass));
}
@NotNull
public static <T extends AMenuItem<D>, D> AMenuBuilder<T, D> newInstance(@NotNull Context context, @NotNull AMenu<T, D> menu) {
return new AMenuBuilder<T, D>(context, menu);
}
private AMenuBuilder(@NotNull Context context, @NotNull AMenu<T, D> menu) {
this.context = context;
this.menuBuilder = new AlertDialog.Builder(context);
this.menu = menu;
}
@NotNull
public AlertDialog.Builder getMenuBuilder() {
return menuBuilder;
}
@NotNull
public AlertDialog create(@NotNull final D data) {
menuBuilder.setItems(menu.getMenuCaptions(context), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
final AMenuItem<D> menuItem = menu.itemAt(item);
if (menuItem != null) {
menuItem.doAction(data, context);
}
}
});
return menuBuilder.create();
}
}

View File

@ -1,23 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view;
import android.content.Context;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 12/18/11
* Time: 1:29 PM
*/
public interface AMenuItem<T> {
@NotNull
String getCaption(@NotNull Context context);
void doAction(@NotNull T data, @NotNull Context context);
}

View File

@ -1,301 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view;
import android.content.Context;
import android.graphics.Canvas;
import android.text.Editable;
import android.text.Layout.Alignment;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;
/**
* Text view that auto adjusts text size to fit within the view.
* If the text size equals the minimum text size and still does not
* fit, append with an ellipsis.
*
* @author Chase Colburn
* @since Apr 4, 2011
*/
public class AutoResizeTextView extends TextView {
// Minimum text size for this text view
public static final float MIN_TEXT_SIZE = 20;
private float initialTextSize = 100;
// Interface for resize notifications
public interface OnTextResizeListener {
public void onTextResize(TextView textView, float oldSize, float newSize);
}
// Off screen canvas for text size rendering
private static final Canvas textResizeCanvas = new Canvas();
// Our ellipse string
private static final String ellipsis = "...";
// Registered resize listener
private OnTextResizeListener textResizeListener;
// Flag for text and/or size changes to force a resize
private boolean needsResize = false;
// Lower bounds for text size
private float minTextSize = MIN_TEXT_SIZE;
// Text view line spacing multiplier
private float spacingMult = 1.0f;
// Text view additional line spacing
private float spacingAdd = 0.0f;
// Add ellipsis to text that overflows at the smallest text size
private boolean addEllipsis = true;
// Default constructor override
public AutoResizeTextView(Context context) {
this(context, null);
}
// Default constructor when inflating from XML file
public AutoResizeTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
// Default constructor override
public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* When text changes, set the force resize flag to true and resetInterpreter the text size.
*/
@Override
protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {
needsResize = true;
// Since this view may be reused, it is good to resetInterpreter the text size
}
/**
* If the text view size changed, set the force resize flag to true
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != oldw || h != oldh) {
needsResize = true;
}
}
/**
* Register listener to receive resize notifications
*
* @param listener
*/
public void setOnResizeListener(OnTextResizeListener listener) {
textResizeListener = listener;
}
/**
* Override the set text size to update our internal reference values
*/
@Override
public void setTextSize(int unit, float size) {
super.setTextSize(unit, size);
}
/**
* Override the set line spacing to update our internal reference values
*/
@Override
public void setLineSpacing(float add, float mult) {
super.setLineSpacing(add, mult);
spacingMult = mult;
spacingAdd = add;
}
/**
* Set the lower text size limit and invalidate the view
*
* @param minTextSize
*/
public void setMinTextSize(float minTextSize) {
this.minTextSize = minTextSize;
requestLayout();
invalidate();
}
/**
* Return lower text size limit
*
* @return
*/
public float getMinTextSize() {
return minTextSize;
}
/**
* Set flag to add ellipsis to text that overflows at the smallest text size
*
* @param addEllipsis
*/
public void setAddEllipsis(boolean addEllipsis) {
this.addEllipsis = addEllipsis;
}
/**
* Return flag to add ellipsis to text that overflows at the smallest text size
*
* @return
*/
public boolean getAddEllipsis() {
return addEllipsis;
}
/**
* Resize text after measuring
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (changed || needsResize) {
int widthLimit = (right - left) - getCompoundPaddingLeft() - getCompoundPaddingRight();
int heightLimit = (bottom - top) - getCompoundPaddingBottom() - getCompoundPaddingTop();
resizeText(widthLimit, heightLimit, getText());
}
super.onLayout(changed, left, top, right, bottom);
}
/**
* Resize the text size with default width and height
*/
public void resizeText() {
resizeText(getText());
}
private void resizeText(final CharSequence text) {
int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop();
int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight();
resizeText(widthLimit, heightLimit, text);
}
/**
* Resize the text size with specified width and height
*
* @param width
* @param height
* @param text
*/
private void resizeText(int width, int height, CharSequence text) {
Log.d(this.getClass().getName(), "Resizing: w=" + width + ", h=" + height + ", text='" + text + "'");
// Do not resize if the view does not have dimensions or there is no text
if (text == null || text.length() == 0 || height <= 0 || width <= 0) {
return;
}
// Get the text view's paint object
TextPaint textPaint = getPaint();
// Store the current text size
float oldTextSize = textPaint.getTextSize();
Log.d(this.getClass().getName(), "Old text size: " + oldTextSize);
// If there is a max text size set, use the lesser of that and the default text size
// todo serso: +2 is a workaround => to be checked boundary constraints
float newTextSize = initialTextSize + 2;
int newTextHeight;
if (text instanceof Editable) {
((Editable) text).append("|");
}
try {
// Get the required text height
newTextHeight = getTextRect(text, textPaint, width, newTextSize);
logDimensions(newTextSize, newTextHeight);
if (newTextHeight > height) {
// Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes
while (newTextHeight > height) {
if (newTextSize <= minTextSize) {
break;
}
newTextSize = Math.max(newTextSize - 1, minTextSize);
newTextHeight = getTextRect(text, textPaint, width, newTextSize);
logDimensions(newTextSize, newTextHeight);
}
} else {
while (newTextHeight < height) {
if (newTextSize <= minTextSize) {
break;
}
newTextSize = Math.max(newTextSize + 1, minTextSize);
newTextHeight = getTextRect(text, textPaint, width, newTextSize);
logDimensions(newTextSize, newTextHeight);
}
}
} finally {
if (text instanceof Editable) {
((Editable) text).delete(text.length() - 1, text.length());
}
}
initialTextSize = newTextSize;
// If we had reached our minimum text size and still don't fit, append an ellipsis
if (addEllipsis && newTextSize == minTextSize && newTextHeight > height) {
// Draw using a static layout
StaticLayout layout = new StaticLayout(text, textPaint, width, Alignment.ALIGN_NORMAL, spacingMult, spacingAdd, false);
layout.draw(textResizeCanvas);
int lastLine = layout.getLineForVertical(height) - 1;
int start = layout.getLineStart(lastLine);
int end = layout.getLineEnd(lastLine);
float lineWidth = layout.getLineWidth(lastLine);
float ellipseWidth = textPaint.measureText(ellipsis);
// Trim characters off until we have enough room to draw the ellipsis
while (width < lineWidth + ellipseWidth) {
lineWidth = textPaint.measureText(text.subSequence(start, --end + 1).toString());
}
setText(text.subSequence(0, end) + ellipsis);
}
// Some devices try to auto adjust line spacing, so force default line spacing
// and invalidate the layout as a side effect
textPaint.setTextSize(newTextSize);
setLineSpacing(spacingAdd, spacingMult);
// Notify the listener if registered
if (textResizeListener != null) {
textResizeListener.onTextResize(this, oldTextSize, newTextSize);
}
// Reset force resize flag
needsResize = false;
}
private void logDimensions(float newTextSize, int newTextHeight) {
Log.d(this.getClass().getName(), "Nex text size: " + newTextSize + ", new text height: " + newTextHeight);
}
// Set the text size of the text paint object and use a static layout to render text off screen before measuring
private int getTextRect(CharSequence source, TextPaint paint, int width, float textSize) {
// Update the text paint object
paint.setTextSize(textSize);
// Draw using a static layout
StaticLayout layout = new StaticLayout(source, paint, width, Alignment.ALIGN_NORMAL, spacingMult, spacingAdd, false);
layout.draw(textResizeCanvas);
return layout.getHeight();
}
}

View File

@ -1,22 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.view;
/**
* User: serso
* Date: 9/13/11
* Time: 12:08 AM
*/
public interface CursorControl {
public void setCursorOnStart();
public void setCursorOnEnd();
public void moveCursorLeft();
public void moveCursorRight();
}

View File

@ -1,41 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view;
import android.content.Context;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 12/18/11
* Time: 1:34 PM
*/
public class EnumMenu<T extends Enum & AMenuItem<D>, D> implements AMenu<T, D> {
@NotNull
private final AMenu<T, D> menu;
@NotNull
public static <T extends Enum & AMenuItem<D>, D> AMenu<T, D> newInstance(@NotNull Class<T> enumClass) {
return new EnumMenu<T, D>(enumClass);
}
private EnumMenu(Class<T> enumClass) {
this.menu = MenuImpl.newInstance(enumClass.getEnumConstants());
}
@Override
public T itemAt(int i) {
return this.menu.itemAt(i);
}
@NotNull
@Override
public CharSequence[] getMenuCaptions(@NotNull final Context context) {
return this.menu.getMenuCaptions(context);
}
}

View File

@ -1,19 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.view;
import android.widget.TextView;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 9/10/11
* Time: 7:21 PM
*/
public interface FontSizeAdjuster {
void adjustFontSize(@NotNull TextView textView);
}

View File

@ -1,24 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.view;
import org.jetbrains.annotations.NotNull;
import org.solovyev.common.utils.history.HistoryAction;
/**
* User: serso
* Date: 9/16/11
* Time: 11:42 PM
*/
public interface HistoryControl<T> {
void doHistoryAction(@NotNull HistoryAction historyAction);
void setCurrentHistoryState(@NotNull T editorHistoryState);
@NotNull
T getCurrentHistoryState();
}

View File

@ -1,58 +0,0 @@
/*
* Copyright (c) 2009-2012. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view;
import android.app.TabActivity;
import android.preference.PreferenceManager;
import android.widget.TabHost;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.view.prefs.StringPreference;
/**
* User: serso
* Date: 1/9/12
* Time: 6:17 PM
*/
public class LastTabSaver implements TabHost.OnTabChangeListener {
private static final String LAST_OPENED_TAB_P_KEY = "last_opened_tab_";
@NotNull
private final StringPreference<String> preference;
@NotNull
private final TabActivity tabActivity;
public LastTabSaver(@NotNull TabActivity tabActivity, @NotNull String defaultTabId) {
this.tabActivity = tabActivity;
this.preference = StringPreference.newInstance(getPreferenceKey(), defaultTabId);
final TabHost tabHost = tabActivity.getTabHost();
tabHost.setCurrentTabByTag(this.getLastOpenedTabId());
tabHost.setOnTabChangedListener(this);
}
public void destroy() {
final TabHost tabHost = tabActivity.getTabHost();
tabHost.setOnTabChangedListener(null);
}
@Override
public void onTabChanged(String tabId) {
preference.putPreference(PreferenceManager.getDefaultSharedPreferences(tabActivity), tabId);
}
@NotNull
public String getLastOpenedTabId() {
return preference.getPreference(PreferenceManager.getDefaultSharedPreferences(tabActivity));
}
@NotNull
private String getPreferenceKey() {
return LAST_OPENED_TAB_P_KEY + tabActivity.getClass().getName();
}
}

View File

@ -1,63 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view;
import android.content.Context;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.utils.CollectionsUtils;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 12/18/11
* Time: 1:31 PM
*/
public class MenuImpl<T extends AMenuItem<D>, D> implements AMenu<T, D> {
private final List<T> menuItems = new ArrayList<T>();
@NotNull
public static <T extends AMenuItem<D>, D> AMenu<T, D> newInstance(T... menuItems) {
return new MenuImpl<T, D>(menuItems);
}
@NotNull
public static <T extends AMenuItem<D>, D> AMenu<T, D> newInstance(@NotNull List<T> menuItems) {
return new MenuImpl<T, D>(menuItems);
}
private MenuImpl(T... menuItems) {
this(CollectionsUtils.asList(menuItems));
}
private MenuImpl(@NotNull List<T> menuItems) {
this.menuItems.addAll(menuItems);
}
@Override
@Nullable
public T itemAt(int i) {
if (i >= 0 && i < menuItems.size()) {
return menuItems.get(i);
} else {
return null;
}
}
@Override
@NotNull
public CharSequence[] getMenuCaptions(@NotNull final Context context) {
final CharSequence[] result = new CharSequence[this.menuItems.size()];
for (int i = 0; i < this.menuItems.size(); i++) {
result[i] = this.menuItems.get(i).getCaption(context);
}
return result;
}
}

View File

@ -1,180 +0,0 @@
package org.solovyev.android.view.prefs;
import android.content.Context;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.utils.Mapper;
/**
* User: serso
* Date: 9/19/11
* Time: 3:17 PM
*/
public abstract class AbstractDialogPreference<T> extends DialogPreference {
@NotNull
protected final static String localNameSpace = "http://schemas.android.com/apk/res/org.solovyev.android.calculator";
@NotNull
protected final static String androidns = "http://schemas.android.com/apk/res/android";
@Nullable
protected TextView valueTextView;
@Nullable
protected String valueText;
@NotNull
protected final Context context;
@Nullable
protected String description;
@Nullable
protected T value;
@Nullable
private T defaultValue;
@Nullable
private final String defaultStringValue;
private final boolean needValueText;
public AbstractDialogPreference(Context context, AttributeSet attrs, @Nullable String defaultStringValue, boolean needValueText) {
super(context, attrs);
this.context = context;
this.defaultStringValue = defaultStringValue;
this.needValueText = needValueText;
final String defaultValueFromAttrs = attrs.getAttributeValue(androidns, "defaultValue");
if ( defaultValueFromAttrs != null ) {
defaultValue = getMapper().parseValue(defaultValueFromAttrs);
} else if (defaultStringValue != null) {
defaultValue = getMapper().parseValue(defaultStringValue);
} else {
throw new IllegalArgumentException();
}
description = attrs.getAttributeValue(androidns, "dialogMessage");
valueText = attrs.getAttributeValue(androidns, "text");
}
@Override
@NotNull
protected final LinearLayout onCreateDialogView() {
if (shouldPersist()) {
value = getPersistedValue();
}
final LinearLayout result = new LinearLayout(context);
result.setOrientation(LinearLayout.VERTICAL);
result.setPadding(6, 6, 6, 6);
if (description != null) {
final TextView splashText = new TextView(context);
splashText.setText(description);
result.addView(splashText);
}
if (needValueText) {
valueTextView = new TextView(context);
valueTextView.setGravity(Gravity.CENTER_HORIZONTAL);
valueTextView.setTextSize(32);
final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
result.addView(valueTextView, params);
}
final View v = createPreferenceView();
initPreferenceView(v);
final LinearLayout.LayoutParams params = getParams();
if (params != null) {
result.addView(v, params);
} else {
result.addView(v);
}
return result;
}
@Nullable
protected abstract LinearLayout.LayoutParams getParams();
@Override
protected void onSetInitialValue(boolean restore, Object defaultValue) {
super.onSetInitialValue(restore, defaultValue);
if (restore) {
if (shouldPersist()) {
value = getPersistedValue();
} else {
value = this.defaultValue;
}
} else {
value = (T) defaultValue;
if (shouldPersist()) {
persist(this.value);
}
}
}
@Override
protected void onBindDialogView(View v) {
super.onBindDialogView(v);
initPreferenceView(null);
}
@NotNull
protected abstract View createPreferenceView();
protected abstract void initPreferenceView(@Nullable View v);
@Nullable
private T getPersistedValue() {
String persistedString = getPersistedString(defaultStringValue);
if ( persistedString == defaultStringValue ) {
return defaultValue;
} else {
return getMapper().parseValue(persistedString);
}
}
protected void persistValue(@Nullable T value) {
Log.d(AbstractDialogPreference.class.getName(), "Trying to persist value: " + value);
this.value = value;
Log.d(AbstractDialogPreference.class.getName(), "android.preference.Preference.callChangeListener()");
if (callChangeListener(value)) {
Log.d(AbstractDialogPreference.class.getName(), "android.preference.Preference.shouldPersist()");
if (shouldPersist()) {
Log.d(AbstractDialogPreference.class.getName(), "org.solovyev.android.view.prefs.AbstractDialogPreference.persist()");
persist(value);
}
}
}
private void persist(@Nullable T value) {
if (value != null) {
final String toBePersistedString = getMapper().formatValue(value);
if (toBePersistedString != null) {
if ( callChangeListener(toBePersistedString) ) {
persistString(toBePersistedString);
}
}
}
}
@NotNull
protected abstract Mapper<T> getMapper();
}

View File

@ -1,66 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.prefs;
import android.content.SharedPreferences;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 12/25/11
* Time: 12:23 PM
*/
public abstract class AbstractPreference<T> implements Preference<T> {
@NotNull
private final String key;
private final T defaultValue;
protected AbstractPreference(@NotNull String key, @Nullable T defaultValue) {
this.key = key;
this.defaultValue = defaultValue;
}
@NotNull
public String getKey() {
return key;
}
public T getDefaultValue() {
return defaultValue;
}
@Override
public final T getPreference(@NotNull SharedPreferences preferences) {
if ( preferences.contains(this.key) ) {
return getPersistedValue(preferences);
} else {
return this.defaultValue;
}
}
@Nullable
protected abstract T getPersistedValue(@NotNull SharedPreferences preferences);
@Override
public void putDefault(@NotNull SharedPreferences preferences) {
putPreference(preferences, this.defaultValue);
}
@Override
public void putPreference(@NotNull SharedPreferences preferences, @Nullable T value) {
if (value != null) {
final SharedPreferences.Editor editor = preferences.edit();
putPersistedValue(editor, value);
editor.commit();
}
}
protected abstract void putPersistedValue(@NotNull SharedPreferences.Editor editor, @NotNull T value);
}

View File

@ -1,47 +0,0 @@
/*
* Copyright (c) 2009-2012. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.prefs;
import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.google.ads.AdView;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorApplication;
/**
* User: serso
* Date: 1/14/12
* Time: 6:47 PM
*/
public class AdViewPreference extends android.preference.Preference {
@Nullable
private AdView adView;
public AdViewPreference(Context context) {
super(context, null);
}
public AdViewPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected View onCreateView(ViewGroup parent) {
// this will create the linear layout defined in ads_layout.xml
View view = super.onCreateView(parent);
if (view instanceof ViewGroup) {
adView = CalculatorApplication.inflateAd((Activity) getContext(), ((ViewGroup) view), 0);
}
return view;
}
}

View File

@ -1,33 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.prefs;
import android.content.SharedPreferences;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 12/25/11
* Time: 1:06 PM
*/
public class BooleanPreference extends AbstractPreference<Boolean>{
public BooleanPreference(@NotNull String key, @Nullable Boolean defaultValue) {
super(key, defaultValue);
}
@Override
protected Boolean getPersistedValue(@NotNull SharedPreferences preferences) {
return preferences.getBoolean(getKey(), false);
}
@Override
protected void putPersistedValue(@NotNull SharedPreferences.Editor editor, @NotNull Boolean value) {
editor.putBoolean(getKey(), value);
}
}

View File

@ -1,40 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.prefs;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.utils.Mapper;
/**
* User: serso
* Date: 12/25/11
* Time: 1:17 PM
*/
public class EnumMapper<T extends Enum> implements Mapper<T>{
@NotNull
private final Class<T> enumClass;
public EnumMapper(@NotNull Class<T> enumClass) {
this.enumClass = enumClass;
}
public static <T extends Enum> Mapper<T> newInstance(@NotNull Class<T> enumClass) {
return new EnumMapper<T>(enumClass);
}
@Override
public String formatValue(@Nullable T value) throws IllegalArgumentException {
return value == null ? null : value.name();
}
@Override
public T parseValue(@Nullable String value) throws IllegalArgumentException {
return value == null ? null : (T)Enum.valueOf(enumClass, value);
}
}

View File

@ -1,33 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.prefs;
import android.content.SharedPreferences;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 12/25/11
* Time: 1:08 PM
*/
public class FloatPreference extends AbstractPreference<Float> {
protected FloatPreference(@NotNull String key, @Nullable Float defaultValue) {
super(key, defaultValue);
}
@Override
protected Float getPersistedValue(@NotNull SharedPreferences preferences) {
return preferences.getFloat(getKey(), -1f);
}
@Override
protected void putPersistedValue(@NotNull SharedPreferences.Editor editor, @NotNull Float value) {
editor.putFloat(getKey(), value);
}
}

View File

@ -1,32 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.prefs;
import android.content.Context;
import android.util.AttributeSet;
import org.jetbrains.annotations.NotNull;
import org.solovyev.common.NumberIntervalMapper;
import org.solovyev.common.utils.Interval;
import org.solovyev.common.utils.Mapper;
/**
* User: serso
* Date: 9/21/11
* Time: 11:41 PM
*/
public class FloatRangeSeekBarPreference extends RangeSeekBarPreference<Float> {
public FloatRangeSeekBarPreference(@NotNull Context context, AttributeSet attrs) {
super(context, attrs);
}
@NotNull
@Override
protected Mapper<Interval<Float>> getMapper() {
return new NumberIntervalMapper<Float>(Float.class);
}
}

View File

@ -1,34 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.prefs;
import android.content.SharedPreferences;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 12/25/11
* Time: 12:47 PM
*/
public class IntegerPreference extends AbstractPreference<Integer> {
public IntegerPreference(@NotNull String key, @Nullable Integer defaultValue) {
super(key, defaultValue);
}
@Override
protected Integer getPersistedValue(@NotNull SharedPreferences preferences) {
return preferences.getInt(getKey(), -1);
}
@Override
protected void putPersistedValue(@NotNull SharedPreferences.Editor editor, @NotNull Integer value) {
editor.putInt(getKey(), value);
}
}

View File

@ -1,32 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.prefs;
import android.content.Context;
import android.util.AttributeSet;
import org.jetbrains.annotations.NotNull;
import org.solovyev.common.NumberIntervalMapper;
import org.solovyev.common.utils.*;
/**
* User: serso
* Date: 9/19/11
* Time: 10:04 PM
*/
public class IntegerRangeSeekBarPreference extends RangeSeekBarPreference<Integer> {
public IntegerRangeSeekBarPreference(@NotNull Context context, AttributeSet attrs) {
super(context, attrs);
}
@NotNull
@Override
protected Mapper<Interval<Integer>> getMapper() {
return new NumberIntervalMapper<Integer>(Integer.class);
}
}

View File

@ -1,33 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.prefs;
import android.content.SharedPreferences;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 12/25/11
* Time: 1:07 PM
*/
public class LongPreference extends AbstractPreference<Long> {
protected LongPreference(@NotNull String key, @Nullable Long defaultValue) {
super(key, defaultValue);
}
@Override
protected Long getPersistedValue(@NotNull SharedPreferences preferences) {
return preferences.getLong(getKey(), -1);
}
@Override
protected void putPersistedValue(@NotNull SharedPreferences.Editor editor, @NotNull Long value) {
editor.putLong(getKey(), value);
}
}

View File

@ -1,87 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.prefs;
import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.view.widgets.NumberPicker;
import org.solovyev.common.NumberIntervalMapper;
import org.solovyev.common.utils.Interval;
import org.solovyev.common.utils.Mapper;
/**
* User: serso
* Date: 9/26/11
* Time: 10:31 PM
*/
public class NumberPickerDialogPreference extends AbstractDialogPreference<Integer> implements NumberPicker.OnChangedListener {
private static final NumberIntervalMapper<Integer> INTEGER_NUMBER_INTERVAL_MAPPER = new NumberIntervalMapper<Integer>(Integer.class);
@NotNull
private NumberPicker numberPicker;
@NotNull
private final Interval<Integer> boundaries;
public NumberPickerDialogPreference(Context context, AttributeSet attrs) {
super(context, attrs, null, false);
//noinspection ConstantConditions
boundaries = INTEGER_NUMBER_INTERVAL_MAPPER.parseValue(attrs.getAttributeValue(localNameSpace, "boundaries"));
createPreferenceView();
}
@Override
protected LinearLayout.LayoutParams getParams() {
final LinearLayout.LayoutParams result = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
result.gravity = Gravity.CENTER;
return result;
}
@NotNull
@Override
protected View createPreferenceView() {
this.numberPicker = new NumberPicker(context);
this.numberPicker.setOnChangeListener(this);
initPreferenceView(this.numberPicker);
return numberPicker;
}
@Override
protected void initPreferenceView(@Nullable View v) {
if ( v == null ) {
v = numberPicker;
}
if (value != null) {
((NumberPicker) v).setRange(boundaries.getLeftBorder(), boundaries.getRightBorder());
((NumberPicker) v).setCurrent(value);
}
}
@NotNull
@Override
protected Mapper<Integer> getMapper() {
return INTEGER_NUMBER_INTERVAL_MAPPER.getMapper();
}
@Override
public void onChanged(NumberPicker picker, int oldVal, int newVal) {
persistValue(newVal);
}
}

View File

@ -1,32 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.prefs;
import android.content.SharedPreferences;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 12/25/11
* Time: 12:21 PM
*/
public interface Preference<T> {
@NotNull
String getKey();
T getDefaultValue();
T getPreference(@NotNull SharedPreferences preferences);
void putPreference(@NotNull SharedPreferences preferences, @Nullable T value);
void putDefault(@NotNull SharedPreferences preferences);
}

View File

@ -1,89 +0,0 @@
package org.solovyev.android.view.prefs;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.view.widgets.AbstractRangeSeekBar;
import org.solovyev.android.view.widgets.NumberRangeSeekBar;
import org.solovyev.common.utils.Interval;
import org.solovyev.common.utils.NumberInterval;
/**
* User: serso
* Date: 9/19/11
* Time: 12:27 PM
*/
public abstract class RangeSeekBarPreference<T extends Number> extends AbstractDialogPreference<Interval<T>> implements AbstractRangeSeekBar.OnRangeSeekBarChangeListener<T> {
@NotNull
private AbstractRangeSeekBar<T> rangeSeekBar;
@NotNull
private final Interval<T> boundaries;
private Integer steps;
public RangeSeekBarPreference(@NotNull Context context, AttributeSet attrs) {
super(context, attrs, null, true);
//noinspection ConstantConditions
boundaries = getMapper().parseValue(attrs.getAttributeValue(localNameSpace, "boundaries"));
steps = attrs.getAttributeIntValue(localNameSpace, "steps", -1);
if ( steps.equals(-1) ) {
steps = null;
}
assert steps == null || steps >= 2;
createPreferenceView();
}
@NotNull
protected View createPreferenceView() {
this.rangeSeekBar = new NumberRangeSeekBar<T>(boundaries, steps, context);
this.rangeSeekBar.setNotifyWhileDragging(true);
this.rangeSeekBar.setOnRangeSeekBarChangeListener(this);
initPreferenceView(this.rangeSeekBar);
return this.rangeSeekBar;
}
@Override
protected LinearLayout.LayoutParams getParams() {
return null;
}
@Override
protected void initPreferenceView(@Nullable View v) {
if ( v == null ) {
v = rangeSeekBar;
}
if (value != null) {
((AbstractRangeSeekBar<T>) v).setSelectedMinValue(value.getLeftBorder());
((AbstractRangeSeekBar<T>) v).setSelectedMaxValue(value.getRightBorder());
setValueText(value);
}
}
@Override
public void rangeSeekBarValuesChanged(T minValue, T maxValue, boolean changeComplete) {
final Interval<T> interval = new NumberInterval<T>(minValue, maxValue);
if (changeComplete) {
persistValue(interval);
}
setValueText(interval);
}
private void setValueText(@NotNull Interval<T> interval) {
final String t = String.valueOf(interval);
valueTextView.setText(valueText == null ? t : t.concat(valueText));
}
}

View File

@ -1,106 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.prefs;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.NumberMapper;
import org.solovyev.common.utils.Mapper;
/* The following code was written by Matthew Wiggins
* and is released under the APACHE 2.0 license
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
public class SeekBarPreference extends AbstractDialogPreference<Integer> implements SeekBar.OnSeekBarChangeListener {
@NotNull
private SeekBar seekBar;
private int max = 0;
public SeekBarPreference(Context context, AttributeSet attrs) {
super(context, attrs, "50", true);
max = attrs.getAttributeIntValue(androidns, "max", 100);
}
@Override
protected LinearLayout.LayoutParams getParams() {
return new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
}
@NotNull
@Override
protected View createPreferenceView() {
seekBar = new SeekBar(context);
seekBar.setOnSeekBarChangeListener(this);
return seekBar;
}
@Override
protected void initPreferenceView(@Nullable View v ) {
if ( v == null) {
v = seekBar;
}
((SeekBar) v).setMax(max);
if (value != null) {
((SeekBar) v).setProgress(value);
setValueText(value);
}
}
@NotNull
@Override
protected Mapper<Integer> getMapper() {
return new NumberMapper<Integer>(Integer.class);
}
public void onProgressChanged(SeekBar seek, int value, boolean fromTouch) {
setValueText(value);
persistValue(value);
}
private void setValueText(int value) {
String t = String.valueOf(value);
valueTextView.setText(valueText == null ? t : t.concat(valueText));
}
public void onStartTrackingTouch(SeekBar seek) {
}
public void onStopTrackingTouch(SeekBar seek) {
}
public void setMax(int max) {
this.max = max;
}
public int getMax() {
return max;
}
public void setProgress(int progress) {
value = progress;
seekBar.setProgress(progress);
}
public int getProgress() {
return value;
}
}

View File

@ -1,54 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.prefs;
import android.content.SharedPreferences;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.utils.Mapper;
import org.solovyev.common.utils.StringMapper;
/**
* User: serso
* Date: 12/25/11
* Time: 12:37 PM
*/
public class StringPreference<T> extends AbstractPreference<T> {
@NotNull
private final Mapper<T> mapper;
public StringPreference(@NotNull String id, @Nullable T defaultValue, @NotNull Mapper<T> mapper) {
super(id, defaultValue);
this.mapper = mapper;
}
@NotNull
public static StringPreference<String> newInstance(@NotNull String id, @Nullable String defaultValue) {
return new StringPreference<String>(id, defaultValue, new StringMapper());
}
@NotNull
public static <T> StringPreference<T> newInstance(@NotNull String id, @Nullable String defaultValue, @NotNull Mapper<T> parser) {
return new StringPreference<T>(id, parser.parseValue(defaultValue), parser);
}
@NotNull
public static <T extends Enum> StringPreference<T> newInstance(@NotNull String id, @Nullable T defaultValue, @NotNull Class<T> enumType) {
return new StringPreference<T>(id, defaultValue, new EnumMapper<T>(enumType));
}
@Override
protected T getPersistedValue(@NotNull SharedPreferences preferences) {
return mapper.parseValue(preferences.getString(getKey(), null));
}
@Override
protected void putPersistedValue(@NotNull SharedPreferences.Editor editor, @NotNull T value) {
editor.putString(getKey(), mapper.formatValue(value));
}
}

View File

@ -1,422 +0,0 @@
package org.solovyev.android.view.widgets;
/**
* User: serso
* Date: 9/19/11
* Time: 3:30 PM
*/
import android.content.Context;
import android.graphics.*;
import android.graphics.Paint.Style;
import android.view.MotionEvent;
import android.widget.ImageView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.R;
import org.solovyev.common.math.LinearNormalizer;
import org.solovyev.common.math.Normalizer;
import org.solovyev.common.utils.Converter;
/**
* Widget that lets users select a minimum and maximum value on a given numerical range.
* The range value types can be one of Long, Double, Integer, Float, Short, Byte or BigDecimal.
*
* @param <T> The Number type of the range values. One of Long, Double, Integer, Float, Short, Byte or BigDecimal.
* @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de)
*/
public abstract class AbstractRangeSeekBar<T> extends ImageView {
@NotNull
private final Paint paint = new Paint();
@NotNull
private final ThumbContainer tc;
@NotNull
private final Converter<T, Double> toDoubleConverter;
@NotNull
private final Converter<Double, T> toTConverter;
@NotNull
private final T minValue, maxValue;
@NotNull
private final Normalizer fromValueNormalizer;
@NotNull
private final Normalizer fromScreenNormalizer;
private double normalizedMinValue = 0d;
private double normalizedMaxValue = 1d;
private Thumb pressedThumb = null;
private boolean notifyWhileDragging = false;
@Nullable
private OnRangeSeekBarChangeListener<T> listener;
/**
* Creates a new RangeSeekBar.
*
* @param minValue The minimum value of the selectable range.
* @param maxValue The maximum value of the selectable range.
* @param steps number of steps to be used in range seek bar
* @param context parent context
* @throws IllegalArgumentException Will be thrown if min/max value types are not one of Long, Double, Integer, Float, Short, Byte or BigDecimal.
*/
public AbstractRangeSeekBar(@NotNull T minValue, @NotNull T maxValue, @Nullable Integer steps, Context context) throws IllegalArgumentException {
super(context);
this.minValue = minValue;
this.maxValue = maxValue;
this.toDoubleConverter = getToDoubleConverter();
this.toTConverter = getToTConverter();
fromValueNormalizer = new LinearNormalizer(toDoubleConverter.convert(minValue), toDoubleConverter.convert(maxValue));
tc = new ThumbContainer();
fromScreenNormalizer = new Normalizer() {
@Override
public double normalize(double value) {
int width = getWidth();
if (width <= 2 * tc.padding) {
// prevent division by zero, simply return 0.
return 0d;
} else {
double result = (value - tc.padding) / (width - 2 * tc.padding);
return Math.min(1d, Math.max(0d, result));
}
}
@Override
public double denormalize(double value) {
return (float) (tc.padding + value * (getWidth() - 2 * tc.padding));
}
};
}
@NotNull
protected abstract Converter<Double,T> getToTConverter();
@NotNull
protected abstract Converter<T,Double> getToDoubleConverter();
public boolean isNotifyWhileDragging() {
return notifyWhileDragging;
}
/**
* Should the widget notify the listener callback while the user is still dragging a thumb? Default is false.
*
* @param flag
*/
public void setNotifyWhileDragging(boolean flag) {
this.notifyWhileDragging = flag;
}
/**
* Returns the absolute minimum value of the range that has been set at construction time.
*
* @return The absolute minimum value of the range.
*/
@NotNull
public T getMinValue() {
return minValue;
}
/**
* Returns the absolute maximum value of the range that has been set at construction time.
*
* @return The absolute maximum value of the range.
*/
@NotNull
public T getMaxValue() {
return maxValue;
}
/**
* Returns the currently selected min value.
*
* @return The currently selected min value.
*/
public T getSelectedMinValue() {
return denormalizeValue(normalizedMinValue);
}
/**
* Sets the currently selected minimum value. The widget will be invalidated and redrawn.
*
* @param value The Number value to set the minimum value to. Will be clamped to given absolute minimum/maximum range.
*/
public void setSelectedMinValue(@NotNull T value) {
setNormalizedMinValue(normalizeValue(value));
}
/**
* Returns the currently selected max value.
*
* @return The currently selected max value.
*/
public T getSelectedMaxValue() {
return denormalizeValue(normalizedMaxValue);
}
/**
* Sets the currently selected maximum value. The widget will be invalidated and redrawn.
*
* @param value The Number value to set the maximum value to. Will be clamped to given absolute minimum/maximum range.
*/
public void setSelectedMaxValue(@NotNull T value) {
setNormalizedMaxValue(normalizeValue(value));
}
/**
* Registers given listener callback to notify about changed selected values.
*
* @param listener The listener to notify about changed selected values.
*/
public void setOnRangeSeekBarChangeListener(OnRangeSeekBarChangeListener<T> listener) {
this.listener = listener;
}
/**
* Handles thumb selection and movement. Notifies listener callback on certain events.
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
pressedThumb = evalPressedThumb(event.getX());
invalidate();
break;
case MotionEvent.ACTION_MOVE:
if (pressedThumb != null) {
double value = convertToNormalizedValue(event.getX());
if (Thumb.MIN.equals(pressedThumb)) {
setNormalizedMinValue(value);
} else if (Thumb.MAX.equals(pressedThumb)) {
setNormalizedMaxValue(value);
}
if (notifyWhileDragging && listener != null) {
listener.rangeSeekBarValuesChanged(getSelectedMinValue(), getSelectedMaxValue(), false);
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
pressedThumb = null;
invalidate();
if (listener != null) {
listener.rangeSeekBarValuesChanged(getSelectedMinValue(), getSelectedMaxValue(), true);
}
break;
}
return true;
}
/**
* Ensures correct size of the widget.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = 200;
if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) {
width = MeasureSpec.getSize(widthMeasureSpec);
}
int height = tc.thumbImage.getHeight();
if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) {
height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
}
setMeasuredDimension(width, height);
}
/**
* Draws the widget on the given canvas.
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw seek bar background line
final RectF rect = tc.getRect();
paint.setStyle(Style.FILL);
paint.setColor(Color.GRAY);
canvas.drawRect(rect, paint);
// draw seek bar active range line
rect.left = convertToScreenValue(normalizedMinValue);
rect.right = convertToScreenValue(normalizedMaxValue);
// orange color
paint.setColor(Color.rgb(255, 165, 0));
canvas.drawRect(rect, paint);
// draw minimum thumb
drawThumb(convertToScreenValue(normalizedMinValue), Thumb.MIN == pressedThumb, canvas);
// draw maximum thumb
drawThumb(convertToScreenValue(normalizedMaxValue), Thumb.MAX == pressedThumb, canvas);
}
/**
* Draws the "normal" resp. "pressed" thumb image on specified x-coordinate.
*
* @param normalizedToScreenValue The x-coordinate in screen space where to draw the image.
* @param pressed Is the thumb currently in "pressed" state?
* @param canvas The canvas to draw upon.
*/
private void drawThumb(float normalizedToScreenValue, boolean pressed, Canvas canvas) {
canvas.drawBitmap(tc.getImage(pressed), normalizedToScreenValue - tc.thumbHalfWidth, (float) ((0.5f * getHeight()) - tc.thumbHalfHeight), paint);
}
/**
* Decides which (if any) thumb is touched by the given x-coordinate.
*
* @param touchX The x-coordinate of a touch event in screen space.
* @return The pressed thumb or null if none has been touched.
*/
private Thumb evalPressedThumb(float touchX) {
Thumb result = null;
boolean minThumbPressed = isInThumbRange(touchX, normalizedMinValue);
boolean maxThumbPressed = isInThumbRange(touchX, normalizedMaxValue);
if (minThumbPressed && maxThumbPressed) {
// if both thumbs are pressed (they lie on top of each other), choose the one with more room to drag. this avoids "stalling" the thumbs in a corner, not being able to drag them apart anymore.
result = (touchX / getWidth() > 0.5f) ? Thumb.MIN : Thumb.MAX;
} else if (minThumbPressed) {
result = Thumb.MIN;
} else if (maxThumbPressed) {
result = Thumb.MAX;
}
return result;
}
/**
* Decides if given x-coordinate in screen space needs to be interpreted as "within" the normalized thumb x-coordinate.
*
* @param touchX The x-coordinate in screen space to check.
* @param normalizedThumbValue The normalized x-coordinate of the thumb to check.
* @return true if x-coordinate is in thumb range, false otherwise.
*/
private boolean isInThumbRange(float touchX, double normalizedThumbValue) {
return Math.abs(touchX - convertToScreenValue(normalizedThumbValue)) <= tc.thumbHalfWidth;
}
/**
* Sets normalized min value to value so that 0 <= value <= normalized max value <= 1.
* The View will get invalidated when calling this method.
*
* @param value The new normalized min value to set.
*/
private void setNormalizedMinValue(double value) {
normalizedMinValue = Math.max(0d, Math.min(1d, Math.min(value, normalizedMaxValue)));
invalidate();
}
/**
* Sets normalized max value to value so that 0 <= normalized min value <= value <= 1.
* The View will get invalidated when calling this method.
*
* @param value The new normalized max value to set.
*/
private void setNormalizedMaxValue(double value) {
normalizedMaxValue = Math.max(0d, Math.min(1d, Math.max(value, normalizedMinValue)));
invalidate();
}
/**
* Converts a normalized value to a Number object in the value space between absolute minimum and maximum.
*
* @param normalized
* @return
*/
@SuppressWarnings("unchecked")
private T denormalizeValue(double normalized) {
return toTConverter.convert(fromValueNormalizer.denormalize(normalized));
}
/**
* Converts the given Number value to a normalized double.
*
* @param value The Number value to normalize.
* @return The normalized double.
*/
private double normalizeValue(T value) {
return fromValueNormalizer.normalize(toDoubleConverter.convert(value));
}
/**
* Converts a normalized value into screen space.
*
* @param normalizedValue The normalized value to convert.
* @return The converted value in screen space.
*/
private float convertToScreenValue(double normalizedValue) {
return (float)this.fromScreenNormalizer.denormalize(normalizedValue);
}
/**
* Converts screen space x-coordinates into normalized values.
*
* @param screenValue The x-coordinate in screen space to convert.
* @return The normalized value.
*/
private double convertToNormalizedValue(float screenValue) {
return this.fromScreenNormalizer.normalize(screenValue);
}
/**
* Callback listener interface to notify about changed range values.
*
* @param <T> The Number type the RangeSeekBar has been declared with.
* @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de)
*/
public interface OnRangeSeekBarChangeListener<T> {
void rangeSeekBarValuesChanged(T minValue, T maxValue, boolean changeComplete);
}
/**
* Thumb constants (min and max).
*
* @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de)
*/
private static enum Thumb {
MIN, MAX
}
private class ThumbContainer {
@NotNull
private final Bitmap thumbImage = BitmapFactory.decodeResource(getResources(), R.drawable.seek_thumb_normal);
@NotNull
private final Bitmap thumbPressedImage = BitmapFactory.decodeResource(getResources(), R.drawable.seek_thumb_pressed);
private final float thumbWidth = thumbImage.getWidth();
private final float thumbHalfWidth = 0.5f * thumbWidth;
private final float thumbHalfHeight = 0.5f * thumbImage.getHeight();
private final float lineHeight = 0.3f * thumbHalfHeight;
private final float padding = thumbHalfWidth;
public RectF getRect() {
return new RectF(padding, 0.5f * (getHeight() - lineHeight), getWidth() - padding, 0.5f * (getHeight() + lineHeight));
}
public Bitmap getImage(boolean pressed) {
return pressed ? thumbPressedImage : thumbImage;
}
}
}

View File

@ -1,254 +0,0 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* 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.
*/
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.view.widgets;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.drawable.Drawable;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.Button;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.R;
import org.solovyev.android.view.FontSizeAdjuster;
import org.solovyev.common.utils.CollectionsUtils;
import org.solovyev.common.utils.Point2d;
import org.solovyev.common.utils.StringUtils;
import java.util.Arrays;
/**
* NOTE: copied from com.android.calculator2.ColorButton
*/
/**
* Button with click-animation effect.
*/
public class ColorButton extends Button {
private int CLICK_FEEDBACK_COLOR;
private static final int CLICK_FEEDBACK_INTERVAL = 10;
private static final int CLICK_FEEDBACK_DURATION = 350;
@NotNull
private Point2d textPosition;
private long animationStart;
private Paint feedbackPaint;
@NotNull
private final OnClickListenerVibrator onClickListener;
private static final float H_TEXT_POSITION_DEFAULT_VALUE = 0.5f;
private float hTextPosition = H_TEXT_POSITION_DEFAULT_VALUE;
public ColorButton(Context context, AttributeSet attrs) {
this(context, attrs, true);
}
public ColorButton(Context context, AttributeSet attrs, boolean init) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, org.solovyev.android.calculator.R.styleable.DragButton);
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
final String attrValue = a.getString(attr);
if (!StringUtils.isEmpty(attrValue)) {
switch (attr) {
case R.styleable.DragButton_hTextPosition:
this.hTextPosition = Float.valueOf(attrValue);
break;
}
}
}
if (init) {
init(context);
}
this.onClickListener = new OnClickListenerVibrator((Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE), PreferenceManager.getDefaultSharedPreferences(context));
}
protected void init(Context context) {
final Resources resources = getResources();
CLICK_FEEDBACK_COLOR = resources.getColor(org.solovyev.android.calculator.R.color.magic_flame);
feedbackPaint = new Paint();
feedbackPaint.setStyle(Style.STROKE);
feedbackPaint.setStrokeWidth(2);
if (CollectionsUtils.contains(getText().toString(), Arrays.asList("+", "-", "/", "×", "*", ""))) {
getPaint().setColor(resources.getColor(R.color.button_operator_text_color));
} else if (getText().toString().equals("C")) {
getPaint().setColor(resources.getColor(R.color.button_ce_text_color));
} else {
getPaint().setColor(resources.getColor(R.color.button_text_color));
}
animationStart = -1;
if (context instanceof FontSizeAdjuster) {
((FontSizeAdjuster) context).adjustFontSize(this);
}
}
@Override
public void onSizeChanged(int w, int h, int oldW, int oldH) {
measureText();
}
protected void measureText() {
Paint paint = getPaint();
if (getText() != null) {
textPosition = getTextPosition(paint, getText());
}
}
private Point2d getTextPosition(@NotNull Paint paint, @NotNull CharSequence text) {
final Point2d result = new Point2d();
result.setX(hTextPosition * getWidth() - 0.5f * paint.measureText(text.toString()));
float height = getHeight() - paint.ascent() - paint.descent();
result.setY(height / 2);
return result;
}
@Override
protected void onTextChanged(CharSequence text, int start, int before, int after) {
measureText();
}
public void drawMagicFlame(int duration, Canvas canvas) {
int alpha = 255 - 255 * duration / CLICK_FEEDBACK_DURATION;
int color = CLICK_FEEDBACK_COLOR | (alpha << 24);
feedbackPaint.setColor(color);
canvas.drawRect(1, 1, getWidth() - 1, getHeight() - 1, feedbackPaint);
}
@Override
public void onDraw(Canvas canvas) {
if (animationStart != -1) {
int animDuration = (int) (System.currentTimeMillis() - animationStart);
if (animDuration >= CLICK_FEEDBACK_DURATION) {
animationStart = -1;
} else {
drawMagicFlame(animDuration, canvas);
postInvalidateDelayed(CLICK_FEEDBACK_INTERVAL);
}
}
CharSequence text = getText();
if (!StringUtils.isEmpty(text) && textPosition != null) {
canvas.drawText(text, 0, text.length(), textPosition.getX(), textPosition.getY(), getPaint());
} else {
drawDrawables(canvas);
}
}
private void drawDrawables(Canvas canvas) {
final int compoundPaddingLeft = getCompoundPaddingLeft();
final int compoundPaddingTop = getCompoundPaddingTop();
final int compoundPaddingRight = getCompoundPaddingRight();
final int compoundPaddingBottom = getCompoundPaddingBottom();
final int scrollX = getScrollX();
final int scrollY = getScrollY();
final int right = getRight();
final int left = getLeft();
final int bottom = getBottom();
final int top = getTop();
final Drawable[] drawables = getCompoundDrawables();
if (drawables != null) {
int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop;
int hspace = right - left - compoundPaddingRight - compoundPaddingLeft;
Drawable topDr = drawables[1];
// IMPORTANT: The coordinates computed are also used in invalidateDrawable()
// Make sure to update invalidateDrawable() when changing this code.
if (topDr != null) {
canvas.save();
canvas.translate(scrollX + compoundPaddingLeft + (hspace - topDr.getBounds().width()) / 2,
scrollY + getPaddingTop() + vspace / 2);
topDr.draw(canvas);
canvas.restore();
}
}
}
public void animateClickFeedback() {
animationStart = System.currentTimeMillis();
invalidate();
}
@Override
public boolean performClick() {
vibrate();
return super.performClick();
}
@Override
public boolean performLongClick() {
vibrate();
return super.performLongClick();
}
private void vibrate() {
this.onClickListener.onClick(this);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean result = super.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
animateClickFeedback();
break;
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_CANCEL:
invalidate();
break;
}
return result;
}
}

View File

@ -1,234 +0,0 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* 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.
*/
package org.solovyev.android.view.widgets;
import android.os.Bundle;
import android.app.Dialog;
import android.content.Context;
import android.graphics.*;
import android.view.MotionEvent;
import android.view.View;
public class ColorPickerDialog extends Dialog {
public interface OnColorChangedListener {
void colorChanged(int color);
}
private OnColorChangedListener mListener;
private int mInitialColor;
private static class ColorPickerView extends View {
private Paint mPaint;
private Paint mCenterPaint;
private final int[] mColors;
private OnColorChangedListener mListener;
ColorPickerView(Context c, OnColorChangedListener l, int color) {
super(c);
mListener = l;
mColors = new int[]{
0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00,
0xFFFFFF00, 0xFFFF0000
};
Shader s = new SweepGradient(0, 0, mColors, null);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setShader(s);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(32);
mCenterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCenterPaint.setColor(color);
mCenterPaint.setStrokeWidth(5);
}
private boolean mTrackingCenter;
private boolean mHighlightCenter;
@Override
protected void onDraw(Canvas canvas) {
float r = CENTER_X - mPaint.getStrokeWidth() * 0.5f;
canvas.translate(CENTER_X, CENTER_X);
canvas.drawOval(new RectF(-r, -r, r, r), mPaint);
canvas.drawCircle(0, 0, CENTER_RADIUS, mCenterPaint);
if (mTrackingCenter) {
int c = mCenterPaint.getColor();
mCenterPaint.setStyle(Paint.Style.STROKE);
if (mHighlightCenter) {
mCenterPaint.setAlpha(0xFF);
} else {
mCenterPaint.setAlpha(0x80);
}
canvas.drawCircle(0, 0,
CENTER_RADIUS + mCenterPaint.getStrokeWidth(),
mCenterPaint);
mCenterPaint.setStyle(Paint.Style.FILL);
mCenterPaint.setColor(c);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(CENTER_X * 2, CENTER_Y * 2);
}
private static final int CENTER_X = 100;
private static final int CENTER_Y = 100;
private static final int CENTER_RADIUS = 32;
private int floatToByte(float x) {
int n = java.lang.Math.round(x);
return n;
}
private int pinToByte(int n) {
if (n < 0) {
n = 0;
} else if (n > 255) {
n = 255;
}
return n;
}
private int ave(int s, int d, float p) {
return s + java.lang.Math.round(p * (d - s));
}
private int interpColor(int colors[], float unit) {
if (unit <= 0) {
return colors[0];
}
if (unit >= 1) {
return colors[colors.length - 1];
}
float p = unit * (colors.length - 1);
int i = (int) p;
p -= i;
// now p is just the fractional part [0...1) and i is the index
int c0 = colors[i];
int c1 = colors[i + 1];
int a = ave(Color.alpha(c0), Color.alpha(c1), p);
int r = ave(Color.red(c0), Color.red(c1), p);
int g = ave(Color.green(c0), Color.green(c1), p);
int b = ave(Color.blue(c0), Color.blue(c1), p);
return Color.argb(a, r, g, b);
}
private int rotateColor(int color, float rad) {
float deg = rad * 180 / 3.1415927f;
int r = Color.red(color);
int g = Color.green(color);
int b = Color.blue(color);
ColorMatrix cm = new ColorMatrix();
ColorMatrix tmp = new ColorMatrix();
cm.setRGB2YUV();
tmp.setRotate(0, deg);
cm.postConcat(tmp);
tmp.setYUV2RGB();
cm.postConcat(tmp);
final float[] a = cm.getArray();
int ir = floatToByte(a[0] * r + a[1] * g + a[2] * b);
int ig = floatToByte(a[5] * r + a[6] * g + a[7] * b);
int ib = floatToByte(a[10] * r + a[11] * g + a[12] * b);
return Color.argb(Color.alpha(color), pinToByte(ir),
pinToByte(ig), pinToByte(ib));
}
private static final float PI = 3.1415926f;
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX() - CENTER_X;
float y = event.getY() - CENTER_Y;
boolean inCenter = java.lang.Math.sqrt(x * x + y * y) <= CENTER_RADIUS;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mTrackingCenter = inCenter;
if (inCenter) {
mHighlightCenter = true;
invalidate();
break;
}
case MotionEvent.ACTION_MOVE:
if (mTrackingCenter) {
if (mHighlightCenter != inCenter) {
mHighlightCenter = inCenter;
invalidate();
}
} else {
float angle = (float) java.lang.Math.atan2(y, x);
// need to turn angle [-PI ... PI] into unit [0....1]
float unit = angle / (2 * PI);
if (unit < 0) {
unit += 1;
}
mCenterPaint.setColor(interpColor(mColors, unit));
invalidate();
}
break;
case MotionEvent.ACTION_UP:
if (mTrackingCenter) {
if (inCenter) {
mListener.colorChanged(mCenterPaint.getColor());
}
mTrackingCenter = false; // so we draw w/o halo
invalidate();
}
break;
}
return true;
}
}
public ColorPickerDialog(Context context,
OnColorChangedListener listener,
int initialColor) {
super(context);
mListener = listener;
mInitialColor = initialColor;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
OnColorChangedListener l = new OnColorChangedListener() {
public void colorChanged(int color) {
mListener.colorChanged(color);
dismiss();
}
};
setContentView(new ColorPickerView(getContext(), l, mInitialColor));
setTitle("Pick a Color");
}
}

View File

@ -1,395 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.view.widgets;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.TextPaint;
import android.util.AttributeSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.R;
import org.solovyev.common.NumberParser;
import org.solovyev.common.utils.CollectionsUtils;
import org.solovyev.common.utils.Point2d;
import org.solovyev.common.utils.StringUtils;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* User: serso
* Date: 7/17/11
* Time: 10:25 PM
*/
public class DirectionDragButton extends DragButton {
@NotNull
private final static Float DEFAULT_DIRECTION_TEXT_SCALE_FLOAT = 0.33f;
@NotNull
private final static Integer DEFAULT_DIRECTION_TEXT_ALPHA = 140;
@NotNull
private final static String DEFAULT_DIRECTION_TEXT_SCALE = "0.33;0.33;0.33;0.33";
protected static class DirectionTextData {
@NotNull
private final GuiDragDirection guiDragDirection;
@NotNull
private String text;
@NotNull
private Point2d position;
@NotNull
private TextPaint paint;
@NotNull
private Float textScale;
private DirectionTextData(@NotNull GuiDragDirection guiDragDirection, @NotNull String text) {
this.guiDragDirection = guiDragDirection;
this.text = text;
}
@NotNull
public GuiDragDirection getGuiDragDirection() {
return guiDragDirection;
}
@NotNull
public String getText() {
return text;
}
public void setText(@NotNull String text) {
this.text = text;
}
@NotNull
public Point2d getPosition() {
return position;
}
public void setPosition(@NotNull Point2d position) {
this.position = position;
}
@NotNull
public TextPaint getPaint() {
return paint;
}
public void setPaint(@NotNull TextPaint paint) {
this.paint = paint;
}
@NotNull
public Float getTextScale() {
return textScale;
}
public void setTextScale(@NotNull Float textScale) {
this.textScale = textScale;
}
}
protected static enum GuiDragDirection {
up(DragDirection.up, 0) {
@Override
public int getAttributeId() {
return R.styleable.DirectionDragButton_textUp;
}
@NotNull
@Override
public Point2d getTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, @NotNull CharSequence text, CharSequence baseText, int w, int h) {
return getUpDownTextPosition(paint, basePaint, text, baseText, 1, w, h);
}
},
down(DragDirection.down, 2) {
@Override
public int getAttributeId() {
return R.styleable.DirectionDragButton_textDown;
}
@NotNull
@Override
public Point2d getTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, @NotNull CharSequence text, CharSequence baseText, int w, int h) {
return getUpDownTextPosition(paint, basePaint, text, baseText, -1, w, h);
}
},
left(DragDirection.left, 3) {
@Override
public int getAttributeId() {
return R.styleable.DirectionDragButton_textLeft;
}
@NotNull
@Override
public Point2d getTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, @NotNull CharSequence text, CharSequence baseText, int w, int h) {
return getLeftRightTextPosition(paint, basePaint, text, baseText, w, h, true);
}
},
right(DragDirection.right, 1) {
@Override
public int getAttributeId() {
return R.styleable.DirectionDragButton_textRight;
}
@NotNull
@Override
public Point2d getTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, @NotNull CharSequence text, CharSequence baseText, int w, int h) {
return getLeftRightTextPosition(paint, basePaint, text, baseText, w, h, false);
}
};
@NotNull
private final DragDirection dragDirection;
private final int attributePosition;
GuiDragDirection(@NotNull DragDirection dragDirection, int attributePosition) {
this.dragDirection = dragDirection;
this.attributePosition = attributePosition;
}
public abstract int getAttributeId();
public int getAttributePosition() {
return attributePosition;
}
@NotNull
public abstract Point2d getTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, @NotNull CharSequence text, CharSequence baseText, int w, int h);
@NotNull
private static Point2d getLeftRightTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, CharSequence text, @NotNull CharSequence baseText, int w, int h, boolean left) {
final Point2d result = new Point2d();
if (left) {
float width = paint.measureText(" ");
result.setX(width);
} else {
float width = paint.measureText(text.toString() + " ");
result.setX(w - width);
}
float selfHeight = paint.ascent() + paint.descent();
basePaint.measureText(StringUtils.getNotEmpty(baseText, "|"));
result.setY(h / 2 - selfHeight / 2);
return result;
}
@NotNull
private static Point2d getUpDownTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, @NotNull CharSequence text, CharSequence baseText, float direction, int w, int h) {
final Point2d result = new Point2d();
float width = paint.measureText(text.toString() + " ");
result.setX(w - width);
float selfHeight = paint.ascent() + paint.descent();
basePaint.measureText(StringUtils.getNotEmpty(baseText, "|"));
if (direction < 0) {
result.setY(h / 2 + h / 3 - selfHeight / 2);
} else {
result.setY(h / 2 - h / 3 - selfHeight / 2);
}
return result;
}
@Nullable
public static GuiDragDirection valueOf(@NotNull DragDirection dragDirection) {
for (GuiDragDirection guiDragDirection : values()) {
if (guiDragDirection.dragDirection == dragDirection) {
return guiDragDirection;
}
}
return null;
}
}
@NotNull
private final Map<GuiDragDirection, DirectionTextData> directionTextDataMap = new EnumMap<GuiDragDirection, DirectionTextData>(GuiDragDirection.class);
@NotNull
private String directionTextScale = DEFAULT_DIRECTION_TEXT_SCALE;
@NotNull
private Integer directionTextAlpha = DEFAULT_DIRECTION_TEXT_ALPHA;
private boolean initialized = false;
public DirectionDragButton(Context context, @NotNull AttributeSet attrs) {
super(context, attrs, false);
init(context, attrs);
}
private void init(@NotNull Context context, @NotNull AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DirectionDragButton);
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
final String attrValue = a.getString(attr);
if (!StringUtils.isEmpty(attrValue)) {
switch (attr) {
case R.styleable.DirectionDragButton_directionTextScale:
this.directionTextScale = attrValue;
break;
case R.styleable.DirectionDragButton_directionTextAlpha:
this.directionTextAlpha = Integer.valueOf(attrValue);
break;
default:
// try drag direction text
for (GuiDragDirection guiDragDirection : GuiDragDirection.values()) {
if (guiDragDirection.getAttributeId() == attr) {
this.directionTextDataMap.put(guiDragDirection, new DirectionTextData(guiDragDirection, attrValue));
break;
}
}
break;
}
}
}
for (Map.Entry<GuiDragDirection, Float> entry : getDirectionTextScales().entrySet()) {
final DirectionTextData dtd = directionTextDataMap.get(entry.getKey());
if (dtd != null) {
dtd.setTextScale(entry.getValue());
}
}
super.init(context);
initialized = true;
}
@Override
protected void measureText() {
super.measureText();
if (initialized) {
final Paint basePaint = getPaint();
final Resources resources = getResources();
for (DirectionTextData directionTextData : directionTextDataMap.values()) {
initDirectionTextPaint(basePaint, directionTextData, resources);
final GuiDragDirection guiDragDirection = directionTextData.getGuiDragDirection();
final String directionText = directionTextData.getText();
final Paint directionPaint = directionTextData.getPaint();
directionTextData.setPosition(guiDragDirection.getTextPosition(directionPaint, basePaint, directionText, getText(), getWidth(), getHeight()));
}
}
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
final TextPaint paint = getPaint();
final Resources resources = getResources();
for (DirectionTextData directionTextData : directionTextDataMap.values()) {
initDirectionTextPaint(paint, directionTextData, resources);
final String text = directionTextData.getText();
final Point2d position = directionTextData.getPosition();
canvas.drawText(text, 0, text.length(), position.getX(), position.getY(), directionTextData.getPaint());
}
}
protected void initDirectionTextPaint(@NotNull Paint basePaint,
@NotNull DirectionTextData directionTextData,
@NotNull Resources resources) {
final TextPaint directionTextPaint = new TextPaint(basePaint);
directionTextPaint.setColor(resources.getColor(R.color.button_text_color));
directionTextPaint.setAlpha(getDirectionTextAlpha());
directionTextPaint.setTextSize(basePaint.getTextSize() * directionTextData.getTextScale());
directionTextData.setPaint(directionTextPaint);
}
protected int getDirectionTextAlpha() {
return directionTextAlpha;
}
@Nullable
public String getTextUp() {
return getText(GuiDragDirection.up);
}
@Nullable
public String getTextDown() {
return getText(GuiDragDirection.down);
}
@Nullable
public String getText(@NotNull DragDirection direction) {
final GuiDragDirection guiDragDirection = GuiDragDirection.valueOf(direction);
return guiDragDirection == null ? null : getText(guiDragDirection);
}
@Nullable
private String getText(@NotNull GuiDragDirection direction) {
DirectionTextData directionTextData = this.directionTextDataMap.get(direction);
return directionTextData == null ? null : directionTextData.getText();
}
@NotNull
public String getDirectionTextScale() {
return directionTextScale;
}
@NotNull
private Map<GuiDragDirection, Float> getDirectionTextScales() {
final List<Float> scales = CollectionsUtils.split(getDirectionTextScale(), ";", new NumberParser<Float>(Float.class));
final Map<GuiDragDirection, Float> result = new HashMap<GuiDragDirection, Float>();
for (GuiDragDirection guiDragDirection : GuiDragDirection.values()) {
result.put(guiDragDirection, DEFAULT_DIRECTION_TEXT_SCALE_FLOAT);
}
if (scales.size() == 1) {
final Float scale = scales.get(0);
for (Map.Entry<GuiDragDirection, Float> entry : result.entrySet()) {
entry.setValue(scale);
}
} else {
for (int i = 0; i < scales.size(); i++) {
for (GuiDragDirection guiDragDirection : GuiDragDirection.values()) {
if (guiDragDirection.getAttributePosition() == i) {
result.put(guiDragDirection, scales.get(i));
}
}
}
}
return result;
}
}

View File

@ -1,108 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.view.widgets;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.utils.Point2d;
public class DragButton extends ColorButton {
@Nullable
private Point2d startPoint = null;
@Nullable
private org.solovyev.android.view.widgets.OnDragListener onDragListener;
private final OnTouchListener onTouchListener = new OnTouchListenerImpl();
public DragButton(Context context, @NotNull AttributeSet attrs) {
this(context, attrs, true);
}
public DragButton(Context context, @NotNull AttributeSet attrs, boolean init) {
super(context, attrs, false);
setOnTouchListener(this.onTouchListener);
if ( init ) {
super.init(context);
}
}
public void setOnDragListener(@Nullable org.solovyev.android.view.widgets.OnDragListener onDragListener) {
this.onDragListener = onDragListener;
}
@Nullable
public org.solovyev.android.view.widgets.OnDragListener getOnDragListener() {
return onDragListener;
}
/**
* OnTouchListener implementation that fires onDrag()
*
* @author serso
*
*/
private final class OnTouchListenerImpl implements OnTouchListener {
@Override
public boolean onTouch(@NotNull View v, @NotNull MotionEvent event) {
// processing on touch event
// in order to avoid possible NPEs
final Point2d localStartPoint = startPoint;
final org.solovyev.android.view.widgets.OnDragListener localOnDragListener = onDragListener;
if (localOnDragListener != null) {
// only if onDrag() listener specified
Log.d(String.valueOf(getId()), "onTouch() for: " + getId() + " . Motion event: " + event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// start tracking: set start point
startPoint = new Point2d(event.getX(), event.getY());
break;
case MotionEvent.ACTION_UP:
// stop tracking
if (localStartPoint != null && localOnDragListener.onDrag(DragButton.this, new DragEvent(localStartPoint, event))) {
if (localOnDragListener.isSuppressOnClickEvent()) {
// prevent on click action
setPressed(false);
// sometimes setPressed(false); doesn't work so to prevent onClick action button disables
if (v instanceof Button) {
final Button button = (Button) v;
button.setEnabled(false);
new Handler().postDelayed(new Runnable() {
public void run() {
button.setEnabled(true);
}
}, 200);
}
}
}
startPoint = null;
break;
}
}
return false;
}
}
}

View File

@ -1,14 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.view.widgets;
public enum DragDirection {
up,
down,
left,
right;
}

View File

@ -1,42 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.view.widgets;
import android.view.MotionEvent;
import org.jetbrains.annotations.NotNull;
import org.solovyev.common.utils.Point2d;
public class DragEvent {
@NotNull
private final Point2d startPoint;
@NotNull
private final MotionEvent motionEvent;
public DragEvent(@NotNull Point2d startPoint, @NotNull MotionEvent motionEvent) {
this.startPoint = startPoint;
this.motionEvent = motionEvent;
}
/**
* @return motion event started at start point
*/
@NotNull
public MotionEvent getMotionEvent() {
return motionEvent;
}
/**
* @return start point of dragging
*/
@NotNull
public Point2d getStartPoint() {
return startPoint;
}
}

View File

@ -1,20 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.view.widgets;
import org.jetbrains.annotations.NotNull;
import java.util.EventListener;
/**
* User: serso
* Date: 9/18/11
* Time: 8:48 PM
*/
public interface DragPreferencesChangeListener extends EventListener{
void onDragPreferencesChange(@NotNull SimpleOnDragListener.Preferences preferences );
}

View File

@ -1,52 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.view.widgets;
import android.util.Log;
import android.view.MotionEvent;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.view.HistoryControl;
import org.solovyev.common.utils.Point2d;
import org.solovyev.common.utils.StringUtils;
import org.solovyev.common.utils.history.HistoryAction;
/**
* User: serso
* Date: 9/16/11
* Time: 11:36 PM
*/
public class HistoryDragProcessor<T> implements SimpleOnDragListener.DragProcessor {
@NotNull
private final HistoryControl<T> historyControl;
public HistoryDragProcessor(@NotNull HistoryControl<T> historyControl) {
this.historyControl = historyControl;
}
@Override
public boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent) {
boolean result = false;
Log.d(String.valueOf(dragButton.getId()), "History on drag event start: " + dragDirection);
final HistoryAction historyAction;
if ( dragDirection == DragDirection.up ) {
historyAction = HistoryAction.undo;
} else if ( dragDirection == DragDirection.down ) {
historyAction = HistoryAction.redo;
} else {
historyAction = null;
}
if (historyAction != null) {
result = true;
historyControl.doHistoryAction(historyAction);
}
return result;
}
}

View File

@ -1,538 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.widgets;
/**
* User: serso
* Date: 9/18/11
* Time: 10:03 PM
*/
/*
* Copyright (C) 2008 The Android Open Source Project
*
* 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.
*/
import android.content.Context;
import android.os.Handler;
import android.text.InputFilter;
import android.text.InputType;
import android.text.Spanned;
import android.text.method.NumberKeyListener;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.solovyev.android.calculator.R;
/**
* A view for selecting a number
*
* For a dialog using this view, see {@link android.app.TimePickerDialog}.
* @hide
*/
public class NumberPicker extends LinearLayout {
/**
* The callback interface used to indicate the number value has been adjusted.
*/
public interface OnChangedListener {
/**
* @param picker The NumberPicker associated with this listener.
* @param oldVal The previous value.
* @param newVal The new value.
*/
void onChanged(NumberPicker picker, int oldVal, int newVal);
}
/**
* Interface used to format the number into a string for presentation
*/
public interface Formatter {
String toString(int value);
}
/*
* Use a custom NumberPicker formatting callback to use two-digit
* minutes strings like "01". Keeping a static formatter etc. is the
* most efficient way to do this; it avoids creating temporary objects
* on every call to format().
*/
public static final NumberPicker.Formatter TWO_DIGIT_FORMATTER =
new NumberPicker.Formatter() {
final StringBuilder mBuilder = new StringBuilder();
final java.util.Formatter mFmt = new java.util.Formatter(
mBuilder, java.util.Locale.US);
final Object[] mArgs = new Object[1];
public String toString(int value) {
mArgs[0] = value;
mBuilder.delete(0, mBuilder.length());
mFmt.format("%02d", mArgs);
return mFmt.toString();
}
};
private final Handler mHandler;
private final Runnable mRunnable = new Runnable() {
public void run() {
if (mIncrement) {
changeCurrent(mCurrent + 1);
mHandler.postDelayed(this, mSpeed);
} else if (mDecrement) {
changeCurrent(mCurrent - 1);
mHandler.postDelayed(this, mSpeed);
}
}
};
private final EditText mText;
private final InputFilter mNumberInputFilter;
private String[] mDisplayedValues;
/**
* Lower value of the range of numbers allowed for the NumberPicker
*/
private int mStart;
/**
* Upper value of the range of numbers allowed for the NumberPicker
*/
private int mEnd;
/**
* Current value of this NumberPicker
*/
private int mCurrent;
/**
* Previous value of this NumberPicker.
*/
private int mPrevious;
private OnChangedListener mListener;
private Formatter mFormatter;
private long mSpeed = 300;
private boolean mIncrement;
private boolean mDecrement;
/**
* Create a new number picker
* @param context the application environment
*/
public NumberPicker(Context context) {
this(context, null);
}
/**
* Create a new number picker
* @param context the application environment
* @param attrs a collection of attributes
*/
public NumberPicker(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(VERTICAL);
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.number_picker, this, true);
mHandler = new Handler();
OnClickListener clickListener = new OnClickListener() {
public void onClick(View v) {
validateInput(mText);
if (!mText.hasFocus()) mText.requestFocus();
// now perform the increment/decrement
if (R.id.increment == v.getId()) {
changeCurrent(mCurrent + 1);
} else if (R.id.decrement == v.getId()) {
changeCurrent(mCurrent - 1);
}
}
};
OnFocusChangeListener focusListener = new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
/* When focus is lost check that the text field
* has valid values.
*/
if (!hasFocus) {
validateInput(v);
}
}
};
OnLongClickListener longClickListener = new OnLongClickListener() {
/**
* We start the long click here but rely on the {@link NumberPickerButton}
* to inform us when the long click has ended.
*/
public boolean onLongClick(View v) {
/* The text view may still have focus so clear it's focus which will
* trigger the on focus changed and any typed values to be pulled.
*/
mText.clearFocus();
if (R.id.increment == v.getId()) {
mIncrement = true;
mHandler.post(mRunnable);
} else if (R.id.decrement == v.getId()) {
mDecrement = true;
mHandler.post(mRunnable);
}
return true;
}
};
InputFilter inputFilter = new NumberPickerInputFilter();
mNumberInputFilter = new NumberRangeKeyListener();
mIncrementButton = (NumberPickerButton) findViewById(R.id.increment);
mIncrementButton.setOnClickListener(clickListener);
mIncrementButton.setOnLongClickListener(longClickListener);
mIncrementButton.setNumberPicker(this);
mDecrementButton = (NumberPickerButton) findViewById(R.id.decrement);
mDecrementButton.setOnClickListener(clickListener);
mDecrementButton.setOnLongClickListener(longClickListener);
mDecrementButton.setNumberPicker(this);
mText = (EditText) findViewById(R.id.timepicker_input);
mText.setOnFocusChangeListener(focusListener);
mText.setFilters(new InputFilter[] {inputFilter});
mText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
if (!isEnabled()) {
setEnabled(false);
}
}
/**
* Set the enabled state of this view. The interpretation of the enabled
* state varies by subclass.
*
* @param enabled True if this view is enabled, false otherwise.
*/
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
mIncrementButton.setEnabled(enabled);
mDecrementButton.setEnabled(enabled);
mText.setEnabled(enabled);
}
/**
* Set the callback that indicates the number has been adjusted by the user.
* @param listener the callback, should not be null.
*/
public void setOnChangeListener(OnChangedListener listener) {
mListener = listener;
}
/**
* Set the formatter that will be used to format the number for presentation
* @param formatter the formatter object. If formatter is null, String.valueOf()
* will be used
*/
public void setFormatter(Formatter formatter) {
mFormatter = formatter;
}
/**
* Set the range of numbers allowed for the number picker. The current
* value will be automatically set to the start.
*
* @param start the start of the range (inclusive)
* @param end the end of the range (inclusive)
*/
public void setRange(int start, int end) {
setRange(start, end, null/*displayedValues*/);
}
/**
* Set the range of numbers allowed for the number picker. The current
* value will be automatically set to the start. Also provide a mapping
* for values used to display to the user.
*
* @param start the start of the range (inclusive)
* @param end the end of the range (inclusive)
* @param displayedValues the values displayed to the user.
*/
public void setRange(int start, int end, String[] displayedValues) {
mDisplayedValues = displayedValues;
mStart = start;
mEnd = end;
mCurrent = start;
updateView();
if (displayedValues != null) {
// Allow text entry rather than strictly numeric entry.
mText.setRawInputType(InputType.TYPE_CLASS_TEXT/* |
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS*/);
}
}
/**
* Set the current value for the number picker.
*
* @param current the current value the start of the range (inclusive)
* @throws IllegalArgumentException when current is not within the range
* of of the number picker
*/
public void setCurrent(int current) {
if (current < mStart || current > mEnd) {
throw new IllegalArgumentException(
"current should be >= start and <= end");
}
mCurrent = current;
updateView();
}
/**
* Sets the speed at which the numbers will scroll when the +/-
* buttons are longpressed
*
* @param speed The speed (in milliseconds) at which the numbers will scroll
* default 300ms
*/
public void setSpeed(long speed) {
mSpeed = speed;
}
private String formatNumber(int value) {
return (mFormatter != null)
? mFormatter.toString(value)
: String.valueOf(value);
}
/**
* Sets the current value of this NumberPicker, and sets mPrevious to the previous
* value. If current is greater than mEnd less than mStart, the value of mCurrent
* is wrapped around.
*
* Subclasses can override this to change the wrapping behavior
*
* @param current the new value of the NumberPicker
*/
protected void changeCurrent(int current) {
// Wrap around the values if we go past the start or end
if (current > mEnd) {
current = mStart;
} else if (current < mStart) {
current = mEnd;
}
mPrevious = mCurrent;
mCurrent = current;
notifyChange();
updateView();
}
/**
* Notifies the listener, if registered, of a change of the value of this
* NumberPicker.
*/
private void notifyChange() {
if (mListener != null) {
mListener.onChanged(this, mPrevious, mCurrent);
}
}
/**
* Updates the view of this NumberPicker. If displayValues were specified
* in {@link #setRange}, the string corresponding to the index specified by
* the current value will be returned. Otherwise, the formatter specified
* in will be used to format the number.
*/
private void updateView() {
/* If we don't have displayed values then use the
* current number else find the correct value in the
* displayed values for the current number.
*/
if (mDisplayedValues == null) {
mText.setText(formatNumber(mCurrent));
} else {
mText.setText(mDisplayedValues[mCurrent - mStart]);
}
mText.setSelection(mText.getText().length());
}
private void validateCurrentView(CharSequence str) {
int val = getSelectedPos(str.toString());
if ((val >= mStart) && (val <= mEnd)) {
if (mCurrent != val) {
mPrevious = mCurrent;
mCurrent = val;
notifyChange();
}
}
updateView();
}
private void validateInput(View v) {
String str = String.valueOf(((TextView) v).getText());
if ("".equals(str)) {
// Restore to the old value as we don't allow empty values
updateView();
} else {
// Check the new value and ensure it's in range
validateCurrentView(str);
}
}
/**
* @hide
*/
public void cancelIncrement() {
mIncrement = false;
}
/**
* @hide
*/
public void cancelDecrement() {
mDecrement = false;
}
private static final char[] DIGIT_CHARACTERS = new char[] {
'-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
};
private NumberPickerButton mIncrementButton;
private NumberPickerButton mDecrementButton;
private class NumberPickerInputFilter implements InputFilter {
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
if (mDisplayedValues == null) {
return mNumberInputFilter.filter(source, start, end, dest, dstart, dend);
}
CharSequence filtered = String.valueOf(source.subSequence(start, end));
String result = String.valueOf(dest.subSequence(0, dstart))
+ filtered
+ dest.subSequence(dend, dest.length());
String str = String.valueOf(result).toLowerCase();
for (String val : mDisplayedValues) {
val = val.toLowerCase();
if (val.startsWith(str)) {
return filtered;
}
}
return "";
}
}
private class NumberRangeKeyListener extends NumberKeyListener {
// XXX This doesn't allow for range limits when controlled by a
// soft input method!
public int getInputType() {
return InputType.TYPE_CLASS_NUMBER;
}
@Override
protected char[] getAcceptedChars() {
return DIGIT_CHARACTERS;
}
@Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
CharSequence filtered = super.filter(source, start, end, dest, dstart, dend);
if (filtered == null) {
filtered = source.subSequence(start, end);
}
String result = String.valueOf(dest.subSequence(0, dstart))
+ filtered
+ dest.subSequence(dend, dest.length());
if ("".equals(result)) {
return result;
}
int val = getSelectedPos(result);
/* Ensure the user can't type in a value greater
* than the max allowed. We have to allow less than min
* as the user might want to delete some numbers
* and then type a new number.
*/
if (val > mEnd) {
return "";
} else {
return filtered;
}
}
}
private int getSelectedPos(String str) {
if (mDisplayedValues == null) {
try {
return Integer.parseInt(str);
} catch (NumberFormatException e) {
/* Ignore as if it's not a number we don't care */
}
} else {
for (int i = 0; i < mDisplayedValues.length; i++) {
/* Don't force the user to type in jan when ja will do */
str = str.toLowerCase();
if (mDisplayedValues[i].toLowerCase().startsWith(str)) {
return mStart + i;
}
}
/* The user might have typed in a number into the month field i.e.
* 10 instead of OCT so support that too.
*/
try {
return Integer.parseInt(str);
} catch (NumberFormatException e) {
/* Ignore as if it's not a number we don't care */
}
}
return mStart;
}
/**
* Returns the current value of the NumberPicker
* @return the current value.
*/
public int getCurrent() {
return mCurrent;
}
/**
* Returns the upper value of the range of the NumberPicker
* @return the uppper number of the range.
*/
protected int getEndRange() {
return mEnd;
}
/**
* Returns the lower value of the range of the NumberPicker
* @return the lower number of the range.
*/
protected int getBeginRange() {
return mStart;
}
}

View File

@ -1,106 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.widgets;
/**
* User: serso
* Date: 9/18/11
* Time: 10:04 PM
*/
/*
* Copyright (C) 2008 The Android Open Source Project
*
* 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.
*/
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.widget.ImageButton;
import org.solovyev.android.calculator.R;
/**
* This class exists purely to cancel long click events, that got
* started in NumberPicker
*/
class NumberPickerButton extends ImageButton {
private NumberPicker mNumberPicker;
public NumberPickerButton(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public NumberPickerButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NumberPickerButton(Context context) {
super(context);
}
public void setNumberPicker(NumberPicker picker) {
mNumberPicker = picker;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
cancelLongpressIfRequired(event);
return super.onTouchEvent(event);
}
@Override
public boolean onTrackballEvent(MotionEvent event) {
cancelLongpressIfRequired(event);
return super.onTrackballEvent(event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_DPAD_CENTER)
|| (keyCode == KeyEvent.KEYCODE_ENTER)) {
cancelLongpress();
}
return super.onKeyUp(keyCode, event);
}
private void cancelLongpressIfRequired(MotionEvent event) {
if ((event.getAction() == MotionEvent.ACTION_CANCEL)
|| (event.getAction() == MotionEvent.ACTION_UP)) {
cancelLongpress();
}
}
private void cancelLongpress() {
if (R.id.increment == getId()) {
mNumberPicker.cancelIncrement();
} else if (R.id.decrement == getId()) {
mNumberPicker.cancelDecrement();
}
}
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (!hasWindowFocus) {
cancelLongpress();
}
}
}

View File

@ -1,60 +0,0 @@
package org.solovyev.android.view.widgets;
import android.content.Context;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.utils.Converter;
import org.solovyev.common.utils.Interval;
import org.solovyev.common.utils.NumberValuer;
/**
* User: serso
* Date: 9/19/11
* Time: 4:26 PM
*/
public class NumberRangeSeekBar<T extends Number> extends AbstractRangeSeekBar<T> {
@NotNull
private final NumberType numberType;
public NumberRangeSeekBar(@NotNull Interval<T> boundaries, @Nullable Integer steps, Context context) throws IllegalArgumentException {
this(boundaries.getLeftBorder(), boundaries.getRightBorder(), steps, context);
}
/**
* Creates a new RangeSeekBar.
*
* @param minValue The minimum value of the selectable range.
* @param maxValue The maximum value of the selectable range.
* @param steps number of steps of range
* @param context parent context
* @throws IllegalArgumentException Will be thrown if min/max value types are not one of Long, Double, Integer, Float, Short, Byte or BigDecimal.
*/
public NumberRangeSeekBar(@NotNull T minValue, @NotNull T maxValue, @Nullable Integer steps, Context context) throws IllegalArgumentException {
super(minValue, maxValue, steps, context);
numberType = NumberType.fromNumber(minValue);
}
@NotNull
@Override
protected Converter<Double, T> getToTConverter() {
return new Converter<Double, T>() {
@NotNull
@Override
public T convert(@NotNull Double value) {
return (T) numberType.toNumber(value);
}
};
}
@NotNull
@Override
protected Converter<T, Double> getToDoubleConverter() {
return new NumberValuer<T>();
}
}

View File

@ -1,68 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.widgets;
import org.jetbrains.annotations.NotNull;
import java.math.BigDecimal;
/**
* Utility enumeration used to convert between Numbers and doubles.
*
* @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de)
*/
enum NumberType {
LONG(Long.class),
DOUBLE(Double.class),
INTEGER(Integer.class),
FLOAT(Float.class),
SHORT(Short.class),
BYTE(Byte.class),
BIG_DECIMAL(BigDecimal.class);
@NotNull
private final Class underlyingClass;
NumberType(@NotNull Class underlyingClass) {
this.underlyingClass = underlyingClass;
}
@NotNull
public static <E extends Number> NumberType fromNumber(E value) throws IllegalArgumentException {
for (NumberType numberType : NumberType.values()) {
if (numberType.underlyingClass.isInstance(value)) {
return numberType;
}
}
throw new IllegalArgumentException("Number class '" + value.getClass().getName() + "' is not supported");
}
public <T extends Number> T toNumber(double value) {
switch (this) {
case LONG:
return (T)new Long((long) value);
case DOUBLE:
return (T)new Double(value);
case INTEGER:
return (T)new Integer((int) value);
case FLOAT:
return (T)new Float((float) value);
case SHORT:
return (T)new Short((short) value);
case BYTE:
return (T)new Byte((byte) value);
case BIG_DECIMAL:
return (T)new BigDecimal(value);
}
throw new InstantiationError("can't convert " + this + " to a Number object");
}
}

View File

@ -1,36 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.widgets;
import android.content.SharedPreferences;
import android.os.Vibrator;
import android.view.View;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 10/26/11
* Time: 11:25 PM
*/
public class OnClickListenerVibrator implements View.OnClickListener {
private static final float VIBRATION_TIME_SCALE = 1.0f;
@NotNull
private VibratorContainer vibrator;
public OnClickListenerVibrator(@Nullable Vibrator vibrator,
@NotNull SharedPreferences preferences) {
this.vibrator = new VibratorContainer(vibrator, preferences, VIBRATION_TIME_SCALE);
}
@Override
public void onClick(View v) {
vibrator.vibrate();
}
}

View File

@ -1,30 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.widgets;
import android.view.View;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 10/26/11
* Time: 10:55 PM
*/
public class OnClickListenerWrapper implements View.OnClickListener{
@NotNull
private final View.OnClickListener onClickListener;
public OnClickListenerWrapper(@NotNull View.OnClickListener onClickListener) {
this.onClickListener = onClickListener;
}
@Override
public void onClick(View v) {
this.onClick(v);
}
}

View File

@ -1,31 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.view.widgets;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.view.widgets.DragButton;
import org.solovyev.android.view.widgets.DragEvent;
import java.util.EventListener;
public interface OnDragListener extends EventListener{
/**
*
* @return 'true': if drag event has taken place (i.e. onDrag() method returned true) then click action will be suppresed
*/
boolean isSuppressOnClickEvent();
/**
* @param dragButton drag button object for which onDrag listener was set
* @param event drag event
*
* @return 'true' if drag event occurred, 'false' otherwise
*/
boolean onDrag(@NotNull DragButton dragButton, @NotNull DragEvent event);
}

View File

@ -1,34 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.widgets;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 10/26/11
* Time: 10:37 PM
*/
public class OnDragListenerWrapper implements OnDragListener {
@NotNull
private final OnDragListener onDragListener;
public OnDragListenerWrapper(@NotNull OnDragListener onDragListener) {
this.onDragListener = onDragListener;
}
@Override
public boolean isSuppressOnClickEvent() {
return this.onDragListener.isSuppressOnClickEvent();
}
@Override
public boolean onDrag(@NotNull DragButton dragButton, @NotNull DragEvent event) {
return this.onDragListener.onDrag(dragButton, event);
}
}

View File

@ -1,305 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.view.widgets;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import android.view.MotionEvent;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.R;
import org.solovyev.common.NumberIntervalMapper;
import org.solovyev.common.utils.*;
import java.util.HashMap;
import java.util.Map;
public class SimpleOnDragListener implements OnDragListener, DragPreferencesChangeListener {
@NotNull
public static final Point2d axis = new Point2d(0, 1);
@NotNull
private DragProcessor dragProcessor;
@NotNull
private Preferences preferences;
public SimpleOnDragListener(@NotNull Preferences preferences) {
this.preferences = preferences;
}
public SimpleOnDragListener(@NotNull DragProcessor dragProcessor, @NotNull Preferences preferences) {
this.dragProcessor = dragProcessor;
this.preferences = preferences;
}
@Override
public boolean onDrag(@NotNull DragButton dragButton, @NotNull DragEvent event) {
boolean result = false;
logDragEvent(dragButton, event);
final Point2d startPoint = event.getStartPoint();
final MotionEvent motionEvent = event.getMotionEvent();
// init end point
final Point2d endPoint = new Point2d(motionEvent.getX(), motionEvent.getY());
final float distance = MathUtils.getDistance(startPoint, endPoint);
final MutableObject<Boolean> right = new MutableObject<Boolean>();
final double angle = Math.toDegrees(MathUtils.getAngle(startPoint, MathUtils.sum(startPoint, axis), endPoint, right));
Log.d(String.valueOf(dragButton.getId()), "Angle: " + angle);
Log.d(String.valueOf(dragButton.getId()), "Is right?: " + right.getObject());
final double duration = motionEvent.getEventTime() - motionEvent.getDownTime();
final Preference distancePreferences = preferences.getPreferencesMap().get(PreferenceType.distance);
final Preference anglePreferences = preferences.getPreferencesMap().get(PreferenceType.angle);
DragDirection direction = null;
for (Map.Entry<DragDirection, DragPreference> directionEntry : distancePreferences.getDirectionPreferences().entrySet()) {
Log.d(String.valueOf(dragButton.getId()), "Drag direction: " + directionEntry.getKey());
Log.d(String.valueOf(dragButton.getId()), "Trying direction interval: " + directionEntry.getValue().getInterval());
if (isInInterval(directionEntry.getValue().getInterval(), distance)) {
final DragPreference anglePreference = anglePreferences.getDirectionPreferences().get(directionEntry.getKey());
Log.d(String.valueOf(dragButton.getId()), "Trying angle interval: " + anglePreference.getInterval());
if (directionEntry.getKey() == DragDirection.left && right.getObject()) {
} else if (directionEntry.getKey() == DragDirection.right && !right.getObject()) {
} else {
if (isInInterval(anglePreference.getInterval(), (float) angle)) {
direction = directionEntry.getKey();
Log.d(String.valueOf(dragButton.getId()), "MATCH! Direction: " + direction);
break;
}
}
}
}
if (direction != null) {
final Preference durationPreferences = preferences.getPreferencesMap().get(PreferenceType.duration);
final DragPreference durationDragPreferences = durationPreferences.getDirectionPreferences().get(direction);
Log.d(String.valueOf(dragButton.getId()), "Trying time interval: " + durationDragPreferences.getInterval());
if (isInInterval(durationDragPreferences.getInterval(), (float) duration)) {
Log.d(String.valueOf(dragButton.getId()), "MATCH!");
result = dragProcessor.processDragEvent(direction, dragButton, startPoint, motionEvent);
}
}
return result;
}
private boolean isInInterval(@NotNull Interval<Float> interval, float value) {
return interval.getLeftBorder() - MathUtils.MIN_AMOUNT <= value && value <= interval.getRightBorder() + MathUtils.MIN_AMOUNT;
}
@Override
public boolean isSuppressOnClickEvent() {
return true;
}
private void logDragEvent(@NotNull DragButton dragButton, @NotNull DragEvent event) {
final Point2d startPoint = event.getStartPoint();
final MotionEvent motionEvent = event.getMotionEvent();
final Point2d endPoint = new Point2d(motionEvent.getX(), motionEvent.getY());
Log.d(String.valueOf(dragButton.getId()), "Start point: " + startPoint + ", End point: " + endPoint);
Log.d(String.valueOf(dragButton.getId()), "Distance: " + MathUtils.getDistance(startPoint, endPoint));
final MutableObject<Boolean> right = new MutableObject<Boolean>();
Log.d(String.valueOf(dragButton.getId()), "Angle: " + Math.toDegrees(MathUtils.getAngle(startPoint, MathUtils.sum(startPoint, axis), endPoint, right)));
Log.d(String.valueOf(dragButton.getId()), "Is right angle? " + right);
Log.d(String.valueOf(dragButton.getId()), "Axis: " + axis + " Vector: " + MathUtils.subtract(endPoint, startPoint));
Log.d(String.valueOf(dragButton.getId()), "Total time: " + (motionEvent.getEventTime() - motionEvent.getDownTime()) + " ms");
}
@NotNull
public DragProcessor getDragProcessor() {
return dragProcessor;
}
public void setDragProcessor(@NotNull DragProcessor dragProcessor) {
this.dragProcessor = dragProcessor;
}
@Override
public void onDragPreferencesChange(@NotNull Preferences preferences) {
this.preferences = preferences;
}
public interface DragProcessor {
boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent);
}
// todo serso: currently we do not use direction
public static String getPreferenceId(@NotNull PreferenceType preferenceType, @NotNull DragDirection direction) {
return "org.solovyev.android.calculator.DragButtonCalibrationActivity" + "_" + preferenceType.name() /*+ "_" + direction.name()*/;
}
@NotNull
public static Preferences getPreferences(@NotNull final SharedPreferences preferences, @NotNull Context context) {
final Mapper<Interval<Float>> mapper = new NumberIntervalMapper<Float>(Float.class);
final Preferences result = new Preferences();
for (PreferenceType preferenceType : PreferenceType.values()) {
for (DragDirection dragDirection : DragDirection.values()) {
final String preferenceId = getPreferenceId(preferenceType, dragDirection);
final String defaultValue;
switch (preferenceType) {
case angle:
defaultValue = context.getResources().getString(R.string.p_drag_angle);
break;
case distance:
defaultValue = context.getResources().getString(R.string.p_drag_distance);
break;
case duration:
defaultValue = context.getResources().getString(R.string.p_drag_duration);
break;
default:
defaultValue = null;
Log.e(SimpleOnDragListener.class.getName(), "New preference type added: default preferences should be defined. Preference id: " + preferenceId);
}
final String value = preferences.getString(preferenceId, defaultValue);
if (defaultValue != null) {
final Interval<Float> intervalPref = mapper.parseValue(value);
assert intervalPref != null;
transformInterval(preferenceType, dragDirection, intervalPref);
Log.d(SimpleOnDragListener.class.getName(), "Preference loaded for " + dragDirection +". Id: " + preferenceId + ", value: " + intervalPref.toString());
final DragPreference directionPreference = new DragPreference(dragDirection, intervalPref);
Preference preference = result.getPreferencesMap().get(preferenceType);
if (preference == null) {
preference = new Preference(preferenceType);
result.getPreferencesMap().put(preferenceType, preference);
}
preference.getDirectionPreferences().put(dragDirection, directionPreference);
}
}
}
return result;
}
@NotNull
public static Interval<Float> transformInterval(@NotNull PreferenceType preferenceType, @NotNull DragDirection dragDirection, @NotNull Interval<Float> interval) {
if (preferenceType == PreferenceType.angle) {
final Float leftBorder = interval.getLeftBorder();
final Float rightBorder = interval.getRightBorder();
if (dragDirection == DragDirection.up) {
interval.setLeftBorder(180f - rightBorder);
interval.setRightBorder(180f - leftBorder);
} else if (dragDirection == DragDirection.left ) {
interval.setLeftBorder(90f - rightBorder);
interval.setRightBorder(90f + rightBorder);
} else if ( dragDirection == DragDirection.right ) {
interval.setLeftBorder(90f - rightBorder);
interval.setRightBorder(90f + rightBorder);
}
}
return interval;
}
public static enum PreferenceType {
angle,
distance,
duration
}
public static class DragPreference {
@NotNull
private DragDirection direction;
@NotNull
private Interval<Float> interval;
public DragPreference(@NotNull DragDirection direction, @NotNull Interval<Float> interval) {
this.direction = direction;
this.interval = interval;
}
@NotNull
public DragDirection getDirection() {
return direction;
}
public void setDirection(@NotNull DragDirection direction) {
this.direction = direction;
}
@NotNull
public Interval<Float> getInterval() {
return interval;
}
public void setInterval(@NotNull Interval<Float> interval) {
this.interval = interval;
}
}
public static class Preference {
@NotNull
private PreferenceType preferenceType;
@NotNull
private Map<DragDirection, DragPreference> directionPreferences = new HashMap<DragDirection, DragPreference>();
public Preference(@NotNull PreferenceType preferenceType) {
this.preferenceType = preferenceType;
}
@NotNull
public PreferenceType getPreferenceType() {
return preferenceType;
}
public void setPreferenceType(@NotNull PreferenceType preferenceType) {
this.preferenceType = preferenceType;
}
@NotNull
public Map<DragDirection, DragPreference> getDirectionPreferences() {
return directionPreferences;
}
public void setDirectionPreferences(@NotNull Map<DragDirection, DragPreference> directionPreferences) {
this.directionPreferences = directionPreferences;
}
}
public static class Preferences {
private final Map<PreferenceType, Preference> preferencesMap = new HashMap<PreferenceType, Preference>();
public Map<PreferenceType, Preference> getPreferencesMap() {
return preferencesMap;
}
}
}

View File

@ -1,65 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.view.widgets;
import android.content.SharedPreferences;
import android.os.Vibrator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.NumberMapper;
/**
* User: serso
* Date: 10/26/11
* Time: 11:40 PM
*/
public class VibratorContainer implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String HAPTIC_FEEDBACK_P_KEY = "org.solovyev.android.calculator.CalculatorModel_haptic_feedback";
public static final boolean HAPTIC_FEEDBACK_DEFAULT = false;
public static final String HAPTIC_FEEDBACK_DURATION_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_haptic_feedback_duration_key";
private final static NumberMapper<Long> mapper = new NumberMapper<Long>(Long.class);
private static final long defaultVibrationTime = 100;
private final float vibrationTimeScale;
@Nullable
private final Vibrator vibrator;
private long time = 0;
public VibratorContainer(@Nullable Vibrator vibrator, @NotNull SharedPreferences preferences, float vibrationTimeScale) {
this.vibrator = vibrator;
this.vibrationTimeScale = vibrationTimeScale;
preferences.registerOnSharedPreferenceChangeListener(this);
onSharedPreferenceChanged(preferences, null);
}
public void vibrate() {
if (time > 0 && vibrator != null) {
vibrator.vibrate(time);
}
}
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, @Nullable String key) {
if (preferences.getBoolean(HAPTIC_FEEDBACK_P_KEY, VibratorContainer.HAPTIC_FEEDBACK_DEFAULT)) {
//noinspection ConstantConditions
this.time = getScaledValue(mapper.parseValue(preferences.getString(HAPTIC_FEEDBACK_DURATION_P_KEY, mapper.formatValue(getScaledValue(defaultVibrationTime)))));
} else {
this.time = 0;
}
}
private long getScaledValue(long vibrationTime) {
return (long) (vibrationTime * vibrationTimeScale);
}
}

View File

@ -1,49 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.common;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.utils.*;
import java.util.Arrays;
import java.util.List;
/**
* User: serso
* Date: 9/21/11
* Time: 12:02 AM
*/
public abstract class AbstractIntervalMapper<T> implements Mapper<Interval<T>> {
@Override
public String formatValue(@Nullable Interval<T> interval) throws IllegalArgumentException {
if (interval != null) {
return CollectionsUtils.formatValue(Arrays.asList(interval.getLeftBorder(), interval.getRightBorder()), ";", getFormatter());
} else {
return null;
}
}
@NotNull
protected abstract Formatter<T> getFormatter();
@Override
public Interval<T> parseValue(@Nullable String s) throws IllegalArgumentException {
//Log.d(AbstractIntervalMapper.class.getName(), "Parsing: " + s);
final List<T> list = CollectionsUtils.split(s, ";", getParser());
assert list.size() == 2;
return newInstance(list.get(0), list.get(1));
}
@NotNull
protected abstract Interval<T> newInstance(@Nullable T left, @Nullable T right);
@NotNull
protected abstract Parser<T> getParser();
}

View File

@ -1,34 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.common;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.math.ValueOfFormatter;
import org.solovyev.common.utils.Formatter;
import org.solovyev.common.utils.Mapper;
/**
* User: serso
* Date: 9/26/11
* Time: 11:27 PM
*/
public class BooleanMapper implements Mapper<Boolean>{
@NotNull
private final Formatter<Boolean> formatter = new ValueOfFormatter<Boolean>();
@Override
public String formatValue(@Nullable Boolean value) throws IllegalArgumentException {
return formatter.formatValue(value);
}
@Override
public Boolean parseValue(@Nullable String value) throws IllegalArgumentException {
return value == null ? null : Boolean.valueOf(value);
}
}

View File

@ -1,44 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.common;
import org.jetbrains.annotations.NotNull;
import org.solovyev.common.utils.Formatter;
import org.solovyev.common.utils.Mapper;
import org.solovyev.common.utils.Parser;
/**
* User: serso
* Date: 9/26/11
* Time: 10:45 PM
*/
public abstract class GenericIntervalMapper<T> extends AbstractIntervalMapper<T> {
@NotNull
private final Mapper<T> mapper;
public GenericIntervalMapper(@NotNull Mapper<T> mapper) {
this.mapper = mapper;
}
@NotNull
@Override
protected Formatter<T> getFormatter() {
return mapper;
}
@NotNull
@Override
protected Parser<T> getParser() {
return mapper;
}
@NotNull
public Mapper<T> getMapper() {
return mapper;
}
}

View File

@ -1,30 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.common;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.utils.Interval;
import org.solovyev.common.utils.NumberInterval;
/**
* User: serso
* Date: 9/20/11
* Time: 11:56 PM
*/
public class NumberIntervalMapper<T extends Number> extends GenericIntervalMapper<T> {
public NumberIntervalMapper(@NotNull Class<T> clazz) {
super(new NumberMapper<T>(clazz));
}
@NotNull
@Override
protected Interval<T> newInstance(@Nullable T left, @Nullable T right) {
return new NumberInterval<T>(left, right);
}
}

View File

@ -1,42 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.common;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.math.ValueOfFormatter;
import org.solovyev.common.utils.Formatter;
import org.solovyev.common.utils.Mapper;
import org.solovyev.common.utils.Parser;
/**
* User: serso
* Date: 9/26/11
* Time: 11:10 PM
*/
public class NumberMapper<T extends Number> implements Mapper<T>{
@NotNull
private final Formatter<T> formatter = new ValueOfFormatter<T>();
@NotNull
private final Parser<T> parser;
public NumberMapper(@NotNull Class<T> clazz) {
this.parser = new NumberParser<T>(clazz);
}
@Override
public String formatValue(@Nullable T value) throws IllegalArgumentException {
return formatter.formatValue(value);
}
@Override
public T parseValue(@Nullable String value) throws IllegalArgumentException {
return this.parser.parseValue(value);
}
}

View File

@ -1,47 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.common;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.utils.Parser;
/**
* User: serso
* Date: 9/26/11
* Time: 11:07 PM
*/
public class NumberParser<T extends Number> implements Parser<T> {
@NotNull
private final Class<T> clazz;
public NumberParser(@NotNull Class<T> clazz) {
this.clazz = clazz;
}
@Override
public T parseValue(@Nullable String value) throws IllegalArgumentException {
T result;
if (value != null) {
if (this.clazz.equals(Integer.class)) {
result = (T) Integer.valueOf(value);
} else if (this.clazz.equals(Float.class)) {
result = (T) Float.valueOf(value);
} else if (this.clazz.equals(Long.class)) {
result = (T) Long.valueOf(value);
} else {
throw new UnsupportedOperationException(this.clazz + " is not supported!");
}
} else {
result = null;
}
return result;
}
}

View File

@ -1,29 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.common;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.utils.Equalizer;
/**
* User: serso
* Date: 12/17/11
* Time: 11:11 PM
*/
public enum SameEqualizer implements Equalizer {
instance;
private SameEqualizer() {
}
@Override
public boolean equals(@Nullable Object first, @Nullable Object second) {
return first == second;
}
}

View File

@ -1,68 +0,0 @@
package org.solovyev.common.math;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 9/20/11
* Time: 5:42 PM
*/
public class DiscreteNormalizer implements Normalizer {
@NotNull
private final Normalizer normalizer;
private final double min;
private final double step;
public DiscreteNormalizer(double min, double max, int steps) {
this(min, max, steps, null);
}
public DiscreteNormalizer(double min, double max, int steps, @Nullable Normalizer normalizer) {
assert min <= max;
assert steps > 1;
if (normalizer != null) {
this.normalizer = normalizer;
} else {
this.normalizer = new LinearNormalizer(min, max);
}
this.step = this.normalizer.normalize((max - min) / (steps - 1));
this.min = this.normalizer.normalize(min);
}
public DiscreteNormalizer(double min, double max, double step) {
assert min <= max;
assert step > 0;
this.normalizer = new LinearNormalizer(min, max);
this.step = normalizer.normalize(step);
this.min = normalizer.normalize(min);
}
@Override
public double normalize(double value) {
double normalizedValue = normalizer.normalize(value);
double result = min;
while (true) {
if ( result + step > normalizedValue ) {
break;
} else {
result += step;
}
}
return result;
}
@Override
public double denormalize(double value) {
return normalizer.denormalize(value);
}
}

View File

@ -1,38 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.common.math;
/**
* User: serso
* Date: 9/19/11
* Time: 9:31 PM
*/
public class LinearNormalizer implements Normalizer {
private final double min;
private final double max;
public LinearNormalizer(double min, double max) {
this.min = min;
this.max = max;
}
@Override
public double normalize(double value){
if ((max - min) != 0d) {
return (value - min) / (max - min);
} else {
return 1d;
}
}
@Override
public double denormalize(double value){
return min + value * (max - min);
}
}

View File

@ -1,13 +0,0 @@
package org.solovyev.common.math;
/**
* User: serso
* Date: 9/20/11
* Time: 5:30 PM
*/
public interface Normalizer {
double normalize(double value);
double denormalize(double value);
}

View File

@ -1,34 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.common.math;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.utils.Formatter;
/**
* User: serso
* Date: 9/20/11
* Time: 10:52 PM
*/
public class ValueOfFormatter<T> implements Formatter<T>{
private final boolean processNulls;
public ValueOfFormatter() {
this(false);
}
public ValueOfFormatter(boolean processNulls) {
this.processNulls = processNulls;
}
@Override
public String formatValue(@Nullable T t) throws IllegalArgumentException {
return t == null ? (processNulls ? String.valueOf(t) : null) : String.valueOf(t);
}
}