diff --git a/microg-ui-tools/build.gradle b/microg-ui-tools/build.gradle index c3cac8ab..cae89799 100644 --- a/microg-ui-tools/build.gradle +++ b/microg-ui-tools/build.gradle @@ -43,7 +43,8 @@ android { } dependencies { - compile 'com.android.support:support-v4:23.4.0' - compile 'com.android.support:appcompat-v7:23.4.0' + compile 'com.android.support:support-v4:25.0.0' + compile 'com.android.support:appcompat-v7:25.0.0' + compile 'com.takisoft.fix:preference-v7:25.0.0.1' } diff --git a/microg-ui-tools/src/main/java/android/support/v4/preference/PreferenceFragment.java b/microg-ui-tools/src/main/java/android/support/v4/preference/PreferenceFragment.java deleted file mode 100644 index 8cb10aa6..00000000 --- a/microg-ui-tools/src/main/java/android/support/v4/preference/PreferenceFragment.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright 2013-2016 microG Project Team - * - * 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 android.support.v4.preference; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.preference.Preference; -import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ListView; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; - -public abstract class PreferenceFragment extends Fragment { - - private static final int FIRST_REQUEST_CODE = 100; - private static final int MSG_BIND_PREFERENCES = 1; - private static final String PREFERENCES_TAG = "android:preferences"; - private boolean mHavePrefs; - private boolean mInitDone; - private ListView mList; - private PreferenceManager mPreferenceManager; - - private Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - - case MSG_BIND_PREFERENCES: - bindPreferences(); - break; - } - } - }; - - final private Runnable mRequestFocus = new Runnable() { - public void run() { - mList.focusableViewAvailable(mList); - } - }; - - private void bindPreferences() { - PreferenceScreen localPreferenceScreen = getPreferenceScreen(); - if (localPreferenceScreen != null) { - ListView localListView = getListView(); - localPreferenceScreen.bind(localListView); - } - } - - private void ensureList() { - if (mList == null) { - View view = getView(); - if (view == null) { - throw new IllegalStateException("Content view not yet created"); - } - - View listView = view.findViewById(android.R.id.list); - if (!(listView instanceof ListView)) { - throw new RuntimeException( - "Content has view with id attribute 'android.R.id.list' that is not a ListView class"); - } - - mList = (ListView) listView; - if (mList == null) { - throw new RuntimeException( - "Your content must have a ListView whose id attribute is 'android.R.id.list'"); - } - - mHandler.post(mRequestFocus); - } - } - - private void postBindPreferences() { - if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) { - mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget(); - } - } - - private void requirePreferenceManager() { - if (this.mPreferenceManager == null) { - throw new RuntimeException("This should be called after super.onCreate."); - } - } - - public void addPreferencesFromIntent(Intent intent) { - requirePreferenceManager(); - PreferenceScreen screen = inflateFromIntent(intent, getPreferenceScreen()); - setPreferenceScreen(screen); - } - - public void addPreferencesFromResource(int resId) { - requirePreferenceManager(); - PreferenceScreen screen = inflateFromResource(getActivity(), resId, getPreferenceScreen()); - setPreferenceScreen(screen); - } - - public Preference findPreference(CharSequence key) { - if (mPreferenceManager == null) { - return null; - } - return mPreferenceManager.findPreference(key); - } - - public ListView getListView() { - ensureList(); - return mList; - } - - public PreferenceManager getPreferenceManager() { - return mPreferenceManager; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - getListView().setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); - if (mHavePrefs) { - bindPreferences(); - } - mInitDone = true; - if (savedInstanceState != null) { - Bundle localBundle = savedInstanceState.getBundle(PREFERENCES_TAG); - if (localBundle != null) { - PreferenceScreen screen = getPreferenceScreen(); - if (screen != null) { - screen.restoreHierarchyState(localBundle); - } - } - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - dispatchActivityResult(requestCode, resultCode, data); - } - - @Override - public void onCreate(Bundle paramBundle) { - super.onCreate(paramBundle); - mPreferenceManager = createPreferenceManager(); - } - - @Override - public View onCreateView(LayoutInflater paramLayoutInflater, ViewGroup paramViewGroup, - Bundle paramBundle) { - ListView listView = new ListView(paramLayoutInflater.getContext()); - listView.setId(android.R.id.list); - listView.setDrawSelectorOnTop(false); - listView.setPadding(12, 6, 12, 0); - //listView.setSelector(null); - return listView; - } - - @Override - public void onDestroy() { - super.onDestroy(); - dispatchActivityDestroy(); - } - - @Override - public void onDestroyView() { - mList = null; - mHandler.removeCallbacks(mRequestFocus); - mHandler.removeMessages(MSG_BIND_PREFERENCES); - super.onDestroyView(); - } - - @Override - public void onSaveInstanceState(Bundle bundle) { - super.onSaveInstanceState(bundle); - PreferenceScreen screen = getPreferenceScreen(); - if (screen != null) { - Bundle localBundle = new Bundle(); - screen.saveHierarchyState(localBundle); - bundle.putBundle(PREFERENCES_TAG, localBundle); - } - } - - @Override - public void onStop() { - super.onStop(); - dispatchActivityStop(); - } - - /** - * Access methods with visibility private * - */ - - private PreferenceManager createPreferenceManager() { - try { - Constructor c = PreferenceManager.class - .getDeclaredConstructor(Activity.class, int.class); - c.setAccessible(true); - return c.newInstance(this.getActivity(), FIRST_REQUEST_CODE); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private PreferenceScreen getPreferenceScreen() { - try { - Method m = PreferenceManager.class.getDeclaredMethod("getPreferenceScreen"); - m.setAccessible(true); - return (PreferenceScreen) m.invoke(mPreferenceManager); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private void setPreferenceScreen(PreferenceScreen preferenceScreen) { - try { - Method m = PreferenceManager.class - .getDeclaredMethod("setPreferences", PreferenceScreen.class); - m.setAccessible(true); - boolean result = (Boolean) m.invoke(mPreferenceManager, preferenceScreen); - if (result && preferenceScreen != null) { - mHavePrefs = true; - if (mInitDone) { - postBindPreferences(); - } - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private void dispatchActivityResult(int requestCode, int resultCode, Intent data) { - try { - Method m = PreferenceManager.class - .getDeclaredMethod("dispatchActivityResult", int.class, int.class, - Intent.class); - m.setAccessible(true); - m.invoke(mPreferenceManager, requestCode, resultCode, data); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private void dispatchActivityDestroy() { - try { - Method m = PreferenceManager.class.getDeclaredMethod("dispatchActivityDestroy"); - m.setAccessible(true); - m.invoke(mPreferenceManager); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private void dispatchActivityStop() { - try { - Method m = PreferenceManager.class.getDeclaredMethod("dispatchActivityStop"); - m.setAccessible(true); - m.invoke(mPreferenceManager); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public PreferenceScreen inflateFromResource(Context context, int resId, - PreferenceScreen rootPreferences) { - PreferenceScreen preferenceScreen; - try { - Method m = PreferenceManager.class - .getDeclaredMethod("inflateFromResource", Context.class, int.class, - PreferenceScreen.class); - m.setAccessible(true); - preferenceScreen = (PreferenceScreen) m - .invoke(mPreferenceManager, context, resId, rootPreferences); - } catch (Exception e) { - throw new RuntimeException(e); - } - return preferenceScreen; - } - - public PreferenceScreen inflateFromIntent(Intent queryIntent, - PreferenceScreen rootPreferences) { - PreferenceScreen preferenceScreen; - try { - Method m = PreferenceManager.class - .getDeclaredMethod("inflateFromIntent", Intent.class, PreferenceScreen.class); - m.setAccessible(true); - preferenceScreen = (PreferenceScreen) m - .invoke(mPreferenceManager, queryIntent, rootPreferences); - } catch (Exception e) { - throw new RuntimeException(e); - } - return preferenceScreen; - } -} diff --git a/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractAboutFragment.java b/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractAboutFragment.java index 7e2a915b..9a728fa0 100644 --- a/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractAboutFragment.java +++ b/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractAboutFragment.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 microG Project Team + * Copyright (C) 2013-2016 microG Project Team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,6 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; -import android.support.v4.content.ContextCompat; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; @@ -40,21 +39,21 @@ public abstract class AbstractAboutFragment extends Fragment { protected abstract void collectLibraries(List libraries); - protected Drawable getIcon() { + public static Drawable getIcon(Context context) { try { - PackageManager pm = getContext().getPackageManager(); - return pm.getPackageInfo(getContext().getPackageName(), 0).applicationInfo.loadIcon(pm); + PackageManager pm = context.getPackageManager(); + return pm.getPackageInfo(context.getPackageName(), 0).applicationInfo.loadIcon(pm); } catch (PackageManager.NameNotFoundException e) { // Never happens, self package always exists! throw new RuntimeException(e); } } - protected String getAppName() { + public static String getAppName(Context context) { try { - PackageManager pm = getContext().getPackageManager(); - CharSequence label = pm.getPackageInfo(getContext().getPackageName(), 0).applicationInfo.loadLabel(pm); - if (TextUtils.isEmpty(label)) return getContext().getPackageName(); + PackageManager pm = context.getPackageManager(); + CharSequence label = pm.getPackageInfo(context.getPackageName(), 0).applicationInfo.loadLabel(pm); + if (TextUtils.isEmpty(label)) return context.getPackageName(); return label.toString().trim(); } catch (PackageManager.NameNotFoundException e) { // Never happens, self package always exists! @@ -62,7 +61,11 @@ public abstract class AbstractAboutFragment extends Fragment { } } - protected String getLibVersion(String packageName) { + protected String getAppName() { + return getAppName(getContext()); + } + + public static String getLibVersion(String packageName) { try { String versionName = (String) Class.forName(packageName + ".BuildConfig").getField("VERSION_NAME").get(null); if (TextUtils.isEmpty(versionName)) return ""; @@ -72,8 +75,12 @@ public abstract class AbstractAboutFragment extends Fragment { } } + public static String getSelfVersion(Context context) { + return getLibVersion(context.getPackageName()); + } + protected String getSelfVersion() { - return getLibVersion(getContext().getPackageName()); + return getSelfVersion(getContext()); } protected String getSummary() { @@ -84,7 +91,7 @@ public abstract class AbstractAboutFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View aboutRoot = inflater.inflate(R.layout.about_root, container, false); - ((ImageView) aboutRoot.findViewById(android.R.id.icon)).setImageDrawable(getIcon()); + ((ImageView) aboutRoot.findViewById(android.R.id.icon)).setImageDrawable(getIcon(getContext())); ((TextView) aboutRoot.findViewById(android.R.id.title)).setText(getAppName()); ((TextView) aboutRoot.findViewById(R.id.about_version)).setText(getString(R.string.about_version_str, getSelfVersion())); String summary = getSummary(); @@ -97,6 +104,7 @@ public abstract class AbstractAboutFragment extends Fragment { libraries.add(new Library(BuildConfig.APPLICATION_ID, getString(R.string.lib_name), getString(R.string.lib_license))); libraries.add(new Library("android.support.v4", getString(R.string.about_android_support_v4), getString(R.string.about_android_support_license))); libraries.add(new Library("android.support.v7.appcompat", getString(R.string.about_android_support_v7_appcompat), getString(R.string.about_android_support_license))); + libraries.add(new Library("android.support.v7.preference#hide_version", getString(R.string.about_android_support_v7_preference), getString(R.string.about_android_support_license))); collectLibraries(libraries); Collections.sort(libraries); ((ListView) aboutRoot.findViewById(android.R.id.list)).setAdapter(new LibraryAdapter(getContext(), libraries.toArray(new Library[libraries.size()]))); diff --git a/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSelfCheckFragment.java b/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSelfCheckFragment.java index 1a3f25b3..9ef9fd92 100644 --- a/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSelfCheckFragment.java +++ b/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSelfCheckFragment.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 microG Project Team + * Copyright (C) 2013-2016 microG Project Team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsActivity.java b/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsActivity.java new file mode 100644 index 00000000..ea5b9427 --- /dev/null +++ b/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsActivity.java @@ -0,0 +1,79 @@ +package org.microg.tools.ui; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.MenuItem; +import android.view.ViewGroup; + +public class AbstractSettingsActivity extends AppCompatActivity { + protected boolean showHomeAsUp = false; + protected int preferencesResource = 0; + private ViewGroup customBarContainer; + protected int customBarLayout = 0; + protected SwitchBar switchBar; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.settings_activity); + + setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + if (showHomeAsUp) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + + switchBar = (SwitchBar) findViewById(R.id.switch_bar); + + customBarContainer = (ViewGroup) findViewById(R.id.custom_bar); + if (customBarLayout != 0) { + customBarContainer.addView(getLayoutInflater().inflate(customBarLayout, customBarContainer, false)); + } + + getSupportFragmentManager().beginTransaction() + .replace(R.id.content_wrapper, getFragment()) + .commit(); + } + + public void setCustomBarLayout(int layout) { + customBarLayout = layout; + if (customBarContainer != null) { + customBarContainer.removeAllViews(); + customBarContainer.addView(getLayoutInflater().inflate(customBarLayout, customBarContainer, false)); + } + } + + public SwitchBar getSwitchBar() { + return switchBar; + } + + public void replaceFragment(Fragment fragment) { + getSupportFragmentManager().beginTransaction() + .addToBackStack("root") + .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) + .replace(R.id.content_wrapper, fragment) + .commit(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + + protected Fragment getFragment() { + if (preferencesResource == 0) { + throw new IllegalStateException("Neither preferencesResource given, nor overriden getFragment()"); + } + ResourceSettingsFragment fragment = new ResourceSettingsFragment(); + Bundle b = new Bundle(); + b.putInt(ResourceSettingsFragment.EXTRA_PREFERENCE_RESOURCE, preferencesResource); + fragment.setArguments(b); + return fragment; + } +} diff --git a/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsFragment.java b/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsFragment.java new file mode 100644 index 00000000..89422e48 --- /dev/null +++ b/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsFragment.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2013-2016 microG Project Team + * + * 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.microg.tools.ui; + +import android.support.v4.app.DialogFragment; +import android.support.v7.preference.Preference; + +import com.takisoft.fix.support.v7.preference.PreferenceFragmentCompat; + +public abstract class AbstractSettingsFragment extends PreferenceFragmentCompat { + private static final String TAG = AbstractSettingsFragment.class.getSimpleName(); + + private static final String DIALOG_FRAGMENT_TAG = + "android.support.v7.preference.PreferenceFragment.DIALOG"; + + @Override + public void onDisplayPreferenceDialog(Preference preference) { + if (preference instanceof DialogPreference) { + DialogFragment f = DialogPreference.DialogPreferenceCompatDialogFragment.newInstance(preference.getKey()); + f.setTargetFragment(this, 0); + f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG); + } else { + super.onDisplayPreferenceDialog(preference); + } + } +} diff --git a/microg-ui-tools/src/main/java/org/microg/tools/ui/DialogPreference.java b/microg-ui-tools/src/main/java/org/microg/tools/ui/DialogPreference.java new file mode 100644 index 00000000..b592db48 --- /dev/null +++ b/microg-ui-tools/src/main/java/org/microg/tools/ui/DialogPreference.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2013-2016 microG Project Team + * + * 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.microg.tools.ui; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceDialogFragmentCompat; +import android.support.v7.preference.PreferenceFragmentCompat; +import android.support.v7.preference.PreferenceViewHolder; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +public class DialogPreference extends android.support.v7.preference.DialogPreference implements PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback { + + private static final String DIALOG_FRAGMENT_TAG = + "android.support.v7.preference.PreferenceFragment.DIALOG"; + + public DialogPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public DialogPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public DialogPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public DialogPreference(Context context) { + super(context); + } + + protected View onCreateDialogView() { + return null; + } + + /** + * Called when the dialog is dismissed and should be used to save data to + * the {@link SharedPreferences}. + * + * @param positiveResult Whether the positive button was clicked (true), or + * the negative button was clicked or the dialog was canceled (false). + */ + protected void onDialogClosed(boolean positiveResult) { + } + + @Override + public boolean onPreferenceDisplayDialog(PreferenceFragmentCompat caller, Preference pref) { + DialogPreferenceCompatDialogFragment fragment = new DialogPreferenceCompatDialogFragment(); + fragment.setTargetFragment(caller, 0); + fragment.show(caller.getFragmentManager(), DIALOG_FRAGMENT_TAG); + return true; + } + + @Override + public void onBindViewHolder(PreferenceViewHolder view) { + super.onBindViewHolder(view); + + ViewGroup.LayoutParams layoutParams = view.findViewById(R.id.icon_frame).getLayoutParams(); + if (layoutParams instanceof LinearLayout.LayoutParams) { + if (((LinearLayout.LayoutParams) layoutParams).leftMargin < 0) { + ((LinearLayout.LayoutParams) layoutParams).leftMargin = 0; + } + } + } + + public static class DialogPreferenceCompatDialogFragment extends PreferenceDialogFragmentCompat { + + @Override + protected View onCreateDialogView(Context context) { + if (getPreference() instanceof DialogPreference) { + View view = ((DialogPreference) getPreference()).onCreateDialogView(); + if (view != null) return view; + } + return super.onCreateDialogView(context); + } + + @Override + public void onDialogClosed(boolean positiveResult) { + if (getPreference() instanceof DialogPreference) { + ((DialogPreference) getPreference()).onDialogClosed(positiveResult); + } + } + + public static DialogFragment newInstance(String key) { + final DialogPreferenceCompatDialogFragment fragment = new DialogPreferenceCompatDialogFragment(); + final Bundle b = new Bundle(1); + b.putString(ARG_KEY, key); + fragment.setArguments(b); + return fragment; + } + } +} diff --git a/microg-ui-tools/src/main/java/org/microg/tools/ui/DimmableIconPreference.java b/microg-ui-tools/src/main/java/org/microg/tools/ui/DimmableIconPreference.java new file mode 100644 index 00000000..c86f7813 --- /dev/null +++ b/microg-ui-tools/src/main/java/org/microg/tools/ui/DimmableIconPreference.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014 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.microg.tools.ui; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceViewHolder; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** + * A preference item that can dim the icon when it's disabled, either directly or because its parent + * is disabled. + */ +public class DimmableIconPreference extends Preference { + private static final int ICON_ALPHA_ENABLED = 255; + private static final int ICON_ALPHA_DISABLED = 102; + + private final CharSequence mContentDescription; + + public DimmableIconPreference(Context context) { + this(context, (AttributeSet) null); + } + + public DimmableIconPreference(Context context, AttributeSet attrs) { + super(context, attrs); + mContentDescription = null; + } + + public DimmableIconPreference(Context context, CharSequence contentDescription) { + super(context); + mContentDescription = contentDescription; + } + + protected boolean shouldDimIcon() { + return !isEnabled(); + } + + private void dimIcon(boolean dimmed) { + Drawable icon = getIcon(); + if (icon != null) { + icon.mutate().setAlpha(dimmed ? ICON_ALPHA_DISABLED : ICON_ALPHA_ENABLED); + setIcon(icon); + } + } + + @Override + public void onBindViewHolder(PreferenceViewHolder view) { + super.onBindViewHolder(view); + if (!TextUtils.isEmpty(mContentDescription)) { + final TextView titleView = (TextView) view.findViewById(android.R.id.title); + titleView.setContentDescription(mContentDescription); + } + ViewGroup.LayoutParams layoutParams = view.findViewById(R.id.icon_frame).getLayoutParams(); + if (layoutParams instanceof LinearLayout.LayoutParams) { + if (((LinearLayout.LayoutParams) layoutParams).leftMargin < 0) { + ((LinearLayout.LayoutParams) layoutParams).leftMargin = 0; + } + } + dimIcon(shouldDimIcon()); + } +} diff --git a/microg-ui-tools/src/main/java/org/microg/tools/ui/ResourceSettingsFragment.java b/microg-ui-tools/src/main/java/org/microg/tools/ui/ResourceSettingsFragment.java new file mode 100644 index 00000000..85dd9b1f --- /dev/null +++ b/microg-ui-tools/src/main/java/org/microg/tools/ui/ResourceSettingsFragment.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 microG Project Team + * + * 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.microg.tools.ui; + +import android.os.Bundle; +import android.support.annotation.Nullable; + +public class ResourceSettingsFragment extends AbstractSettingsFragment { + + public static final String EXTRA_PREFERENCE_RESOURCE = "preferencesResource"; + + protected int preferencesResource; + + @Override + public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) { + Bundle b = getArguments(); + if (b != null) { + preferencesResource = b.getInt(EXTRA_PREFERENCE_RESOURCE, preferencesResource); + } + if (preferencesResource != 0) { + addPreferencesFromResource(preferencesResource); + } + } +} diff --git a/microg-ui-tools/src/main/java/org/microg/tools/ui/SwitchBar.java b/microg-ui-tools/src/main/java/org/microg/tools/ui/SwitchBar.java new file mode 100644 index 00000000..b086b748 --- /dev/null +++ b/microg-ui-tools/src/main/java/org/microg/tools/ui/SwitchBar.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2014-2016 microG Project Team + * + * 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.microg.tools.ui; + +import android.content.Context; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.v7.widget.SwitchCompat; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; +import android.text.style.TextAppearanceSpan; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.LinearLayout; +import android.widget.Switch; +import android.widget.TextView; + +import java.util.ArrayList; + +import static android.os.Build.VERSION.SDK_INT; + +public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedChangeListener, + View.OnClickListener { + + public static interface OnSwitchChangeListener { + /** + * Called when the checked state of the Switch has changed. + * + * @param switchView The Switch view whose state has changed. + * @param isChecked The new checked state of switchView. + */ + void onSwitchChanged(SwitchCompat switchView, boolean isChecked); + } + + private final TextAppearanceSpan mSummarySpan; + + private ToggleSwitch mSwitch; + private TextView mTextView; + private String mLabel; + private String mSummary; + + private ArrayList mSwitchChangeListeners = + new ArrayList(); + + public SwitchBar(Context context) { + this(context, null); + } + + public SwitchBar(Context context, AttributeSet attrs) { + super(context, attrs); + + LayoutInflater.from(context).inflate(R.layout.switch_bar, this); + + mTextView = (TextView) findViewById(R.id.switch_text); + if (SDK_INT > Build.VERSION_CODES.JELLY_BEAN) { + mTextView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); + } + mLabel = getResources().getString(R.string.v7_preference_off); + mSummarySpan = new TextAppearanceSpan(context, android.support.v7.appcompat.R.style.TextAppearance_AppCompat_Widget_Switch); + updateText(); + + mSwitch = (ToggleSwitch) findViewById(R.id.switch_widget); + // Prevent onSaveInstanceState() to be called as we are managing the state of the Switch + // on our own + mSwitch.setSaveEnabled(false); + if (SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + mSwitch.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); + } + + addOnSwitchChangeListener(new OnSwitchChangeListener() { + @Override + public void onSwitchChanged(SwitchCompat switchView, boolean isChecked) { + setTextViewLabel(isChecked); + } + }); + + setOnClickListener(this); + + // Default is hide + setVisibility(View.GONE); + } + + public void setTextViewLabel(boolean isChecked) { + mLabel = getResources() + .getString(isChecked ? R.string.v7_preference_on : R.string.v7_preference_off); + updateText(); + } + + public void setSummary(String summary) { + mSummary = summary; + updateText(); + } + + private void updateText() { + if (TextUtils.isEmpty(mSummary)) { + mTextView.setText(mLabel); + return; + } + final SpannableStringBuilder ssb = new SpannableStringBuilder(mLabel).append('\n'); + final int start = ssb.length(); + ssb.append(mSummary); + ssb.setSpan(mSummarySpan, start, ssb.length(), 0); + mTextView.setText(ssb); + } + + public void setChecked(boolean checked) { + setTextViewLabel(checked); + mSwitch.setChecked(checked); + } + + public void setCheckedInternal(boolean checked) { + setTextViewLabel(checked); + mSwitch.setCheckedInternal(checked); + } + + public boolean isChecked() { + return mSwitch.isChecked(); + } + + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + mTextView.setEnabled(enabled); + mSwitch.setEnabled(enabled); + } + + public final ToggleSwitch getSwitch() { + return mSwitch; + } + + public void show() { + if (!isShowing()) { + setVisibility(View.VISIBLE); + mSwitch.setOnCheckedChangeListener(this); + } + } + + public void hide() { + if (isShowing()) { + setVisibility(View.GONE); + mSwitch.setOnCheckedChangeListener(null); + } + } + + public boolean isShowing() { + return (getVisibility() == View.VISIBLE); + } + + @Override + public void onClick(View v) { + final boolean isChecked = !mSwitch.isChecked(); + setChecked(isChecked); + } + + public void propagateChecked(boolean isChecked) { + final int count = mSwitchChangeListeners.size(); + for (int n = 0; n < count; n++) { + mSwitchChangeListeners.get(n).onSwitchChanged(mSwitch, isChecked); + } + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + propagateChecked(isChecked); + } + + public void addOnSwitchChangeListener(OnSwitchChangeListener listener) { + if (mSwitchChangeListeners.contains(listener)) { + throw new IllegalStateException("Cannot add twice the same OnSwitchChangeListener"); + } + mSwitchChangeListeners.add(listener); + } + + public void removeOnSwitchChangeListener(OnSwitchChangeListener listener) { + if (!mSwitchChangeListeners.contains(listener)) { + throw new IllegalStateException("Cannot remove OnSwitchChangeListener"); + } + mSwitchChangeListeners.remove(listener); + } + + static class SavedState extends BaseSavedState { + boolean checked; + boolean visible; + + SavedState(Parcelable superState) { + super(superState); + } + + /** + * Constructor called from {@link #CREATOR} + */ + private SavedState(Parcel in) { + super(in); + checked = (Boolean) in.readValue(null); + visible = (Boolean) in.readValue(null); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeValue(checked); + out.writeValue(visible); + } + + @Override + public String toString() { + return "SwitchBar.SavedState{" + + Integer.toHexString(System.identityHashCode(this)) + + " checked=" + checked + + " visible=" + visible + "}"; + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + + @Override + public Parcelable onSaveInstanceState() { + Parcelable superState = super.onSaveInstanceState(); + + SavedState ss = new SavedState(superState); + ss.checked = mSwitch.isChecked(); + ss.visible = isShowing(); + return ss; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + SavedState ss = (SavedState) state; + + super.onRestoreInstanceState(ss.getSuperState()); + + mSwitch.setCheckedInternal(ss.checked); + setTextViewLabel(ss.checked); + setVisibility(ss.visible ? View.VISIBLE : View.GONE); + mSwitch.setOnCheckedChangeListener(ss.visible ? this : null); + + requestLayout(); + } + + @Override + public CharSequence getAccessibilityClassName() { + return Switch.class.getName(); + } +} diff --git a/microg-ui-tools/src/main/java/org/microg/tools/ui/TintIconPreference.java b/microg-ui-tools/src/main/java/org/microg/tools/ui/TintIconPreference.java new file mode 100644 index 00000000..ae81350a --- /dev/null +++ b/microg-ui-tools/src/main/java/org/microg/tools/ui/TintIconPreference.java @@ -0,0 +1,44 @@ +package org.microg.tools.ui; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.v4.graphics.drawable.DrawableCompat; +import android.support.v7.preference.PreferenceViewHolder; +import android.util.AttributeSet; +import android.util.TypedValue; + +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.LOLLIPOP; + +public class TintIconPreference extends DimmableIconPreference { + + public TintIconPreference(Context context) { + this(context, (AttributeSet) null); + } + + public TintIconPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + private static int getThemeAccentColor(Context context) { + int colorAttr; + if (SDK_INT >= LOLLIPOP) { + colorAttr = android.R.attr.colorAccent; + } else { + //Get colorAccent defined for AppCompat + colorAttr = context.getResources().getIdentifier("colorAccent", "attr", context.getPackageName()); + } + TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(colorAttr, outValue, true); + return outValue.data; + } + + @Override + public void onBindViewHolder(PreferenceViewHolder view) { + super.onBindViewHolder(view); + Drawable icon = getIcon(); + if (icon != null) { + DrawableCompat.setTint(icon, getThemeAccentColor(getContext())); + } + } +} diff --git a/microg-ui-tools/src/main/java/org/microg/tools/ui/ToggleSwitch.java b/microg-ui-tools/src/main/java/org/microg/tools/ui/ToggleSwitch.java new file mode 100644 index 00000000..44f0a09a --- /dev/null +++ b/microg-ui-tools/src/main/java/org/microg/tools/ui/ToggleSwitch.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2014-2016 microG Project Team + * + * 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.microg.tools.ui; + +import android.content.Context; +import android.support.v7.widget.SwitchCompat; +import android.util.AttributeSet; + +public class ToggleSwitch extends SwitchCompat { + + private ToggleSwitch.OnBeforeCheckedChangeListener mOnBeforeListener; + + public interface OnBeforeCheckedChangeListener { + boolean onBeforeCheckedChanged(ToggleSwitch toggleSwitch, boolean checked); + } + + public ToggleSwitch(Context context) { + super(context); + } + + public ToggleSwitch(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ToggleSwitch(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public void setOnBeforeCheckedChangeListener(OnBeforeCheckedChangeListener listener) { + mOnBeforeListener = listener; + } + + @Override + public void setChecked(boolean checked) { + if (mOnBeforeListener != null + && mOnBeforeListener.onBeforeCheckedChanged(this, checked)) { + return; + } + super.setChecked(checked); + } + + public void setCheckedInternal(boolean checked) { + super.setChecked(checked); + } +} diff --git a/microg-ui-tools/src/main/res/drawable-v21/switchbar_background.xml b/microg-ui-tools/src/main/res/drawable-v21/switchbar_background.xml new file mode 100644 index 00000000..20909c3a --- /dev/null +++ b/microg-ui-tools/src/main/res/drawable-v21/switchbar_background.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/microg-ui-tools/src/main/res/drawable/self_check.xml b/microg-ui-tools/src/main/res/drawable/self_check.xml new file mode 100644 index 00000000..5c28f0c3 --- /dev/null +++ b/microg-ui-tools/src/main/res/drawable/self_check.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/microg-ui-tools/src/main/res/drawable/switchbar_background.xml b/microg-ui-tools/src/main/res/drawable/switchbar_background.xml new file mode 100644 index 00000000..643d428b --- /dev/null +++ b/microg-ui-tools/src/main/res/drawable/switchbar_background.xml @@ -0,0 +1,4 @@ + + + diff --git a/microg-ui-tools/src/main/res/layout-v21/preference_material.xml b/microg-ui-tools/src/main/res/layout-v21/preference_material.xml new file mode 100644 index 00000000..2c93ad42 --- /dev/null +++ b/microg-ui-tools/src/main/res/layout-v21/preference_material.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/microg-ui-tools/src/main/res/layout/about_root.xml b/microg-ui-tools/src/main/res/layout/about_root.xml index c731c4a7..4488ae32 100644 --- a/microg-ui-tools/src/main/res/layout/about_root.xml +++ b/microg-ui-tools/src/main/res/layout/about_root.xml @@ -34,7 +34,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" - android:text="@+id/about_root_title" + android:text="@string/about_root_title" android:textAppearance="@style/TextAppearance.AppCompat.Headline" android:textColor="?attr/colorAccent"/> @@ -44,7 +44,7 @@ android:layout_height="wrap_content" android:gravity="center_horizontal" android:visibility="gone" - android:text="@+id/about_root_summary" + android:text="@string/about_root_summary" android:textAppearance="@style/TextAppearance.AppCompat.Subhead" android:textColor="?attr/colorAccent"/> @@ -53,7 +53,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" - android:text="@+id/about_root_version" + android:text="@string/about_root_version" android:textAppearance="@style/TextAppearance.AppCompat.Subhead"/> + + + + + + + + + diff --git a/microg-ui-tools/src/main/res/layout/settings_activity.xml b/microg-ui-tools/src/main/res/layout/settings_activity.xml new file mode 100644 index 00000000..4571b19c --- /dev/null +++ b/microg-ui-tools/src/main/res/layout/settings_activity.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + diff --git a/microg-ui-tools/src/main/res/layout/switch_bar.xml b/microg-ui-tools/src/main/res/layout/switch_bar.xml new file mode 100644 index 00000000..b320e3c9 --- /dev/null +++ b/microg-ui-tools/src/main/res/layout/switch_bar.xml @@ -0,0 +1,46 @@ + + + + + + + + + + \ No newline at end of file diff --git a/microg-ui-tools/src/main/res/values/colors.xml b/microg-ui-tools/src/main/res/values/colors.xml index a9410025..237df0fc 100644 --- a/microg-ui-tools/src/main/res/values/colors.xml +++ b/microg-ui-tools/src/main/res/values/colors.xml @@ -18,4 +18,7 @@ #ff263238 #ff21272b #ff009688 + + #ff37474f + #ff7fcac3 diff --git a/microg-ui-tools/src/main/res/values/strings.xml b/microg-ui-tools/src/main/res/values/strings.xml index 05af915f..8a1de419 100644 --- a/microg-ui-tools/src/main/res/values/strings.xml +++ b/microg-ui-tools/src/main/res/values/strings.xml @@ -17,7 +17,7 @@ microG UI Tools - Apache License 2.0 by microG Team + Apache License 2.0, microG Team Version %1$s %1$s %2$s @@ -39,5 +39,6 @@ v4 Support Library v7 appcompat Support Library - Apache License 2.0 by The Android Open Source Project + v7 preference Support Library + Apache License 2.0, The Android Open Source Project diff --git a/microg-ui-tools/src/main/res/values/themes.xml b/microg-ui-tools/src/main/res/values/themes.xml index 80726729..225a7b5f 100644 --- a/microg-ui-tools/src/main/res/values/themes.xml +++ b/microg-ui-tools/src/main/res/values/themes.xml @@ -16,7 +16,7 @@ -