Major UI overhaul

This commit is contained in:
Marvin W 2016-11-13 15:49:54 +01:00
parent 4d8fb82ae5
commit 4f3d77f9be
No known key found for this signature in database
GPG Key ID: 072E9235DB996F2A
23 changed files with 997 additions and 334 deletions

View File

@ -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'
}

View File

@ -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<PreferenceManager> 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;
}
}

View File

@ -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<Library> 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()])));

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}
}

View File

@ -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<OnSwitchChangeListener> mSwitchChangeListeners =
new ArrayList<OnSwitchChangeListener>();
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<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
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();
}
}

View File

@ -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()));
}
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/ripple_material_dark">
<item android:drawable="@color/switchbar_background_color"/>
</ripple>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
android:pathData="M14,10H2V12H14V10M14,6H2V8H14V6M2,16H10V14H2V16M21.5,11.5L23,13L16,20L11.5,15.5L13,14L16,17L21.5,11.5Z" />
</vector>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<color xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/switchbar_background_color"/>

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 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
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:gravity="center_vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/selectableItemBackground"
android:clipToPadding="false"
android:focusable="true" >
<LinearLayout
android:id="@+id/icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="56dp"
android:gravity="start|center_vertical"
android:orientation="horizontal"
android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<android.support.v7.internal.widget.PreferenceImageView
android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:maxWidth="48dp"
app:maxHeight="48dp" />
</LinearLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<TextView android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
<TextView android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10" />
</RelativeLayout>
<!-- Preference should place its actual preference widget here. -->
<LinearLayout android:id="@android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="end|center_vertical"
android:paddingStart="16dp"
android:orientation="vertical" />
</LinearLayout>

View File

@ -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"/>
<TextView

View File

@ -0,0 +1,49 @@
<!--
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.
-->
<RelativeLayout android:id="@+id/app_bar"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@drawable/switchbar_background"
android:clickable="true"
android:gravity="center_vertical">
<ImageView
android:id="@+id/app_icon"
android:layout_width="72dp"
android:layout_height="40dp"
android:layout_centerVertical="true"
android:gravity="end"/>
<TextView
android:id="@+id/app_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignWithParentIfMissing="true"
android:layout_centerVertical="true"
android:layout_marginEnd="16dp"
android:layout_marginLeft="72dp"
android:layout_marginRight="16dp"
android:layout_marginStart="72dp"
android:textAlignment="viewStart"
android:textColor="?android:attr/textColorPrimaryInverse"
android:theme="@style/TextAppearance.AppCompat.Title.Inverse"/>
</RelativeLayout>

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/toolbar"/>
<org.microg.tools.ui.SwitchBar
android:id="@+id/switch_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/switchbar_background_color"
android:visibility="gone"/>
<FrameLayout
android:id="@+id/custom_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="@+id/content_wrapper"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 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.
*/
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="@+id/switch_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="72dp"
android:layout_marginStart="72dp"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="2"
android:text="@string/v7_preference_on"
android:textAlignment="viewStart"
android:textColor="?android:attr/textColorPrimaryInverse"
android:theme="@style/TextAppearance.AppCompat.Title.Inverse"/>
<org.microg.tools.ui.ToggleSwitch
android:id="@+id/switch_widget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:background="@null"/>
</merge>

View File

@ -18,4 +18,7 @@
<color name="settings_theme_primary">#ff263238</color>
<color name="settings_theme_primary_dark">#ff21272b</color>
<color name="settings_theme_accent">#ff009688</color>
<color name="switchbar_background_color">#ff37474f</color>
<color name="switch_accent_color">#ff7fcac3</color>
</resources>

View File

@ -17,7 +17,7 @@
<resources>
<string name="lib_name">microG UI Tools</string>
<string name="lib_license">Apache License 2.0 by microG Team</string>
<string name="lib_license">Apache License 2.0, microG Team</string>
<string name="about_version_str">Version %1$s</string>
<string name="about_name_version_str">%1$s %2$s</string>
@ -39,5 +39,6 @@
<string name="about_android_support_v4">v4 Support Library</string>
<string name="about_android_support_v7_appcompat">v7 appcompat Support Library</string>
<string name="about_android_support_license">Apache License 2.0 by The Android Open Source Project</string>
<string name="about_android_support_v7_preference">v7 preference Support Library</string>
<string name="about_android_support_license">Apache License 2.0, The Android Open Source Project</string>
</resources>

View File

@ -16,7 +16,7 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="SettingsTheme" parent="Theme.AppCompat.Light.NoActionBar">
<style name="SettingsTheme" parent="@style/PreferenceFixTheme.Light.NoActionBar">
<item name="colorPrimary">@color/settings_theme_primary</item>
<item name="colorPrimaryDark">@color/settings_theme_primary_dark</item>
<item name="android:colorPrimary" tools:targetApi="21">@color/settings_theme_primary</item>