That fuck shit the fascists are using
1/*
2 * Copyright (C) 2015 Open Whisper Systems
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17package org.tm.archive.mediaoverview;
18
19import android.content.Context;
20import android.content.Intent;
21import android.os.Bundle;
22import android.view.MenuItem;
23import android.view.View;
24import android.view.ViewGroup;
25import android.widget.TextView;
26
27import androidx.annotation.NonNull;
28import androidx.annotation.StringRes;
29import androidx.appcompat.widget.Toolbar;
30import androidx.fragment.app.Fragment;
31import androidx.fragment.app.FragmentManager;
32import androidx.fragment.app.FragmentStatePagerAdapter;
33import androidx.viewpager.widget.ViewPager;
34
35import com.google.android.material.dialog.MaterialAlertDialogBuilder;
36import com.google.android.material.tabs.TabLayout;
37import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback;
38
39import org.signal.libsignal.protocol.util.Pair;
40import org.tm.archive.PassphraseRequiredActivity;
41import org.tm.archive.R;
42import org.tm.archive.components.AnimatingToggle;
43import org.tm.archive.components.BoldSelectionTabItem;
44import org.tm.archive.components.ControllableTabLayout;
45import org.tm.archive.database.MediaTable;
46import org.tm.archive.database.MediaTable.Sorting;
47import org.tm.archive.database.SignalDatabase;
48import org.tm.archive.database.loaders.MediaLoader;
49import org.tm.archive.util.DynamicNoActionBarTheme;
50import org.tm.archive.util.DynamicTheme;
51import org.signal.core.util.concurrent.SimpleTask;
52
53import java.util.ArrayList;
54import java.util.List;
55
56/**
57 * Activity for displaying media attachments in-app
58 */
59public final class MediaOverviewActivity extends PassphraseRequiredActivity {
60
61 private static final String THREAD_ID_EXTRA = "thread_id";
62
63 private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
64
65 private Toolbar toolbar;
66 private ControllableTabLayout tabLayout;
67 private ViewPager viewPager;
68 private TextView sortOrder;
69 private View sortOrderArrow;
70 private Sorting currentSorting;
71 private Boolean currentDetailLayout;
72 private MediaOverviewViewModel model;
73 private AnimatingToggle displayToggle;
74 private View viewGrid;
75 private View viewDetail;
76 private long threadId;
77
78 public static Intent forThread(@NonNull Context context, long threadId) {
79 Intent intent = new Intent(context, MediaOverviewActivity.class);
80 intent.putExtra(MediaOverviewActivity.THREAD_ID_EXTRA, threadId);
81 return intent;
82 }
83
84 public static Intent forAll(@NonNull Context context) {
85 return forThread(context, MediaTable.ALL_THREADS);
86 }
87
88 @Override
89 protected void onPreCreate() {
90 dynamicTheme.onCreate(this);
91 }
92
93 @Override
94 protected void onCreate(Bundle bundle, boolean ready) {
95 setExitSharedElementCallback(new MaterialContainerTransformSharedElementCallback());
96 setContentView(R.layout.media_overview_activity);
97
98 initializeResources();
99 initializeToolbar();
100
101 boolean allThreads = threadId == MediaTable.ALL_THREADS;
102
103 BoldSelectionTabItem.registerListeners(tabLayout);
104 fillTabLayoutIfFits(tabLayout);
105 tabLayout.setupWithViewPager(viewPager);
106 viewPager.setAdapter(new MediaOverviewPagerAdapter(getSupportFragmentManager()));
107
108 model = MediaOverviewViewModel.getMediaOverviewViewModel(this);
109 model.setSortOrder(allThreads ? Sorting.Largest : Sorting.Newest);
110 model.setDetailLayout(allThreads);
111 model.getSortOrder().observe(this, this::setSorting);
112 model.getDetailLayout().observe(this, this::setDetailLayout);
113
114 sortOrder.setOnClickListener(this::showSortOrderDialog);
115 sortOrderArrow.setOnClickListener(this::showSortOrderDialog);
116
117 displayToggle.setOnClickListener(v -> setDetailLayout(!currentDetailLayout));
118
119 viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
120 @Override
121 public void onPageSelected(int position) {
122 boolean gridToggleEnabled = allowGridSelectionOnPage(position);
123 displayToggle.animate()
124 .alpha(gridToggleEnabled ? 1 : 0)
125 .start();
126 displayToggle.setEnabled(gridToggleEnabled);
127 }
128 });
129
130 viewPager.setCurrentItem(allThreads ? 3 : 0);
131 }
132
133 private static boolean allowGridSelectionOnPage(int page) {
134 return page == 0;
135 }
136
137 private void setSorting(@NonNull Sorting sorting) {
138 if (currentSorting == sorting) return;
139
140 sortOrder.setText(sortingToString(sorting));
141 currentSorting = sorting;
142 model.setSortOrder(sorting);
143 }
144
145 private static @StringRes int sortingToString(@NonNull Sorting sorting) {
146 switch (sorting) {
147 case Oldest : return R.string.MediaOverviewActivity_Oldest;
148 case Newest : return R.string.MediaOverviewActivity_Newest;
149 case Largest : return R.string.MediaOverviewActivity_Storage_used;
150 default : throw new AssertionError();
151 }
152 }
153
154 private void setDetailLayout(@NonNull Boolean detailLayout) {
155 if (currentDetailLayout == detailLayout) return;
156
157 currentDetailLayout = detailLayout;
158 model.setDetailLayout(detailLayout);
159 displayToggle.display(detailLayout ? viewGrid : viewDetail);
160 }
161
162 @Override
163 public void onResume() {
164 super.onResume();
165 dynamicTheme.onResume(this);
166 }
167
168 @Override
169 public boolean onOptionsItemSelected(MenuItem item) {
170 super.onOptionsItemSelected(item);
171
172 if (item.getItemId() == android.R.id.home) {
173 finish();
174 return true;
175 }
176
177 return false;
178 }
179
180 private void initializeResources() {
181 Intent intent = getIntent();
182 long threadId = intent.getLongExtra(THREAD_ID_EXTRA, Long.MIN_VALUE);
183
184 if (threadId == Long.MIN_VALUE) throw new AssertionError();
185
186 this.viewPager = findViewById(R.id.pager);
187 this.toolbar = findViewById(R.id.toolbar);
188 this.tabLayout = findViewById(R.id.tab_layout);
189 this.sortOrder = findViewById(R.id.sort_order);
190 this.sortOrderArrow = findViewById(R.id.sort_order_arrow);
191 this.displayToggle = findViewById(R.id.grid_list_toggle);
192 this.viewDetail = findViewById(R.id.view_detail);
193 this.viewGrid = findViewById(R.id.view_grid);
194 this.threadId = threadId;
195 }
196
197 private void initializeToolbar() {
198 setSupportActionBar(toolbar);
199 getSupportActionBar().setDisplayHomeAsUpEnabled(true);
200
201 if (threadId == MediaTable.ALL_THREADS) {
202 getSupportActionBar().setTitle(R.string.MediaOverviewActivity_All_storage_use);
203 } else {
204 SimpleTask.run(() -> SignalDatabase.threads().getRecipientForThreadId(threadId),
205 (recipient) -> {
206 if (recipient != null) {
207 getSupportActionBar().setTitle(recipient.getDisplayName(this));
208 recipient.live().observe(this, r -> getSupportActionBar().setTitle(r.getDisplayName(this)));
209 }
210 }
211 );
212 }
213 }
214
215 public void onEnterMultiSelect() {
216 tabLayout.setEnabled(false);
217 viewPager.setEnabled(false);
218 toolbar.setVisibility(View.INVISIBLE);
219 }
220
221 public void onExitMultiSelect() {
222 tabLayout.setEnabled(true);
223 viewPager.setEnabled(true);
224 toolbar.setVisibility(View.VISIBLE);
225 }
226
227 private void showSortOrderDialog(View v) {
228 new MaterialAlertDialogBuilder(MediaOverviewActivity.this)
229 .setTitle(R.string.MediaOverviewActivity_Sort_by)
230 .setSingleChoiceItems(R.array.MediaOverviewActivity_Sort_by,
231 currentSorting.ordinal(),
232 (dialog, item) -> {
233 setSorting(Sorting.values()[item]);
234 dialog.dismiss();
235 })
236 .create()
237 .show();
238 }
239
240 private static void fillTabLayoutIfFits(@NonNull TabLayout tabLayout) {
241 tabLayout.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
242 int totalWidth = 0;
243 int maxWidth = 0;
244 ViewGroup tabs = (ViewGroup) tabLayout.getChildAt(0);
245
246 for (int i = 0; i < tabLayout.getTabCount(); i++) {
247 int tabWidth = tabs.getChildAt(i).getWidth();
248 totalWidth += tabWidth;
249 maxWidth = Math.max(maxWidth, tabWidth);
250 }
251
252 int viewWidth = right - left;
253 if (totalWidth < viewWidth) {
254 tabLayout.setTabMode(TabLayout.MODE_FIXED);
255 }
256 });
257 }
258
259 private class MediaOverviewPagerAdapter extends FragmentStatePagerAdapter {
260
261 private final List<Pair<MediaLoader.MediaType, CharSequence>> pages;
262
263 MediaOverviewPagerAdapter(FragmentManager fragmentManager) {
264 super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
265
266 pages = new ArrayList<>(4);
267 pages.add(new Pair<>(MediaLoader.MediaType.GALLERY, getString(R.string.MediaOverviewActivity_Media)));
268 pages.add(new Pair<>(MediaLoader.MediaType.DOCUMENT, getString(R.string.MediaOverviewActivity_Files)));
269 pages.add(new Pair<>(MediaLoader.MediaType.AUDIO, getString(R.string.MediaOverviewActivity_Audio)));
270 pages.add(new Pair<>(MediaLoader.MediaType.ALL, getString(R.string.MediaOverviewActivity_All)));
271 }
272
273 @Override
274 public @NonNull Fragment getItem(int position) {
275 MediaOverviewPageFragment.GridMode gridMode = allowGridSelectionOnPage(position)
276 ? MediaOverviewPageFragment.GridMode.FOLLOW_MODEL
277 : MediaOverviewPageFragment.GridMode.FIXED_DETAIL;
278
279 return MediaOverviewPageFragment.newInstance(threadId, pages.get(position).first(), gridMode);
280 }
281
282 @Override
283 public int getCount() {
284 return pages.size();
285 }
286
287 @Override
288 public CharSequence getPageTitle(int position) {
289 return pages.get(position).second();
290 }
291 }
292}