/* * Copyright 2019 Uriah Shaul Mandel * * 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 com.bald.uriah.baldphone.activities; import android.Manifest; import android.annotation.SuppressLint; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Point; import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.os.AsyncTask; import android.os.BatteryManager; import android.os.Bundle; import android.os.Handler; import android.speech.RecognizerIntent; import android.util.Log; import android.util.TypedValue; import android.view.Display; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.PopupWindow; import android.widget.Toast; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import com.bald.uriah.baldphone.BuildConfig; import com.bald.uriah.baldphone.R; import com.bald.uriah.baldphone.adapters.BaldPagerAdapter; import com.bald.uriah.baldphone.databases.apps.AppsDatabaseHelper; import com.bald.uriah.baldphone.services.NotificationListenerService; import com.bald.uriah.baldphone.utils.BDB; import com.bald.uriah.baldphone.utils.BDialog; import com.bald.uriah.baldphone.utils.BPrefs; import com.bald.uriah.baldphone.utils.BaldHomeWatcher; import com.bald.uriah.baldphone.utils.BaldPrefsUtils; import com.bald.uriah.baldphone.utils.BaldToast; import com.bald.uriah.baldphone.utils.D; import com.bald.uriah.baldphone.utils.DropDownRecyclerViewAdapter; import com.bald.uriah.baldphone.utils.PageTransformers; import com.bald.uriah.baldphone.utils.S; import com.bald.uriah.baldphone.utils.UpdatingUtil; import com.bald.uriah.baldphone.views.BaldImageButton; import com.bald.uriah.baldphone.views.BatteryView; import com.bald.uriah.baldphone.views.ViewPagerHolder; import com.bald.uriah.baldphone.views.home.HomePage1; import com.bald.uriah.baldphone.views.home.NotesView; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import github.nisrulz.lantern.Lantern; import static com.bald.uriah.baldphone.services.NotificationListenerService.ACTION_REGISTER_ACTIVITY; import static com.bald.uriah.baldphone.services.NotificationListenerService.ACTIVITY_NONE; import static com.bald.uriah.baldphone.services.NotificationListenerService.KEY_EXTRA_ACTIVITY; import static com.bald.uriah.baldphone.services.NotificationListenerService.NOTIFICATIONS_ALOT; import static com.bald.uriah.baldphone.services.NotificationListenerService.NOTIFICATIONS_HOME_SCREEN; import static com.bald.uriah.baldphone.services.NotificationListenerService.NOTIFICATIONS_NONE; import static com.bald.uriah.baldphone.services.NotificationListenerService.NOTIFICATIONS_SOME; public class HomeScreenActivity extends BaldActivity { private static final String TAG = HomeScreenActivity.class.getSimpleName(); private static final int[] SOUND_DRAWABLES = {R.drawable.mute_on_background, R.drawable.vibration_on_background, R.drawable.sound_on_background}, SOUND_TEXTS = {R.string.mute, R.string.vibrate, R.string.sound}; private static final IntentFilter BATTERY_FILTER = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); private static final int SPEECH_REQUEST_CODE = 7; private static int onStartCounter = 0; private static boolean flashState; @NonNull public final NotesView.RecognizerManager recognizerManager = new NotesView.RecognizerManager(); public boolean finishedUpdatingApps, launchAppsActivity; public BaldPagerAdapter baldPagerAdapter; private Point screenSize; private Lantern lantern; private SharedPreferences sharedPreferences; private BaldPrefsUtils baldPrefsUtils; private ViewPagerHolder viewPagerHolder; private BatteryView batteryView; private boolean lowBatteryAlert; /** * Listens to changes in battery {@value Intent#ACTION_BATTERY_CHANGED} */ private final BroadcastReceiver batteryReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (batteryView != null) { final int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); final int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); final int batteryPct = Math.round(level / (float) scale * 100); final int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); final boolean charged = chargePlug == BatteryManager.BATTERY_PLUGGED_AC || chargePlug == BatteryManager.BATTERY_PLUGGED_WIRELESS || chargePlug == BatteryManager.BATTERY_PLUGGED_USB; batteryView.setLevel(batteryPct, charged); if (lowBatteryAlert) getWindow().setStatusBarColor((batteryPct < D.LOW_BATTERY_LEVEL && !charged) ? ContextCompat.getColor(context, R.color.battery_low) : D.DEFAULT_STATUS_BAR_COLOR); } } }; private int notificationCount = 0; @ColorInt private int decorationColorOnBackground; private BaldImageButton notificationsButton, sosButton, soundButton, flashButton; private AudioManager audioManager; private BaldHomeWatcher baldHomeWatcher; private boolean flashInited; private Handler handler = new Handler(); /** * "Shakes" the notifications icon when it has more than {@value NotificationListenerService#NOTIFICATIONS_ALOT} */ private final Runnable shakeIt = new Runnable() { @Override public void run() { final Drawable d = notificationsButton.getDrawable(); if (d instanceof AnimatedVectorDrawable) { final AnimatedVectorDrawable animatedVectorDrawable = (AnimatedVectorDrawable) d; animatedVectorDrawable.start(); final int minusSeconds = Math.min((int) (Math.max((notificationCount - NOTIFICATIONS_ALOT) * 0.5f, 0)), 7); handler.postDelayed(this, (10 - minusSeconds) * D.SECOND); } } }; /** * Listens to broadcasts from {@link NotificationListenerService} * This listener only gets the number of notifications and updates {@link HomeScreenActivity#notificationsButton} * The red dot is being updated via {@link HomePage1#notificationReceiver} */ public final BroadcastReceiver notificationReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { notificationCount = intent.getIntExtra("amount", -1); if (notificationCount >= NOTIFICATIONS_ALOT) { final Drawable drawable = getDrawable(R.drawable.notification_alot_on_background); final float opacity = Math.min(((notificationCount - NOTIFICATIONS_ALOT) / 10.0f), 1.0f); drawable.setTint(S.blendColors(decorationColorOnBackground, getResources().getColor(R.color.battery_low), 1 - opacity)); notificationsButton.setImageDrawable(drawable); } else if (notificationCount >= NOTIFICATIONS_SOME) { notificationsButton.setImageResource(R.drawable.notification_some_on_background); } else if (notificationCount >= NOTIFICATIONS_NONE) { notificationsButton.setImageResource(R.drawable.notification_none_on_background); } else { notificationsButton.setImageResource(R.drawable.error_on_background); } handler.removeCallbacks(shakeIt); handler.postDelayed(shakeIt, 5 * D.SECOND); } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); S.logImportant("HomeScreenActivity was started!"); sharedPreferences = BPrefs.get(this); if (!sharedPreferences.getBoolean(BPrefs.AFTER_TUTORIAL_KEY, false) && !testing) { startActivity(new Intent(this, TutorialActivity.class)); finish(); return; } try { startService(new Intent(this, NotificationListenerService.class)); } catch (Exception e) { Log.e(TAG, S.str(e.getMessage())); e.printStackTrace(); BaldToast.from(this).setType(BaldToast.TYPE_ERROR).setText("Could not start Notification Listener Service!").show(); } new UpdateApps(this).execute(this.getApplicationContext()); lowBatteryAlert = sharedPreferences.getBoolean(BPrefs.LOW_BATTERY_ALERT_KEY, BPrefs.LOW_BATTERY_ALERT_DEFAULT_VALUE); audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); final Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay(); screenSize = new Point(); display.getSize(screenSize); final TypedValue typedValue = new TypedValue(); final Resources.Theme theme = getTheme(); theme.resolveAttribute(R.attr.bald_decoration_on_background, typedValue, true); decorationColorOnBackground = typedValue.data; if ((sharedPreferences.getInt(BPrefs.STATUS_BAR_KEY, BPrefs.STATUS_BAR_DEFAULT_VALUE) == 1)) { getWindow().requestFeature(Window.FEATURE_NO_TITLE); getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } attachToXml(); lantern = new Lantern(this) .observeLifecycle(this); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { flashInited = lantern.initTorch(); } if (sharedPreferences.getBoolean(BPrefs.EMERGENCY_BUTTON_VISIBLE_KEY, BPrefs.EMERGENCY_BUTTON_VISIBLE_DEFAULT_VALUE)) sosButton.setOnClickListener((v) -> { startActivity(new Intent(this, SOSActivity.class)); overridePendingTransition(R.anim.slide_in_down, R.anim.nothing); }); else sosButton.setVisibility(View.GONE); notificationsButton.setOnClickListener((v) -> { startActivity(new Intent(this, NotificationsActivity.class)); overridePendingTransition(R.anim.slide_in_down, R.anim.nothing); }); soundButton.setOnClickListener(v -> S.showDropDownPopup(this, getWindow().getDecorView().getWidth(), new DropDownRecyclerViewAdapter.DropDownListener() { @SuppressLint("InlinedApi") @Override public void onUpdate(DropDownRecyclerViewAdapter.ViewHolder viewHolder, final int position, PopupWindow popupWindow) { viewHolder.pic.setImageResource(SOUND_DRAWABLES[position]); viewHolder.text.setText(SOUND_TEXTS[position]); viewHolder.itemView.setOnClickListener(v1 -> { try { audioManager.setRingerMode(position); soundButton.setImageResource(SOUND_DRAWABLES[position]); } catch (SecurityException e) { startActivity(new Intent(android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS)); } popupWindow.dismiss(); }); } @Override public int size() { return 3; } }, soundButton)); batteryView.setOnClickListener((v) -> BaldToast.from(this) .setText(batteryView.percentage + "%") .setBig(true) .setType(BaldToast.TYPE_INFORMATIVE) .show()); baldPrefsUtils = BaldPrefsUtils.newInstance(this); viewPagerHandler(); baldHomeWatcher = new BaldHomeWatcher(this, this::updateViewPager); recognizerManager.setHomeScreen(this); } @Override protected void onStart() { super.onStart(); onStartCounter++; if (finishedUpdatingApps) updateViewPager(); baldHomeWatcher.startWatch(); if (!testing) { // TODO replace with system env final int percent = (int) (Math.random() * 100) + 1;//1 - 100 if (percent < (100 + (onStartCounter - 20) * 5)) if (percent % 3 == 1 && !isActivityDefault()) { // one in 3 chance onStartCounter = 0; BDB.from(this) .setTitle(R.string.set_home_screen) .setSubText(R.string.set_home_screen_subtext) .addFlag(BDialog.FLAG_OK | BDialog.FLAG_NO) .setPositiveButtonListener(params -> { FakeLauncherActivity.resetPreferredLauncherAndOpenChooser(this); return true; }) .show(); } else if (percent > 99 && Math.random() < 0.2) { onStartCounter = 0; S.shareBaldPhone(this); } else if (percent > 95) { //noinspection ConstantConditions if (BuildConfig.FLAVOR.equals("baldUpdates")) if (sharedPreferences.getLong(BPrefs.LAST_UPDATE_ASKED_VERSION_KEY, 0) + 2 * D.DAY < System.currentTimeMillis()) { UpdatingUtil.checkForUpdates(this, false); } } } } /* the security exception will happen only after api 23 so Lint please shush*/ @SuppressLint("InlinedApi") protected void onResume() { // remember to change in Page1EditorActivity.java too! super.onResume(); if (baldPrefsUtils.hasChanged(this)) { viewPagerHolder.getViewPager().removeAllViews();//android auto saves fragments, not good for us in this case this.recreate(); } soundButton.setImageResource(SOUND_DRAWABLES[audioManager.getRingerMode()]); flashButton.setImageResource(flashState ? R.drawable.flashlight_on_background : R.drawable.flashlight_off_on_background); if (flashInited) { flashButton.setOnClickListener((v) -> { flashState = !flashState; lantern.enableTorchMode(true); if (!flashState) // looks weird (it is) but necessary. otherwise it wont turn off after device rotation... lantern.enableTorchMode(false); flashButton.setImageResource(flashState ? R.drawable.flashlight_on_background : R.drawable.flashlight_off_on_background); }); } else if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { flashButton.setOnClickListener(v -> startActivity(new Intent(this, PermissionActivity.class) .putExtra(PermissionActivity.EXTRA_REQUIRED_PERMISSIONS, PERMISSION_CAMERA) )); } else if (!testing) { // For travis screenshots to show the flashlight flashButton.setOnClickListener(D.EMPTY_CLICK_LISTENER); flashButton.setVisibility(View.GONE); } LocalBroadcastManager.getInstance(this). registerReceiver(notificationReceiver, new IntentFilter(NotificationListenerService.HOME_SCREEN_ACTIVITY_BROADCAST)); handler.postDelayed(() -> LocalBroadcastManager.getInstance(this). sendBroadcast( new Intent(ACTION_REGISTER_ACTIVITY) .putExtra(KEY_EXTRA_ACTIVITY, NOTIFICATIONS_HOME_SCREEN)), 200 * D.MILLISECOND); registerReceiver(batteryReceiver, BATTERY_FILTER); } @Override protected void onPause() { //read https://stackoverflow.com/questions/6165070/receiver-not-registered-exception-error //android platform may unregister the receiver without asking anyone, and this is the best solution. //first occurred in LG k10 api level 23 try { LocalBroadcastManager.getInstance(this).unregisterReceiver(notificationReceiver); } catch (IllegalArgumentException ignore) { } try { LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_REGISTER_ACTIVITY).putExtra(KEY_EXTRA_ACTIVITY, ACTIVITY_NONE)); } catch (IllegalArgumentException ignore) { } try { unregisterReceiver(batteryReceiver); } catch (IllegalArgumentException ignore) { } handler.removeCallbacks(shakeIt); super.onPause(); } @Override protected void onStop() { baldHomeWatcher.stopWatch(); super.onStop(); } @Override protected void onDestroy() { recognizerManager.setHomeScreen(null); super.onDestroy(); } /** * Starts the view pager - being called only in {@link #onCreate(Bundle)} */ private void viewPagerHandler() { baldPagerAdapter = new BaldPagerAdapter(this); viewPagerHolder.setPageTransformer(false, PageTransformers.pageTransformers[sharedPreferences.getInt(BPrefs.PAGE_TRANSFORMERS_KEY, BPrefs.PAGE_TRANSFORMERS_DEFAULT_VALUE)]); viewPagerHolder.setViewPagerAdapter(baldPagerAdapter); viewPagerHolder.setCurrentItem(baldPagerAdapter.startingPage); } /** * Updates {@link HomeScreenActivity#baldPagerAdapter} apps * Sets the page to {@link BaldPagerAdapter#startingPage} */ private void updateViewPager() { baldPagerAdapter.obtainAppList(); viewPagerHolder.setCurrentItem(baldPagerAdapter.startingPage); viewPagerHolder.notifyDataChanegd(); } private void attachToXml() { setContentView(R.layout.home_screen); viewPagerHolder = findViewById(R.id.view_pager_holder); final ViewGroup top_bar = findViewById(R.id.top_bar); int tmpPadding = Math.min(screenSize.x, screenSize.y) / 45; for (int i = 0; i < top_bar.getChildCount(); i++) { top_bar.getChildAt(i).setPadding(tmpPadding, tmpPadding, tmpPadding, tmpPadding); } sosButton = top_bar.findViewById(R.id.sos); soundButton = top_bar.findViewById(R.id.sound); batteryView = top_bar.findViewById(R.id.battery); notificationsButton = top_bar.findViewById(R.id.notifications); flashButton = top_bar.findViewById(R.id.flash); } @Override public void startActivity(Intent intent, @Nullable Bundle options) { try { super.startActivity(intent, options); } catch (Exception e) { Log.e(TAG, S.str(e.getMessage())); e.printStackTrace(); BaldToast.error(this); } } private boolean isActivityDefault() { final List<IntentFilter> filters = new ArrayList<>(); final ComponentName myComponentName = getComponentName(); final String myPackageName = myComponentName.getPackageName(); final List<ComponentName> activities = new ArrayList<>(); final PackageManager packageManager = getPackageManager(); packageManager.getPreferredActivities(filters, activities, myPackageName); return (activities.contains(myComponentName)); } public void displaySpeechRecognizer() { try { startActivityForResult( new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH) .putExtra( RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM ), SPEECH_REQUEST_CODE); } catch (ActivityNotFoundException e) { Log.e(TAG, S.str(e.getMessage())); e.printStackTrace(); BaldToast.error(this); } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == SPEECH_REQUEST_CODE && resultCode == RESULT_OK) { final String spokenText = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS).get(0); recognizerManager.onSpeechRecognizerResult(spokenText); } } @Override public void onBackPressed() { if (vibrator != null) vibrator.vibrate(D.vibetime); updateViewPager(); } static class UpdateApps extends AsyncTask<Context, Void, Void> { final WeakReference<HomeScreenActivity> homeScreenWeakReference; UpdateApps(HomeScreenActivity homeScreen) { super(); homeScreenWeakReference = new WeakReference<>(homeScreen); } @Override protected Void doInBackground(Context... contexts) { try { AppsDatabaseHelper.updateDB(contexts[0]); } catch (Exception e) { BaldToast.from(contexts[0].getApplicationContext()).setType(BaldToast.TYPE_ERROR).setLength(Toast.LENGTH_LONG).setText(e.getMessage()); } return null; } @Override protected void onPostExecute(Void aVoid) { HomeScreenActivity homeScreen = homeScreenWeakReference.get(); if (homeScreen != null) { homeScreen.updateViewPager(); homeScreen.finishedUpdatingApps = true; if (homeScreen.launchAppsActivity) homeScreen.startActivity(new Intent(homeScreen, AppsActivity.class)); } } } @Override protected int requiredPermissions() { return PERMISSION_NONE; } }