mirror of
https://codeberg.org/tom79/Fedilab.git
synced 2024-12-23 01:00:04 +02:00
Charts
This commit is contained in:
parent
6019f5129b
commit
c04a51f3b7
8 changed files with 332 additions and 1 deletions
|
@ -265,7 +265,11 @@
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
android:label="@string/action_about"
|
android:label="@string/action_about"
|
||||||
android:theme="@style/AppThemeBar" />
|
android:theme="@style/AppThemeBar" />
|
||||||
|
<activity
|
||||||
|
android:name=".mastodon.activities.CheckHomeCacheActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:label="@string/home_cache"
|
||||||
|
android:theme="@style/AppThemeBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".mastodon.activities.admin.AdminDomainBlockActivity"
|
android:name=".mastodon.activities.admin.AdminDomainBlockActivity"
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
package app.fedilab.android.mastodon.activities;
|
||||||
|
/* Copyright 2023 Thomas Schneider
|
||||||
|
*
|
||||||
|
* This file is a part of Fedilab
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*
|
||||||
|
* Fedilab 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 Fedilab; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewTreeObserver;
|
||||||
|
|
||||||
|
import androidx.appcompat.widget.LinearLayoutCompat;
|
||||||
|
|
||||||
|
import com.github.mikephil.charting.components.Description;
|
||||||
|
import com.github.mikephil.charting.components.XAxis;
|
||||||
|
import com.github.mikephil.charting.data.Entry;
|
||||||
|
import com.github.mikephil.charting.data.LineData;
|
||||||
|
import com.github.mikephil.charting.data.LineDataSet;
|
||||||
|
import com.github.mikephil.charting.formatter.IndexAxisValueFormatter;
|
||||||
|
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
|
||||||
|
import com.github.mikephil.charting.renderer.XAxisRenderer;
|
||||||
|
import com.github.mikephil.charting.utils.MPPointF;
|
||||||
|
import com.github.mikephil.charting.utils.Transformer;
|
||||||
|
import com.github.mikephil.charting.utils.Utils;
|
||||||
|
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.activities.MainActivity;
|
||||||
|
import app.fedilab.android.databinding.ActivityCheckHomeCachetBinding;
|
||||||
|
import app.fedilab.android.mastodon.client.entities.api.Status;
|
||||||
|
import app.fedilab.android.mastodon.client.entities.app.StatusCache;
|
||||||
|
import app.fedilab.android.mastodon.exception.DBException;
|
||||||
|
import app.fedilab.android.mastodon.helper.Helper;
|
||||||
|
import app.fedilab.android.mastodon.helper.ThemeHelper;
|
||||||
|
import es.dmoral.toasty.Toasty;
|
||||||
|
|
||||||
|
|
||||||
|
public class CheckHomeCacheActivity extends BaseBarActivity {
|
||||||
|
|
||||||
|
|
||||||
|
private ActivityCheckHomeCachetBinding binding;
|
||||||
|
private List<Status> statuses;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
binding = ActivityCheckHomeCachetBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
|
||||||
|
if (getSupportActionBar() != null) {
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
}
|
||||||
|
if (MainActivity.currentAccount == null || MainActivity.currentAccount.mastodon_account == null) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
binding.chart.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||||
|
@Override
|
||||||
|
public void onGlobalLayout() {
|
||||||
|
binding.chart.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||||
|
int height = (binding.chart.getWidth());
|
||||||
|
LinearLayoutCompat.LayoutParams params = new LinearLayoutCompat.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height);
|
||||||
|
binding.chart.setLayoutParams(params);
|
||||||
|
binding.chart.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
statuses = new StatusCache(this).getHome(MainActivity.currentAccount);
|
||||||
|
if (statuses == null || statuses.size() < 2) {
|
||||||
|
runOnUiThread(() -> binding.noAction.setVisibility(View.VISIBLE));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ArrayList<String> xVals = new ArrayList<>();
|
||||||
|
String xDate;
|
||||||
|
int inc = 0;
|
||||||
|
//We loop through cache
|
||||||
|
List<Entry> statusEntry = new ArrayList<>();
|
||||||
|
int index = 0;
|
||||||
|
for (Status status : statuses) {
|
||||||
|
//We aggregate message in same hour range
|
||||||
|
boolean sameHourRange = true;
|
||||||
|
int count = 0;
|
||||||
|
while (inc < statuses.size() && sameHourRange) {
|
||||||
|
Calendar currentStatusDate = Calendar.getInstance();
|
||||||
|
currentStatusDate.setTime(statuses.get(inc).created_at);
|
||||||
|
String xDateH = new SimpleDateFormat("hh", Locale.getDefault()).format(statuses.get(inc).created_at);
|
||||||
|
SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault());
|
||||||
|
String xDateD = df.format(statuses.get(inc).created_at);
|
||||||
|
xDate = xDateD + " " + String.format(Locale.getDefault(), "%sh", xDateH);
|
||||||
|
if (inc + 1 < statuses.size()) {
|
||||||
|
Calendar nextStatusDate = Calendar.getInstance();
|
||||||
|
nextStatusDate.setTime(statuses.get(inc + 1).created_at);
|
||||||
|
if (currentStatusDate.get(Calendar.HOUR) != nextStatusDate.get(Calendar.HOUR)) {
|
||||||
|
sameHourRange = false;
|
||||||
|
statusEntry.add(new Entry(index, count));
|
||||||
|
index++;
|
||||||
|
xVals.add(xDate);
|
||||||
|
} else {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
} else { //Last item
|
||||||
|
count++;
|
||||||
|
statusEntry.add(new Entry(index, count));
|
||||||
|
xVals.add(xDate);
|
||||||
|
}
|
||||||
|
inc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<ILineDataSet> dataSets = new ArrayList<>();
|
||||||
|
|
||||||
|
LineDataSet dataStatus = new LineDataSet(statusEntry, getString(R.string.cached_messages));
|
||||||
|
dataStatus.setDrawValues(false);
|
||||||
|
dataStatus.setDrawFilled(true);
|
||||||
|
dataStatus.setDrawCircles(false);
|
||||||
|
dataStatus.setDrawCircleHole(false);
|
||||||
|
dataStatus.setMode(LineDataSet.Mode.CUBIC_BEZIER);
|
||||||
|
dataSets.add(dataStatus);
|
||||||
|
|
||||||
|
LineData data = new LineData(dataSets);
|
||||||
|
binding.chart.setData(data);
|
||||||
|
IndexAxisValueFormatter formatter = new IndexAxisValueFormatter() {
|
||||||
|
@Override
|
||||||
|
public String getFormattedValue(float value) {
|
||||||
|
if (value < xVals.size()) {
|
||||||
|
return xVals.get((int) value);
|
||||||
|
} else
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
binding.chart.setExtraBottomOffset(80);
|
||||||
|
// binding.chart.getXAxis().setGranularity(1f);
|
||||||
|
binding.chart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
|
||||||
|
binding.chart.getXAxis().setLabelRotationAngle(-45f);
|
||||||
|
binding.chart.getXAxis().setValueFormatter(formatter);
|
||||||
|
binding.chart.getXAxis().setEnabled(true);
|
||||||
|
binding.chart.getXAxis().setTextColor(ThemeHelper.getAttColor(CheckHomeCacheActivity.this, R.attr.colorOnBackground));
|
||||||
|
binding.chart.getAxisLeft().setTextColor(ThemeHelper.getAttColor(CheckHomeCacheActivity.this, R.attr.colorOnBackground));
|
||||||
|
binding.chart.getAxisRight().setTextColor(ThemeHelper.getAttColor(CheckHomeCacheActivity.this, R.attr.colorOnBackground));
|
||||||
|
binding.chart.getLegend().setTextColor(ThemeHelper.getAttColor(CheckHomeCacheActivity.this, R.attr.colorOnBackground));
|
||||||
|
binding.chart.getAxisLeft().setAxisMinimum(0f);
|
||||||
|
binding.chart.getAxisRight().setAxisMinimum(0f);
|
||||||
|
binding.chart.getXAxis().setLabelCount(10, true);
|
||||||
|
binding.chart.getLegend().setEnabled(false);
|
||||||
|
|
||||||
|
Description description = binding.chart.getDescription();
|
||||||
|
description.setEnabled(false);
|
||||||
|
|
||||||
|
binding.chart.invalidate();
|
||||||
|
|
||||||
|
} catch (DBException | NegativeArraySizeException e) {
|
||||||
|
binding.noAction.setVisibility(View.VISIBLE);
|
||||||
|
Toasty.error(this, getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LineChartXAxisValueFormatter extends IndexAxisValueFormatter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFormattedValue(float value) {
|
||||||
|
|
||||||
|
long emissionsMilliSince1970Time = ((long) value);
|
||||||
|
Log.v(Helper.TAG, "value: " + value);
|
||||||
|
Date timeMilliseconds = new Date(emissionsMilliSince1970Time);
|
||||||
|
DateFormat dateTimeFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault());
|
||||||
|
return dateTimeFormat.format(timeMilliseconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomXAxisRenderer extends XAxisRenderer {
|
||||||
|
public CustomXAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans) {
|
||||||
|
super(viewPortHandler, xAxis, trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawLabel(Canvas c, String formattedLabel, float x, float y, MPPointF anchor, float angleDegrees) {
|
||||||
|
String[] line = formattedLabel.split("\n");
|
||||||
|
Utils.drawXAxisValue(c, line[0], x, y, mAxisLabelPaint, anchor, angleDegrees);
|
||||||
|
Utils.drawXAxisValue(c, line[1], x + mAxisLabelPaint.getTextSize(), y + mAxisLabelPaint.getTextSize(), mAxisLabelPaint, anchor, angleDegrees);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -229,6 +229,28 @@ public class StatusCache {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get all cache messages for home
|
||||||
|
*
|
||||||
|
* @param baseAccount Status {@link BaseAccount}
|
||||||
|
* @return List<Status>
|
||||||
|
* @throws DBException Exception
|
||||||
|
*/
|
||||||
|
public List<Status> getHome(BaseAccount baseAccount) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
String selection = Sqlite.COL_INSTANCE + "='" + baseAccount.instance + "' AND " + Sqlite.COL_USER_ID + "= '" + baseAccount.user_id + "' AND " + Sqlite.COL_SLUG + "= '" + Timeline.TimeLineEnum.HOME.getValue() + "' ";
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_STATUS_CACHE, null, selection, null, Sqlite.COL_STATUS_ID, null, Sqlite.COL_STATUS_ID + " ASC", null);
|
||||||
|
return cursorToListOfStatuses(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* count messages for other timelines
|
* count messages for other timelines
|
||||||
*
|
*
|
||||||
|
|
|
@ -15,10 +15,12 @@ package app.fedilab.android.mastodon.ui.fragment.settings;
|
||||||
* see <http://www.gnu.org/licenses>. */
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import androidx.preference.ListPreference;
|
import androidx.preference.ListPreference;
|
||||||
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
import androidx.preference.PreferenceScreen;
|
import androidx.preference.PreferenceScreen;
|
||||||
|
@ -29,6 +31,7 @@ import androidx.work.WorkManager;
|
||||||
import app.fedilab.android.BaseMainActivity;
|
import app.fedilab.android.BaseMainActivity;
|
||||||
import app.fedilab.android.R;
|
import app.fedilab.android.R;
|
||||||
import app.fedilab.android.activities.MainActivity;
|
import app.fedilab.android.activities.MainActivity;
|
||||||
|
import app.fedilab.android.mastodon.activities.CheckHomeCacheActivity;
|
||||||
import app.fedilab.android.mastodon.helper.Helper;
|
import app.fedilab.android.mastodon.helper.Helper;
|
||||||
import app.fedilab.android.mastodon.jobs.FetchHomeWorker;
|
import app.fedilab.android.mastodon.jobs.FetchHomeWorker;
|
||||||
import es.dmoral.toasty.Toasty;
|
import es.dmoral.toasty.Toasty;
|
||||||
|
@ -65,6 +68,15 @@ public class FragmentHomeCacheSettings extends PreferenceFragmentCompat implemen
|
||||||
String timeRefresh = sharedpreferences.getString(getString(R.string.SET_FETCH_HOME_DELAY_VALUE) + MainActivity.currentUserID + MainActivity.currentInstance, "60");
|
String timeRefresh = sharedpreferences.getString(getString(R.string.SET_FETCH_HOME_DELAY_VALUE) + MainActivity.currentUserID + MainActivity.currentInstance, "60");
|
||||||
SET_FETCH_HOME_DELAY_VALUE.setValue(timeRefresh);
|
SET_FETCH_HOME_DELAY_VALUE.setValue(timeRefresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Preference pref_category_show_data = findPreference(getString(R.string.pref_category_show_data));
|
||||||
|
if (pref_category_show_data != null) {
|
||||||
|
pref_category_show_data.setOnPreferenceClickListener(preference -> {
|
||||||
|
startActivity(new Intent(requireActivity(), CheckHomeCacheActivity.class));
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:autoMirrored="true"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3.5,18.49l6,-6.01 4,4L22,6.92l-1.41,-1.41 -7.09,7.97 -4,-4L2,16.99z" />
|
||||||
|
</vector>
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
Copyright 2023 Thomas Schneider
|
||||||
|
|
||||||
|
This file is a part of Fedilab
|
||||||
|
|
||||||
|
This program 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.
|
||||||
|
|
||||||
|
Fedilab 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 Fedilab; if not,
|
||||||
|
see <http://www.gnu.org/licenses>.
|
||||||
|
-->
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:id="@+id/container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.github.mikephil.charting.charts.LineChart
|
||||||
|
android:id="@+id/chart"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/no_action"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/no_action_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:text="@string/no_cached_messages"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
</RelativeLayout>
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
|
||||||
|
</ScrollView>
|
|
@ -1645,6 +1645,8 @@
|
||||||
<item>u+1f6a9</item>
|
<item>u+1f6a9</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string name="pref_category_key_account" translatable="false">pref_category_account</string>
|
<string name="pref_category_key_account" translatable="false">pref_category_account</string>
|
||||||
|
<string name="pref_category_show_data" translatable="false">pref_category_show_data</string>
|
||||||
|
|
||||||
<string name="pref_category_key_timeline" translatable="false">pref_category_timeline</string>
|
<string name="pref_category_key_timeline" translatable="false">pref_category_timeline</string>
|
||||||
<string name="pref_category_key_notifications" translatable="false">pref_category_notifications</string>
|
<string name="pref_category_key_notifications" translatable="false">pref_category_notifications</string>
|
||||||
<string name="pref_category_key_interface" translatable="false">pref_category_interface</string>
|
<string name="pref_category_key_interface" translatable="false">pref_category_interface</string>
|
||||||
|
@ -1927,4 +1929,6 @@
|
||||||
<string name="send_anyway">Send anyway</string>
|
<string name="send_anyway">Send anyway</string>
|
||||||
<string name="set_remove_battery">Ignore battery optimizations</string>
|
<string name="set_remove_battery">Ignore battery optimizations</string>
|
||||||
<string name="set_autoplay_gif">Autoplay animated media</string>
|
<string name="set_autoplay_gif">Autoplay animated media</string>
|
||||||
|
<string name="no_cached_messages">No Home cached messages!</string>
|
||||||
|
<string name="check_home_cache">Check Home cache</string>
|
||||||
</resources>
|
</resources>
|
|
@ -23,4 +23,11 @@
|
||||||
app:title="@string/type_of_home_delay_title"
|
app:title="@string/type_of_home_delay_title"
|
||||||
app:useSimpleSummaryProvider="true" />
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:title="@string/check_home_cache"
|
||||||
|
app:icon="@drawable/baseline_show_chart_24"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:key="@string/pref_category_show_data" />
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
Loading…
Reference in a new issue