Merge branch 'dev' of https://github.com/iSoron/uhabits into dev
@ -1,129 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.androidbase.activities;
|
|
||||||
|
|
||||||
import android.content.*;
|
|
||||||
import android.os.*;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.appcompat.app.*;
|
|
||||||
import android.view.*;
|
|
||||||
|
|
||||||
import org.isoron.androidbase.*;
|
|
||||||
|
|
||||||
import static android.R.anim.fade_in;
|
|
||||||
import static android.R.anim.fade_out;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for all activities in the application.
|
|
||||||
* <p>
|
|
||||||
* This class delegates the responsibilities of an Android activity to other
|
|
||||||
* classes. For example, callbacks related to menus are forwarded to a {@link
|
|
||||||
* BaseMenu}, while callbacks related to activity results are forwarded to a
|
|
||||||
* {@link BaseScreen}.
|
|
||||||
* <p>
|
|
||||||
* A BaseActivity also installs an {@link java.lang.Thread.UncaughtExceptionHandler}
|
|
||||||
* to the main thread. By default, this handler is an instance of
|
|
||||||
* BaseExceptionHandler, which logs the exception to the disk before the application
|
|
||||||
* crashes. To the default handler, you should override the method
|
|
||||||
* getExceptionHandler.
|
|
||||||
*/
|
|
||||||
abstract public class BaseActivity extends AppCompatActivity
|
|
||||||
{
|
|
||||||
@Nullable
|
|
||||||
private BaseMenu baseMenu;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private BaseScreen screen;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(@Nullable Menu menu)
|
|
||||||
{
|
|
||||||
if (menu == null) return true;
|
|
||||||
if (baseMenu == null) return true;
|
|
||||||
baseMenu.onCreate(getMenuInflater(), menu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(@Nullable MenuItem item)
|
|
||||||
{
|
|
||||||
if (item == null) return false;
|
|
||||||
if (baseMenu == null) return false;
|
|
||||||
return baseMenu.onItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void restartWithFade(Class<?> cls)
|
|
||||||
{
|
|
||||||
new Handler().postDelayed(() ->
|
|
||||||
{
|
|
||||||
finish();
|
|
||||||
overridePendingTransition(fade_in, fade_out);
|
|
||||||
startActivity(new Intent(this, cls));
|
|
||||||
|
|
||||||
}, 500); // HACK: Let the menu disappear first
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBaseMenu(@Nullable BaseMenu baseMenu)
|
|
||||||
{
|
|
||||||
this.baseMenu = baseMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setScreen(@Nullable BaseScreen screen)
|
|
||||||
{
|
|
||||||
this.screen = screen;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showDialog(AppCompatDialogFragment dialog, String tag)
|
|
||||||
{
|
|
||||||
dialog.show(getSupportFragmentManager(), tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showDialog(AppCompatDialog dialog)
|
|
||||||
{
|
|
||||||
dialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onActivityResult(int request, int result, Intent data)
|
|
||||||
{
|
|
||||||
if (screen == null) super.onActivityResult(request, result, data);
|
|
||||||
else screen.onResult(request, result, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState)
|
|
||||||
{
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
Thread.setDefaultUncaughtExceptionHandler(getExceptionHandler());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Thread.UncaughtExceptionHandler getExceptionHandler()
|
|
||||||
{
|
|
||||||
return new BaseExceptionHandler(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume()
|
|
||||||
{
|
|
||||||
super.onResume();
|
|
||||||
if(screen != null) screen.reattachDialogs();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.isoron.androidbase.activities
|
||||||
|
|
||||||
|
import android.R.anim
|
||||||
|
import android.content.*
|
||||||
|
import android.os.*
|
||||||
|
import android.view.*
|
||||||
|
import androidx.appcompat.app.*
|
||||||
|
import org.isoron.androidbase.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for all activities in the application.
|
||||||
|
*
|
||||||
|
* This class delegates the responsibilities of an Android activity to other classes. For example,
|
||||||
|
* callbacks related to menus are forwarded to a []BaseMenu], while callbacks related to activity
|
||||||
|
* results are forwarded to a [BaseScreen].
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* A BaseActivity also installs an [java.lang.Thread.UncaughtExceptionHandler] to the main thread.
|
||||||
|
* By default, this handler is an instance of BaseExceptionHandler, which logs the exception to the
|
||||||
|
* disk before the application crashes. To the default handler, you should override the method
|
||||||
|
* getExceptionHandler.
|
||||||
|
*/
|
||||||
|
abstract class BaseActivity : AppCompatActivity() {
|
||||||
|
private var baseMenu: BaseMenu? = null
|
||||||
|
private var screen: BaseScreen? = null
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||||
|
if (menu != null) baseMenu?.onCreate(menuInflater, menu)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||||
|
if (item == null) return false
|
||||||
|
return baseMenu?.onItemSelected(item) ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun restartWithFade(cls: Class<*>?) {
|
||||||
|
Handler().postDelayed({
|
||||||
|
finish()
|
||||||
|
overridePendingTransition(anim.fade_in, anim.fade_out)
|
||||||
|
startActivity(Intent(this, cls))
|
||||||
|
}, 500) // HACK: Let the menu disappear first
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBaseMenu(baseMenu: BaseMenu?) {
|
||||||
|
this.baseMenu = baseMenu
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setScreen(screen: BaseScreen?) {
|
||||||
|
this.screen = screen
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showDialog(dialog: AppCompatDialogFragment, tag: String?) {
|
||||||
|
dialog.show(supportFragmentManager, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showDialog(dialog: AppCompatDialog) {
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(request: Int, result: Int, data: Intent?) {
|
||||||
|
val screen = screen
|
||||||
|
if(screen == null) super.onActivityResult(request, result, data)
|
||||||
|
else screen.onResult(request, result, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(getExceptionHandler())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getExceptionHandler() = BaseExceptionHandler(this)
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
screen?.reattachDialogs()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun startActivity(intent: Intent?) {
|
||||||
|
try {
|
||||||
|
super.startActivity(intent)
|
||||||
|
} catch(e: ActivityNotFoundException) {
|
||||||
|
this.screen?.showMessage(R.string.activity_not_found)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,110 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.androidbase.activities;
|
|
||||||
|
|
||||||
import android.content.*;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
import android.view.*;
|
|
||||||
import android.widget.*;
|
|
||||||
|
|
||||||
import org.isoron.androidbase.*;
|
|
||||||
import org.isoron.androidbase.utils.*;
|
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static android.os.Build.VERSION_CODES.LOLLIPOP;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for all root views in the application.
|
|
||||||
* <p>
|
|
||||||
* A root view is an Android view that is directly attached to an activity. This
|
|
||||||
* view usually includes a toolbar and a progress bar. This abstract class hides
|
|
||||||
* some of the complexity of setting these things up, for every version of
|
|
||||||
* Android.
|
|
||||||
*/
|
|
||||||
public abstract class BaseRootView extends FrameLayout
|
|
||||||
{
|
|
||||||
@NonNull
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
protected boolean shouldDisplayHomeAsUp = false;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private BaseScreen screen;
|
|
||||||
|
|
||||||
public BaseRootView(@NonNull Context context)
|
|
||||||
{
|
|
||||||
super(context);
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getDisplayHomeAsUp()
|
|
||||||
{
|
|
||||||
return shouldDisplayHomeAsUp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDisplayHomeAsUp(boolean b)
|
|
||||||
{
|
|
||||||
shouldDisplayHomeAsUp = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public Toolbar getToolbar()
|
|
||||||
{
|
|
||||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
|
||||||
if (toolbar == null) throw new RuntimeException(
|
|
||||||
"Your BaseRootView should have a " +
|
|
||||||
"toolbar with id R.id.toolbar");
|
|
||||||
return toolbar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getToolbarColor()
|
|
||||||
{
|
|
||||||
StyledResources res = new StyledResources(context);
|
|
||||||
return res.getColor(R.attr.colorPrimary);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void initToolbar()
|
|
||||||
{
|
|
||||||
if (SDK_INT >= LOLLIPOP)
|
|
||||||
{
|
|
||||||
getToolbar().setElevation(InterfaceUtils.dpToPixels(context, 2));
|
|
||||||
|
|
||||||
View view = findViewById(R.id.toolbarShadow);
|
|
||||||
if (view != null) view.setVisibility(GONE);
|
|
||||||
|
|
||||||
view = findViewById(R.id.headerShadow);
|
|
||||||
if (view != null) view.setVisibility(GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onAttachedToScreen(BaseScreen screen)
|
|
||||||
{
|
|
||||||
this.screen = screen;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public BaseScreen getScreen()
|
|
||||||
{
|
|
||||||
return screen;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.isoron.androidbase.activities
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build.VERSION
|
||||||
|
import android.os.Build.VERSION_CODES
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.appcompat.widget.Toolbar
|
||||||
|
import org.isoron.androidbase.R
|
||||||
|
import org.isoron.androidbase.utils.InterfaceUtils.dpToPixels
|
||||||
|
import org.isoron.androidbase.utils.StyledResources
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for all root views in the application.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* A root view is an Android view that is directly attached to an activity. This
|
||||||
|
* view usually includes a toolbar and a progress bar. This abstract class hides
|
||||||
|
* some of the complexity of setting these things up, for every version of
|
||||||
|
* Android.
|
||||||
|
*/
|
||||||
|
abstract class BaseRootView(context: Context) : FrameLayout(context) {
|
||||||
|
var displayHomeAsUp = false
|
||||||
|
var screen: BaseScreen? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
open fun getToolbar(): Toolbar {
|
||||||
|
return findViewById(R.id.toolbar)
|
||||||
|
?: throw RuntimeException("Your BaseRootView should have a toolbar with id R.id.toolbar")
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun getToolbarColor(): Int = StyledResources(context).getColor(R.attr.colorPrimary)
|
||||||
|
|
||||||
|
protected open fun initToolbar() {
|
||||||
|
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
|
||||||
|
getToolbar().elevation = dpToPixels(context, 2f)
|
||||||
|
findViewById<View>(R.id.toolbarShadow)?.visibility = View.GONE
|
||||||
|
findViewById<View>(R.id.headerShadow)?.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onAttachedToScreen(screen: BaseScreen?) {
|
||||||
|
this.screen = screen
|
||||||
|
}
|
||||||
|
}
|
@ -1,318 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.androidbase.activities;
|
|
||||||
|
|
||||||
import android.content.*;
|
|
||||||
import android.graphics.*;
|
|
||||||
import android.graphics.drawable.*;
|
|
||||||
import android.net.*;
|
|
||||||
import android.os.*;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
import androidx.core.content.res.*;
|
|
||||||
import androidx.appcompat.app.*;
|
|
||||||
import androidx.appcompat.view.ActionMode;
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
import android.view.*;
|
|
||||||
import android.widget.*;
|
|
||||||
|
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
|
||||||
|
|
||||||
import org.isoron.androidbase.*;
|
|
||||||
import org.isoron.androidbase.utils.*;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static android.os.Build.VERSION_CODES.LOLLIPOP;
|
|
||||||
import static androidx.core.content.FileProvider.getUriForFile;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for all screens in the application.
|
|
||||||
* <p>
|
|
||||||
* Screens are responsible for deciding what root views and what menus should be
|
|
||||||
* attached to the main window. They are also responsible for showing other
|
|
||||||
* screens and for receiving their results.
|
|
||||||
*/
|
|
||||||
public class BaseScreen
|
|
||||||
{
|
|
||||||
protected BaseActivity activity;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private BaseRootView rootView;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private BaseSelectionMenu selectionMenu;
|
|
||||||
|
|
||||||
protected Snackbar snackbar;
|
|
||||||
|
|
||||||
public BaseScreen(@NonNull BaseActivity activity)
|
|
||||||
{
|
|
||||||
this.activity = activity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public static int getDefaultActionBarColor(Context context)
|
|
||||||
{
|
|
||||||
if (SDK_INT < LOLLIPOP)
|
|
||||||
{
|
|
||||||
return ResourcesCompat.getColor(context.getResources(),
|
|
||||||
R.color.grey_900, context.getTheme());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
StyledResources res = new StyledResources(context);
|
|
||||||
return res.getColor(R.attr.colorPrimary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public static void setupActionBarColor(@NonNull AppCompatActivity activity,
|
|
||||||
int color)
|
|
||||||
{
|
|
||||||
|
|
||||||
Toolbar toolbar = (Toolbar) activity.findViewById(R.id.toolbar);
|
|
||||||
if (toolbar == null) return;
|
|
||||||
|
|
||||||
activity.setSupportActionBar(toolbar);
|
|
||||||
|
|
||||||
ActionBar actionBar = activity.getSupportActionBar();
|
|
||||||
if (actionBar == null) return;
|
|
||||||
|
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
|
||||||
|
|
||||||
ColorDrawable drawable = new ColorDrawable(color);
|
|
||||||
actionBar.setBackgroundDrawable(drawable);
|
|
||||||
|
|
||||||
if (SDK_INT >= LOLLIPOP)
|
|
||||||
{
|
|
||||||
int darkerColor = ColorUtils.mixColors(color, Color.BLACK, 0.75f);
|
|
||||||
activity.getWindow().setStatusBarColor(darkerColor);
|
|
||||||
|
|
||||||
toolbar.setElevation(InterfaceUtils.dpToPixels(activity, 2));
|
|
||||||
|
|
||||||
View view = activity.findViewById(R.id.toolbarShadow);
|
|
||||||
if (view != null) view.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
view = activity.findViewById(R.id.headerShadow);
|
|
||||||
if (view != null) view.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies the screen that its contents should be updated.
|
|
||||||
*/
|
|
||||||
public void invalidate()
|
|
||||||
{
|
|
||||||
if (rootView == null) return;
|
|
||||||
rootView.invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void invalidateToolbar()
|
|
||||||
{
|
|
||||||
if (rootView == null) return;
|
|
||||||
|
|
||||||
activity.runOnUiThread(() ->
|
|
||||||
{
|
|
||||||
Toolbar toolbar = rootView.getToolbar();
|
|
||||||
activity.setSupportActionBar(toolbar);
|
|
||||||
ActionBar actionBar = activity.getSupportActionBar();
|
|
||||||
if (actionBar == null) return;
|
|
||||||
|
|
||||||
actionBar.setDisplayHomeAsUpEnabled(rootView.getDisplayHomeAsUp());
|
|
||||||
|
|
||||||
int color = rootView.getToolbarColor();
|
|
||||||
setActionBarColor(actionBar, color);
|
|
||||||
setStatusBarColor(color);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when another Activity has finished, and has returned some result.
|
|
||||||
*
|
|
||||||
* @param requestCode the request code originally supplied to {@link
|
|
||||||
* android.app.Activity#startActivityForResult(Intent,
|
|
||||||
* int, Bundle)}.
|
|
||||||
* @param resultCode the result code sent by the other activity.
|
|
||||||
* @param data an Intent containing extra data sent by the other
|
|
||||||
* activity.
|
|
||||||
* @see {@link android.app.Activity#onActivityResult(int, int, Intent)}
|
|
||||||
*/
|
|
||||||
public void onResult(int requestCode, int resultCode, Intent data)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called after activity has been recreated, and the dialogs should be
|
|
||||||
* reattached to their controllers.
|
|
||||||
*/
|
|
||||||
public void reattachDialogs()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the menu to be shown by this screen.
|
|
||||||
* <p>
|
|
||||||
* This menu will be visible if when there is no active selection operation.
|
|
||||||
* If the provided menu is null, then no menu will be shown.
|
|
||||||
*
|
|
||||||
* @param menu the menu to be shown.
|
|
||||||
*/
|
|
||||||
public void setMenu(@Nullable BaseMenu menu)
|
|
||||||
{
|
|
||||||
activity.setBaseMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the root view for this screen.
|
|
||||||
*
|
|
||||||
* @param rootView the root view for this screen.
|
|
||||||
*/
|
|
||||||
public void setRootView(@Nullable BaseRootView rootView)
|
|
||||||
{
|
|
||||||
this.rootView = rootView;
|
|
||||||
activity.setContentView(rootView);
|
|
||||||
if (rootView == null) return;
|
|
||||||
rootView.onAttachedToScreen(this);
|
|
||||||
invalidateToolbar();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the menu to be shown when a selection is active on the screen.
|
|
||||||
*
|
|
||||||
* @param menu the menu to be shown during a selection
|
|
||||||
*/
|
|
||||||
public void setSelectionMenu(@Nullable BaseSelectionMenu menu)
|
|
||||||
{
|
|
||||||
this.selectionMenu = menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows a message on the screen.
|
|
||||||
*
|
|
||||||
* @param stringId the string resource id for this message.
|
|
||||||
*/
|
|
||||||
public void showMessage(@StringRes Integer stringId)
|
|
||||||
{
|
|
||||||
if (stringId == null || rootView == null) return;
|
|
||||||
if (snackbar == null)
|
|
||||||
{
|
|
||||||
snackbar = Snackbar.make(rootView, stringId, Snackbar.LENGTH_SHORT);
|
|
||||||
int tvId = R.id.snackbar_text;
|
|
||||||
TextView tv = (TextView) snackbar.getView().findViewById(tvId);
|
|
||||||
tv.setTextColor(Color.WHITE);
|
|
||||||
}
|
|
||||||
else snackbar.setText(stringId);
|
|
||||||
snackbar.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showSendEmailScreen(@StringRes int toId,
|
|
||||||
@StringRes int subjectId,
|
|
||||||
String content)
|
|
||||||
{
|
|
||||||
String to = activity.getString(toId);
|
|
||||||
String subject = activity.getString(subjectId);
|
|
||||||
|
|
||||||
Intent intent = new Intent();
|
|
||||||
intent.setAction(Intent.ACTION_SEND);
|
|
||||||
intent.setType("message/rfc822");
|
|
||||||
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{ to });
|
|
||||||
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
|
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, content);
|
|
||||||
activity.startActivity(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showSendFileScreen(@NonNull String archiveFilename)
|
|
||||||
{
|
|
||||||
File file = new File(archiveFilename);
|
|
||||||
Uri fileUri = getUriForFile(activity, "org.isoron.uhabits", file);
|
|
||||||
|
|
||||||
Intent intent = new Intent();
|
|
||||||
intent.setAction(Intent.ACTION_SEND);
|
|
||||||
intent.setType("application/zip");
|
|
||||||
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
|
|
||||||
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
||||||
activity.startActivity(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instructs the screen to start a selection.
|
|
||||||
* <p>
|
|
||||||
* If a selection menu was provided, this menu will be shown instead of the
|
|
||||||
* regular one.
|
|
||||||
*/
|
|
||||||
public void startSelection()
|
|
||||||
{
|
|
||||||
activity.startSupportActionMode(new ActionModeWrapper());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setActionBarColor(@NonNull ActionBar actionBar, int color)
|
|
||||||
{
|
|
||||||
ColorDrawable drawable = new ColorDrawable(color);
|
|
||||||
actionBar.setBackgroundDrawable(drawable);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setStatusBarColor(int baseColor)
|
|
||||||
{
|
|
||||||
if (SDK_INT < LOLLIPOP) return;
|
|
||||||
|
|
||||||
int darkerColor = ColorUtils.mixColors(baseColor, Color.BLACK, 0.75f);
|
|
||||||
activity.getWindow().setStatusBarColor(darkerColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ActionModeWrapper implements ActionMode.Callback
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public boolean onActionItemClicked(@Nullable ActionMode mode,
|
|
||||||
@Nullable MenuItem item)
|
|
||||||
{
|
|
||||||
if (item == null || selectionMenu == null) return false;
|
|
||||||
return selectionMenu.onItemClicked(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateActionMode(@Nullable ActionMode mode,
|
|
||||||
@Nullable Menu menu)
|
|
||||||
{
|
|
||||||
if (selectionMenu == null) return false;
|
|
||||||
if (mode == null || menu == null) return false;
|
|
||||||
selectionMenu.onCreate(activity.getMenuInflater(), mode, menu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyActionMode(@Nullable ActionMode mode)
|
|
||||||
{
|
|
||||||
if (selectionMenu == null) return;
|
|
||||||
selectionMenu.onFinish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPrepareActionMode(@Nullable ActionMode mode,
|
|
||||||
@Nullable Menu menu)
|
|
||||||
{
|
|
||||||
if (selectionMenu == null || menu == null) return false;
|
|
||||||
return selectionMenu.onPrepare(menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,234 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.isoron.androidbase.activities
|
||||||
|
|
||||||
|
import android.content.*
|
||||||
|
import android.graphics.*
|
||||||
|
import android.graphics.drawable.*
|
||||||
|
import android.view.*
|
||||||
|
import android.widget.*
|
||||||
|
import androidx.annotation.*
|
||||||
|
import androidx.appcompat.app.*
|
||||||
|
import androidx.appcompat.view.ActionMode
|
||||||
|
import androidx.appcompat.widget.Toolbar
|
||||||
|
import androidx.core.content.*
|
||||||
|
import com.google.android.material.snackbar.*
|
||||||
|
import org.isoron.androidbase.*
|
||||||
|
import org.isoron.androidbase.utils.*
|
||||||
|
import org.isoron.androidbase.utils.ColorUtils.mixColors
|
||||||
|
import org.isoron.androidbase.utils.InterfaceUtils.dpToPixels
|
||||||
|
import java.io.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for all screens in the application.
|
||||||
|
*
|
||||||
|
* Screens are responsible for deciding what root views and what menus should be attached to the
|
||||||
|
* main window. They are also responsible for showing other screens and for receiving their results.
|
||||||
|
*/
|
||||||
|
open class BaseScreen(@JvmField protected var activity: BaseActivity) {
|
||||||
|
|
||||||
|
private var rootView: BaseRootView? = null
|
||||||
|
private var selectionMenu: BaseSelectionMenu? = null
|
||||||
|
private var snackbar: Snackbar? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the screen that its contents should be updated.
|
||||||
|
*/
|
||||||
|
fun invalidate() {
|
||||||
|
rootView?.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun invalidateToolbar() {
|
||||||
|
rootView?.let { root ->
|
||||||
|
activity.runOnUiThread {
|
||||||
|
val toolbar = root.getToolbar()
|
||||||
|
activity.setSupportActionBar(toolbar)
|
||||||
|
activity.supportActionBar?.let { actionBar ->
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(root.displayHomeAsUp)
|
||||||
|
val color = root.getToolbarColor()
|
||||||
|
setActionBarColor(actionBar, color)
|
||||||
|
setStatusBarColor(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when another Activity has finished, and has returned some result.
|
||||||
|
*
|
||||||
|
* @param requestCode the request code originally supplied to startActivityForResult.
|
||||||
|
* @param resultCode the result code sent by the other activity.
|
||||||
|
* @param data an Intent containing extra data sent by the other
|
||||||
|
* activity.
|
||||||
|
* @see {@link android.app.Activity.onActivityResult
|
||||||
|
*/
|
||||||
|
open fun onResult(requestCode: Int, resultCode: Int, data: Intent?) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after activity has been recreated, and the dialogs should be
|
||||||
|
* reattached to their controllers.
|
||||||
|
*/
|
||||||
|
open fun reattachDialogs() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the menu to be shown by this screen.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This menu will be visible if when there is no active selection operation.
|
||||||
|
* If the provided menu is null, then no menu will be shown.
|
||||||
|
*
|
||||||
|
* @param menu the menu to be shown.
|
||||||
|
*/
|
||||||
|
fun setMenu(menu: BaseMenu?) {
|
||||||
|
activity.setBaseMenu(menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the root view for this screen.
|
||||||
|
*
|
||||||
|
* @param rootView the root view for this screen.
|
||||||
|
*/
|
||||||
|
fun setRootView(rootView: BaseRootView?) {
|
||||||
|
this.rootView = rootView
|
||||||
|
activity.setContentView(rootView)
|
||||||
|
rootView?.let {
|
||||||
|
it.onAttachedToScreen(this)
|
||||||
|
invalidateToolbar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the menu to be shown when a selection is active on the screen.
|
||||||
|
*
|
||||||
|
* @param menu the menu to be shown during a selection
|
||||||
|
*/
|
||||||
|
fun setSelectionMenu(menu: BaseSelectionMenu?) {
|
||||||
|
selectionMenu = menu
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a message on the screen.
|
||||||
|
*
|
||||||
|
* @param stringId the string resource id for this message.
|
||||||
|
*/
|
||||||
|
fun showMessage(@StringRes stringId: Int?) {
|
||||||
|
val rootView = this.rootView
|
||||||
|
var snackbar = this.snackbar
|
||||||
|
if (stringId == null || rootView == null) return
|
||||||
|
if (snackbar == null) {
|
||||||
|
snackbar = Snackbar.make(rootView, stringId, Snackbar.LENGTH_SHORT)
|
||||||
|
val tvId = R.id.snackbar_text
|
||||||
|
val tv = snackbar.view.findViewById<TextView>(tvId)
|
||||||
|
tv.setTextColor(Color.WHITE)
|
||||||
|
this.snackbar = snackbar
|
||||||
|
}
|
||||||
|
snackbar.setText(stringId)
|
||||||
|
snackbar.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showSendEmailScreen(@StringRes toId: Int, @StringRes subjectId: Int, content: String?) {
|
||||||
|
val to = activity.getString(toId)
|
||||||
|
val subject = activity.getString(subjectId)
|
||||||
|
activity.startActivity(Intent().apply {
|
||||||
|
action = Intent.ACTION_SEND
|
||||||
|
type = "message/rfc822"
|
||||||
|
putExtra(Intent.EXTRA_EMAIL, arrayOf(to))
|
||||||
|
putExtra(Intent.EXTRA_SUBJECT, subject)
|
||||||
|
putExtra(Intent.EXTRA_TEXT, content)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showSendFileScreen(archiveFilename: String) {
|
||||||
|
val file = File(archiveFilename)
|
||||||
|
val fileUri = FileProvider.getUriForFile(activity, "org.isoron.uhabits", file)
|
||||||
|
activity.startActivity(Intent().apply {
|
||||||
|
action = Intent.ACTION_SEND
|
||||||
|
type = "application/zip"
|
||||||
|
putExtra(Intent.EXTRA_STREAM, fileUri)
|
||||||
|
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instructs the screen to start a selection.
|
||||||
|
*
|
||||||
|
* If a selection menu was provided, this menu will be shown instead of the regular one.
|
||||||
|
*/
|
||||||
|
fun startSelection() {
|
||||||
|
activity.startSupportActionMode(ActionModeWrapper())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setActionBarColor(actionBar: ActionBar, color: Int) {
|
||||||
|
val drawable = ColorDrawable(color)
|
||||||
|
actionBar.setBackgroundDrawable(drawable)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setStatusBarColor(baseColor: Int) {
|
||||||
|
val darkerColor = mixColors(baseColor, Color.BLACK, 0.75f)
|
||||||
|
activity.window.statusBarColor = darkerColor
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class ActionModeWrapper : ActionMode.Callback {
|
||||||
|
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
|
||||||
|
val selectionMenu = selectionMenu
|
||||||
|
if (item == null || selectionMenu == null) return false
|
||||||
|
return selectionMenu.onItemClicked(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||||
|
if (mode == null || menu == null) return false
|
||||||
|
val selectionMenu = selectionMenu ?: return false
|
||||||
|
selectionMenu.onCreate(activity.menuInflater, mode, menu)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyActionMode(mode: ActionMode?) {
|
||||||
|
selectionMenu?.onFinish()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||||
|
val selectionMenu = selectionMenu
|
||||||
|
if (selectionMenu == null || menu == null) return false
|
||||||
|
return selectionMenu.onPrepare(menu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
@Deprecated("")
|
||||||
|
fun getDefaultActionBarColor(context: Context) =
|
||||||
|
StyledResources(context).getColor(R.attr.colorPrimary)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@Deprecated("")
|
||||||
|
fun setupActionBarColor(activity: AppCompatActivity, color: Int) {
|
||||||
|
val toolbar = activity.findViewById<Toolbar>(R.id.toolbar) ?: return
|
||||||
|
activity.setSupportActionBar(toolbar)
|
||||||
|
val supportActionBar = activity.supportActionBar ?: return
|
||||||
|
supportActionBar.setDisplayHomeAsUpEnabled(true)
|
||||||
|
val drawable = ColorDrawable(color)
|
||||||
|
supportActionBar.setBackgroundDrawable(drawable)
|
||||||
|
val darkerColor = mixColors(color, Color.BLACK, 0.75f)
|
||||||
|
activity.window.statusBarColor = darkerColor
|
||||||
|
toolbar.elevation = dpToPixels(activity, 2f)
|
||||||
|
activity.findViewById<View>(R.id.toolbarShadow)?.visibility = View.GONE
|
||||||
|
activity.findViewById<View>(R.id.headerShadow)?.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 605 B |
Before Width: | Height: | Size: 541 B |
Before Width: | Height: | Size: 586 B |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 585 B |
Before Width: | Height: | Size: 543 B |
Before Width: | Height: | Size: 583 B |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.4 KiB |